[
  {
    "path": ".gitattributes",
    "content": "# Use Unix line endings in all text files.\n* text=auto eol=lf"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n- Demonstrating empathy and kindness toward other people\n- Being respectful of differing opinions, viewpoints, and experiences\n- Giving and gracefully accepting constructive feedback\n- Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n- Focusing on what is best not just for us as individuals, but for the overall\n  community\n\nExamples of unacceptable behavior include:\n\n- The use of sexualized language or imagery, and sexual attention or advances of\n  any kind\n- Trolling, insulting or derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or email address,\n  without their explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at hello@lcas.dev.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of\nactions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by\n[Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing Guidelines\n\n## Submitting a pull request\n\nFirst, please be sure to ensure `deno task ok` is run and successfully passes.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      # Check for updates to GitHub Actions every week\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: ci\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  test:\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 10\n\n    strategy:\n      fail-fast: false\n      matrix:\n        deno: [\"v2.x\", \"canary\"]\n        os: [macOS-latest, windows-latest, ubuntu-latest]\n        include:\n          - os: ubuntu-latest\n            cache_path: ~/.cache/deno/\n          - os: macos-latest\n            cache_path: ~/Library/Caches/deno/\n          - os: windows-latest\n            cache_path: ~\\AppData\\Local\\deno\\\n\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n\n      - name: Setup Deno\n        uses: denoland/setup-deno@v2\n        with:\n          cache: true\n          deno-version: ${{ matrix.deno }}\n\n      - name: Install dependencies\n        run: deno install\n\n      - name: Verify formatting\n        if: startsWith(matrix.os, 'ubuntu') && matrix.deno == 'v2.x'\n        run: deno fmt --check\n\n      - name: Run linter\n        if: startsWith(matrix.os, 'ubuntu') && matrix.deno == 'v2.x'\n        run: deno lint\n\n      - name: Spell-check\n        if: startsWith(matrix.os, 'ubuntu') && matrix.deno == 'v2.x'\n        uses: crate-ci/typos@master\n\n      - name: Type check project\n        run: deno task check:types\n\n      - name: Run tests\n        run: deno task test\n\n      - name: Check docs\n        run: deno task check:docs\n\n      - name: Build fresh.deno.dev\n        if: startsWith(matrix.os, 'ubuntu') && matrix.deno == 'v2.x'\n        run: deno task build-www\n"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "content": "name: Deploy\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  deploy:\n    name: Deploy\n    runs-on: ubuntu-latest\n\n    permissions:\n      id-token: write # Needed for auth with Deno Deploy\n      contents: read # Needed to clone the repository\n\n    steps:\n      - name: Clone repository\n        uses: actions/checkout@v4\n\n      - name: Install Deno\n        uses: denoland/setup-deno@v2\n        with:\n          cache: true\n\n      - name: Run install step\n        run: \"deno install\"\n\n      - name: Build step\n        working-directory: ./www\n        run: \"deno task build\"\n\n      - name: Upload to Deno Deploy\n        uses: denoland/deployctl@v1\n        # Skip publishing for forks\n        if: github.repository_owner == 'denoland'\n        with:\n          project: \"fresh\"\n          entrypoint: \"server.js\"\n          root: \"./www/_fresh/\"\n"
  },
  {
    "path": ".github/workflows/post_publish.yml",
    "content": "name: post_publish\n\non:\n  release:\n    types: [published]\n\njobs:\n  update-dl-version:\n    name: update dl.deno.land version\n    runs-on: ubuntu-22.04\n    if: github.repository == 'denoland/fresh'\n    steps:\n      - name: Checkout repo\n        uses: actions/checkout@v4\n\n      - name: Authenticate with Google Cloud\n        uses: google-github-actions/auth@v2\n        with:\n          project_id: denoland\n          credentials_json: ${{ secrets.GCP_SA_KEY }}\n          export_environment_variables: true\n          create_credentials_file: true\n\n      - name: Setup gcloud\n        uses: google-github-actions/setup-gcloud@v2\n        with:\n          project_id: denoland\n\n      - name: Upload version file to dl.deno.land\n        run: |\n          cat packages/fresh/deno.json | jq -r \".version\" > release-latest.txt\n          gsutil -h \"Cache-Control: no-cache\" cp release-latest.txt gs://dl.deno.land/fresh/release-latest.txt\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish JSR\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      id-token: write\n\n    # Skip publishing for forks\n    if: github.repository_owner == 'denoland'\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Install Deno\n        uses: denoland/setup-deno@v2\n        with:\n          cache: true\n\n      - name: Install dependencies\n        run: deno install\n\n      - name: Publish\n        run: deno publish\n"
  },
  {
    "path": ".gitignore",
    "content": "_fresh/\n.vite/\nvendor/\nnode_modules/\n.docs/\n.DS_Store\ntmp_*\ncoverage/\nvite-inspect/\n.vite-inspect/\ndemo/dist/"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"denoland.vscode-deno\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"deno.enable\": true,\n  \"deno.lint\": true,\n  \"deno.codeLens.test\": true,\n  \"deno.documentPreloadLimit\": 2000,\n  \"editor.formatOnSave\": true,\n  \"editor.defaultFormatter\": \"denoland.vscode-deno\",\n  \"[typescriptreact]\": {\n    \"editor.defaultFormatter\": \"denoland.vscode-deno\"\n  },\n  \"[typescript]\": {\n    \"editor.defaultFormatter\": \"denoland.vscode-deno\"\n  },\n  \"[javascriptreact]\": {\n    \"editor.defaultFormatter\": \"denoland.vscode-deno\"\n  },\n  \"[javascript]\": {\n    \"editor.defaultFormatter\": \"denoland.vscode-deno\"\n  },\n  \"[markdown]\": {\n    \"editor.defaultFormatter\": \"denoland.vscode-deno\"\n  },\n  \"css.customData\": [\n    \".vscode/tailwind.json\"\n  ],\n  \"[json]\": {\n    \"editor.defaultFormatter\": \"denoland.vscode-deno\"\n  }\n}\n"
  },
  {
    "path": ".vscode/tailwind.json",
    "content": "{\n  \"version\": 1.1,\n  \"atDirectives\": [\n    {\n      \"name\": \"@tailwind\",\n      \"description\": \"Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.\",\n      \"references\": [\n        {\n          \"name\": \"Tailwind Documentation\",\n          \"url\": \"https://tailwindcss.com/docs/functions-and-directives#tailwind\"\n        }\n      ]\n    },\n    {\n      \"name\": \"@apply\",\n      \"description\": \"Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.\",\n      \"references\": [\n        {\n          \"name\": \"Tailwind Documentation\",\n          \"url\": \"https://tailwindcss.com/docs/functions-and-directives#apply\"\n        }\n      ]\n    },\n    {\n      \"name\": \"@responsive\",\n      \"description\": \"You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\\n```css\\n@responsive {\\n  .alert {\\n    background-color: #E53E3E;\\n  }\\n}\\n```\\n\",\n      \"references\": [\n        {\n          \"name\": \"Tailwind Documentation\",\n          \"url\": \"https://tailwindcss.com/docs/functions-and-directives#responsive\"\n        }\n      ]\n    },\n    {\n      \"name\": \"@screen\",\n      \"description\": \"The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\\n```css\\n@screen sm {\\n  /* ... */\\n}\\n```\\n…gets transformed into this:\\n```css\\n@media (min-width: 640px) {\\n  /* ... */\\n}\\n```\\n\",\n      \"references\": [\n        {\n          \"name\": \"Tailwind Documentation\",\n          \"url\": \"https://tailwindcss.com/docs/functions-and-directives#screen\"\n        }\n      ]\n    },\n    {\n      \"name\": \"@variants\",\n      \"description\": \"Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\\n```css\\n@variants hover, focus {\\n   .btn-brand {\\n    background-color: #3182CE;\\n  }\\n}\\n```\\n\",\n      \"references\": [\n        {\n          \"name\": \"Tailwind Documentation\",\n          \"url\": \"https://tailwindcss.com/docs/functions-and-directives#variants\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-2023 Luca Casonato\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[Documentation](#-documentation) | [Getting started](#-getting-started) |\n[API Reference](https://deno.land/x/fresh?doc)\n\n# fresh\n\n<img align=\"right\" src=\"https://fresh.deno.dev/logo.svg\" height=\"150px\" alt=\"The Fresh logo: a sliced lemon dripping with juice\">\n\n**Fresh** is a next generation web framework, built for speed, reliability, and\nsimplicity.\n\nSome stand-out features:\n\n- Island based client hydration for maximum interactivity.\n- Zero runtime overhead: no JS is shipped to the client by default.\n- No configuration necessary.\n- TypeScript support out of the box.\n- File-system routing à la Next.js.\n\n## 📖 Documentation\n\nThe [documentation](https://fresh.deno.dev/docs/introduction) is available on\n[fresh.deno.dev](https://fresh.deno.dev/).\n\n## 🚀 Getting started\n\nInstall the latest [Deno CLI](https://deno.com/) version.\n\nYou can scaffold a new project by running the Fresh init script. To scaffold a\nproject run the following:\n\n```sh\ndeno run -Ar jsr:@fresh/init\n```\n\nThen navigate to the newly created project folder:\n\n```\ncd fresh-project\n```\n\nFrom within your project folder, start the development server using the\n`deno task` command:\n\n```\ndeno task dev\n```\n\nNow open http://localhost:5173 in your browser to view the page. You make\nchanges to the project source code and see them reflected in your browser.\n\nTo deploy the project to the live internet, you can use\n[Deno Deploy](https://deno.com/deploy):\n\n1. Push your project to GitHub.\n2. [Create a Deno Deploy project.](https://console.deno.com/new)\n3. Select your GitHub repository.\n4. The project will be deployed to a public $project.$username.deno.net\n   subdomain with no configuration necessary.\n\nFor a more in-depth getting started guide, visit the\n[Getting Started](https://fresh.deno.dev/docs/getting-started) page in the Fresh\ndocs.\n\n## Contributing\n\nWe appreciate your help! To contribute, please read our\n[contributing guideline](./.github/CONTRIBUTING.md).\n\n## Adding your project to the showcase\n\nIf you feel that your project would be helpful to other Fresh users, please\nconsider putting your project on the\n[showcase](https://fresh.deno.dev/showcase). However, websites that are just for\npromotional purposes may not be listed.\n\nTo take a screenshot, run the following command.\n\n```sh\ndeno task screenshot [url] [your-app-name]\n```\n\nThen add your site to\n[showcase.json](https://github.com/denoland/fresh/blob/main/www/data/showcase.json),\npreferably with source code on GitHub, but not required.\n\n## Badges\n\n![Made with Fresh](./www/static/fresh-badge.svg)\n\n```md\n[![Made with Fresh](https://fresh.deno.dev/fresh-badge.svg)](https://fresh.deno.dev)\n```\n\n```html\n<a href=\"https://fresh.deno.dev\">\n  <img\n    width=\"197\"\n    height=\"37\"\n    src=\"https://fresh.deno.dev/fresh-badge.svg\"\n    alt=\"Made with Fresh\"\n  />\n</a>\n```\n\n![Made with Fresh(dark)](./www/static/fresh-badge-dark.svg)\n\n```md\n[![Made with Fresh](https://fresh.deno.dev/fresh-badge-dark.svg)](https://fresh.deno.dev)\n```\n\n```html\n<a href=\"https://fresh.deno.dev\">\n  <img\n    width=\"197\"\n    height=\"37\"\n    src=\"https://fresh.deno.dev/fresh-badge-dark.svg\"\n    alt=\"Made with Fresh\"\n  />\n</a>\n```\n\n## Hashtags\n\nUse the following hashtags in your social media posts that reference Fresh and\nas Topics in the About section of your GitHub repos that contain Fresh code. It\nwill assure maximum visibility for your posts and code, and promote Fresh\ndevelopment ecosystem visibility.\n\n- #denofresh\n- #deno\n\nGithub repo Topics will not include the hash symbol.\n"
  },
  {
    "path": "_typos.toml",
    "content": "[files]\nextend-exclude = [\n  \"packages/fresh/tests/fixture_partials/routes/scroll_restoration/index.tsx\",\n  \"www/static/fonts/FixelVariable.woff2\",\n  \"www/static/fonts/FixelVariableItalic.woff2\",\n  \"packages/fresh/tests/lorem_ipsum.txt\",\n]\n\n[default]\nextend-ignore-identifiers-re = [\"Fixel\"]\n"
  },
  {
    "path": "deno.json",
    "content": "{\n  \"vendor\": true,\n  \"nodeModulesDir\": \"manual\",\n  \"workspace\": [\n    \"./packages/*\",\n    \"./www\"\n  ],\n  \"tasks\": {\n    \"demo\": \"deno task --cwd=packages/plugin-vite demo\",\n    \"demo:build\": \"deno task --cwd=packages/plugin-vite demo:build\",\n    \"demo:start\": \"deno task --cwd=packages/plugin-vite demo:start\",\n    \"test\": \"deno test -A --parallel\",\n    \"www\": \"deno task --cwd=www dev\",\n    \"build-www\": \"deno task --cwd=www build\",\n    \"screenshot\": \"deno run -A www/utils/screenshot.ts\",\n    \"check:types\": \"deno check --allow-import\",\n    \"check:docs\": \"deno run -A tools/check_docs.ts\",\n    \"ok\": \"deno fmt --check && deno lint && deno task check:types && deno task test\",\n    \"test:www\": \"deno test -A www/main_test.*\",\n    \"release\": \"deno run -A tools/release.ts\"\n  },\n  \"exclude\": [\n    \"**/_fresh/*\",\n    \"**/tmp/*\",\n    \"*/tests_OLD/**\",\n    \"**/vite.config.ts.*\",\n    \"*/vite.config.ts.*\",\n    \"vite.config.ts.*\"\n  ],\n  \"publish\": {\n    \"include\": [\n      \"src/**\",\n      \"deno.json\",\n      \"README.md\",\n      \"LICENSE\",\n      \"www/static/fresh-badge.svg\",\n      \"www/static/fresh-badge-dark.svg\",\n      \"*.todo\"\n    ],\n    \"exclude\": [\"**/*_test.*\", \"src/__OLD/**\", \"*.todo\", \"**/tests/**\"]\n  },\n  \"imports\": {\n    \"@deno/doc\": \"jsr:@deno/doc@^0.172.0\",\n    \"@deno/esbuild-plugin\": \"jsr:@deno/esbuild-plugin@^1.2.0\",\n    \"@fresh/build-id\": \"jsr:@fresh/build-id@^1.0.0\",\n    \"@std/cli\": \"jsr:@std/cli@^1.0.19\",\n    \"@std/collections\": \"jsr:@std/collections@^1.1.2\",\n    \"@std/dotenv\": \"jsr:@std/dotenv@^0.225.5\",\n    \"@std/http\": \"jsr:@std/http@^1.0.15\",\n    \"@std/uuid\": \"jsr:@std/uuid@^1.0.7\",\n    \"@supabase/postgrest-js\": \"npm:@supabase/postgrest-js@^1.21.4\",\n    \"@types/mime-db\": \"npm:@types/mime-db@^1.43.6\",\n    \"@types/node\": \"npm:@types/node@^24.3.0\",\n    \"@types/pg\": \"npm:@types/pg@^8.15.5\",\n    \"@types/prismjs\": \"npm:@types/prismjs@^1.26.5\",\n    \"docsearch\": \"https://esm.sh/@docsearch/js@3.5.2?target=es2020\",\n    \"esbuild\": \"npm:esbuild@0.25.7\",\n    \"esbuild-wasm\": \"npm:esbuild-wasm@0.25.7\",\n    \"fresh\": \"jsr:@fresh/core@^2.0.0\",\n    \"mime-db\": \"npm:mime-db@^1.54.0\",\n    \"preact\": \"npm:preact@^10.28.2\",\n    \"preact-render-to-string\": \"npm:preact-render-to-string@^6.6.5\",\n    \"$ga4\": \"https://raw.githubusercontent.com/denoland/ga4/main/mod.ts\",\n    \"@opentelemetry/api\": \"npm:@opentelemetry/api@^1.9.0\",\n    \"@preact/signals\": \"npm:@preact/signals@^2.5.1\",\n    \"@std/encoding\": \"jsr:@std/encoding@1\",\n    \"@std/fmt\": \"jsr:@std/fmt@^1.0.7\",\n    \"@std/fs\": \"jsr:@std/fs@1\",\n    \"@std/html\": \"jsr:@std/html@1\",\n    \"@std/jsonc\": \"jsr:@std/jsonc@1\",\n    \"@std/media-types\": \"jsr:@std/media-types@1\",\n    \"@std/path\": \"jsr:@std/path@1\",\n    \"@std/semver\": \"jsr:@std/semver@1\",\n    \"@std/streams\": \"jsr:@std/streams@1\",\n\n    \"@astral/astral\": \"jsr:@astral/astral@^0.5.5\",\n    \"@marvinh-test/fresh-island\": \"jsr:@marvinh-test/fresh-island@^0.0.3\",\n    \"linkedom\": \"npm:linkedom@^0.18.10\",\n    \"@std/async\": \"jsr:@std/async@^1.0.13\",\n    \"@std/expect\": \"jsr:@std/expect@^1.0.16\",\n    \"@std/testing\": \"jsr:@std/testing@^1.0.12\",\n\n    \"@tailwindcss/postcss\": \"npm:@tailwindcss/postcss@^4.1.10\",\n    \"redis\": \"npm:redis@^5.8.2\",\n    \"rollup\": \"npm:rollup@^4.55.1\",\n    \"tailwindcss\": \"npm:tailwindcss@^4.1.10\",\n    \"postcss\": \"npm:postcss@8.5.6\",\n\n    \"ts-morph\": \"npm:ts-morph@^26.0.0\",\n\n    \"@std/front-matter\": \"jsr:@std/front-matter@^1.0.5\",\n    \"github-slugger\": \"npm:github-slugger@^2.0.0\",\n    \"imagescript\": \"https://deno.land/x/imagescript@1.3.0/mod.ts\",\n    \"marked\": \"npm:marked@^15.0.11\",\n    \"marked-mangle\": \"npm:marked-mangle@^1.1.9\",\n    \"prismjs\": \"npm:prismjs@^1.29.0\",\n    \"vite\": \"npm:vite@^7.3.1\"\n  },\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.asynciterable\", \"deno.ns\", \"deno.unstable\"],\n    \"jsx\": \"precompile\",\n    \"jsxImportSource\": \"preact\",\n    \"jsxPrecompileSkipElements\": [\n      \"a\",\n      \"img\",\n      \"source\",\n      \"body\",\n      \"html\",\n      \"head\",\n      \"title\",\n      \"meta\",\n      \"script\",\n      \"link\",\n      \"style\",\n      \"base\",\n      \"noscript\",\n      \"template\"\n    ],\n    \"types\": [\"vite/client\"]\n  },\n  \"lint\": {\n    \"rules\": {\n      \"tags\": [\"recommended\", \"fresh\", \"jsr\", \"jsx\", \"react\"],\n      \"exclude\": [\"no-window\"],\n      \"include\": [\"no-console\"]\n    }\n  },\n  \"fmt\": {\n    \"exclude\": [\"./www/static/**/*.svg\"]\n  }\n}\n"
  },
  {
    "path": "docs/1.x/concepts/ahead-of-time-builds.md",
    "content": "---\ndescription: |\n  Fresh optimize assets ahead of time, which makes pages load way quicker.\n---\n\nFresh enables you to pre-optimize frontend assets before the code is deployed.\nDuring that process the code for Islands will be compressed and optimized, so\nthat Fresh can send as little code as possible to the browser. Depending on the\namount of code an island needs, this process can take several seconds if done on\nthe fly server-side.\n\nDoing those optimizations ahead-of-time and deploying the already optimized\nassets alongside with your code, allows Fresh to treat them as like any other\nstatic file and can serve it immediately without any further processing. On\npages with islands, having to do no processing greatly speeds up page load\ntimes.\n\nPlugins can build static assets during ahead-of-time builds. This can be used to\npre-process or generate CSS files, for example.\n\n## Creating an optimized build\n\nTo have Fresh optimize all the assets, run one of the following commands:\n\n```sh Terminal\n# As a task in newer Fresh projects\ndeno task build\n# or invoke it manually\ndeno run -A dev.ts build\n```\n\nThis will create a `_fresh` folder in the project directory. That folder\ncontains the optimized assets and a `snapshot.json` file which includes some\nmetadata for Fresh.\n\nAny other static files generated by plugins will be stored in the\n`_fresh/static` subfolder. They will be served the same as other\n[static files](/docs/1.x/concepts/static-files.md).\n\n> [info]: The `_fresh` folder should not be committed to the repository. Add an\n> entry in the `.gitignore` file to ensure that it is not committed. Create that\n> file at the root of your git repository if not present.\n>\n> ```gitignore .gitignore\n> # Ignore fresh build directory\n> _fresh/\n> ```\n\n## Running Fresh with optimized assets\n\nWhen Fresh is started in non-development mode (usually via `main.ts`), Fresh\nwill automatically pick up optimized assets when a `_fresh` folder exists. If\nfound, Fresh will print the following message to the terminal:\n\n```sh Terminal output\nUsing snapshot found at /path/to/project/_fresh\n```\n\n## Deploying an optimized Fresh project\n\nIf you are deploying a Fresh project to Deno Deploy, you can use ahead-of-time\nbuilds to optimize the assets before deploying them. This will make your\napplication load quicker.\n\nOpen the Deno Deploy dashboard for your project and head to the \"Git\nIntegration\" section in the project settings. Enter `deno task build` in the\n\"Build command\" field and save. This will switch your Deno Deploy project to use\nahead-of-time builds.\n\n## Migrating existing projects with Plugins\n\nIf you're using Fresh plugins, extract them into a `fresh.config.ts` file, so\nthat both the `dev.ts` and `main.ts` script have access to them.\n\n```ts fresh.config.ts\nimport { defineConfig } from \"$fresh/server.ts\";\nimport twindPlugin from \"$fresh/plugins/twind.ts\";\nimport twindConfig from \"./twind.config.ts\";\n\nexport default defineConfig({\n  plugins: [twindPlugin(twindConfig)],\n});\n```\n\n```ts main.ts\nimport { start } from \"$fresh/server.ts\";\nimport manifest from \"./fresh.gen.ts\";\nimport config from \"./fresh.config.ts\";\n\nawait start(manifest, config);\n```\n\n```ts dev.ts\nimport dev from \"$fresh/dev.ts\";\nimport config from \"./fresh.config.ts\";\n\nawait dev(import.meta.url, \"./main.ts\", config);\n```\n"
  },
  {
    "path": "docs/1.x/concepts/app-wrapper.md",
    "content": "---\ndescription: |\n  Add a global app wrapper to provide common meta tags or context for application routes.\n---\n\nAn app wrapper is defined in an `_app.tsx` file in `routes/` folder and is\ntypically used to create the outer structure of an HTML document. It must\ncontain a default export that is a regular Preact component. Only one such\nwrapper is allowed per application.\n\nThe component to be wrapped is received via props, in addition to a few other\nthings. This allows for the introduction of a global container functioning as a\ntemplate which can be conditioned based on state and params. Note that any state\nset by middleware is available via `props.state`.\n\n```tsx routes/_app.tsx\nimport { PageProps } from \"$fresh/server.ts\";\n\nexport default function App({ Component, state }: PageProps) {\n  // do something with state here\n  return (\n    <html>\n      <head>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>My Fresh app</title>\n      </head>\n      <body>\n        <Component />\n      </body>\n    </html>\n  );\n}\n```\n\n## Async app wrapper\n\nSimilar to routes and layouts, the app wrapper can be made asynchronous. This\nchanges the function signature so that the first argument is the `Request`\ninstance and the second one is the `FreshContext`.\n\n```tsx routes/_app.tsx\nimport { FreshContext } from \"$fresh/server.ts\";\n\nexport default async function App(req: Request, ctx: FreshContext) {\n  const data = await loadData();\n\n  return (\n    <html>\n      <head>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>My Fresh app</title>\n      </head>\n      <body>\n        <h1>Hello {data.name}</h1>\n        <ctx.Component />\n      </body>\n    </html>\n  );\n}\n```\n\n### Define helper\n\nTo make it quicker to type the async app wrapper, Fresh includes a `defineApp`\nhelper which already infers the correct types for you.\n\n```tsx routes/_app.tsx\nimport { defineApp } from \"$fresh/server.ts\";\n\nexport default defineApp(async (req, ctx) => {\n  const data = await loadData();\n\n  return (\n    <html>\n      <head>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>My Fresh app</title>\n      </head>\n      <body>\n        <h1>Hello {data.name}</h1>\n        <ctx.Component />\n      </body>\n    </html>\n  );\n});\n```\n\n## Disabling the app wrapper\n\nRendering the app wrapper can be skipped on a route or layout basis. To do that,\nset `skipAppWrapper: true` to the layout or route config.\n\n```tsx routes/my-special-route.tsx\nimport { RouteConfig } from \"$fresh/server.ts\";\n\nexport const config: RouteConfig = {\n  skipAppWrapper: true, // Skip the app wrapper during rendering\n};\n\nexport default function Page() {\n  // ...\n}\n```\n"
  },
  {
    "path": "docs/1.x/concepts/architecture.md",
    "content": "---\ndescription: |\n  Fresh's architecture is designed to make it easy to build fast, scalable, and reliable applications.\n---\n\nFresh is designed to make it easy to build fast, scalable, and reliable\napplications. To do this, it makes opinionated decisions about how one should\nbuild web applications. These decisions are backed by strong empirical data\ngathered from experts in the field. Some examples of these principles are:\n\n- Page load times should be reduced to a minimum.\n- The work performed on the client should be minimized.\n- Errors should have a small blast radius - stuff should gracefully degrade.\n\nThe single biggest architecture decision that Fresh makes is its usage of the\n[islands architecture][islands] pattern. This means that Fresh applications ship\npure HTML to the client by default. Parts of a server-rendered page can then be\nindependently re-hydrated with interactive widgets (islands). This means that\nthe client is only responsible for rendering parts of the page that are\ninteractive enough to warrant the extra effort. Any content that is purely\nstatic does not have related client-side JavaScript and is thus very\nlightweight.\n\n<!-- TODO(lucacasonato): elaborate on request handling, form actions, etc. -->\n\n[islands]: https://www.patterns.dev/posts/islands-architecture/\n"
  },
  {
    "path": "docs/1.x/concepts/data-fetching.md",
    "content": "---\ndescription: |\n  Data fetching in Fresh happens inside of route handler functions. These can pass route data to the page via page props.\n---\n\nServer side data fetching in Fresh is accomplished through asynchronous handler\nfunctions. These handler functions can call a `ctx.render()` function with the\ndata to be rendered as an argument. This data can then be retrieved by the page\ncomponent through the `data` property on the `props`.\n\nHere is an example:\n\n```tsx routes/projects/[id].tsx\ninterface Project {\n  name: string;\n  stars: number;\n}\n\nexport const handler: Handlers<Project> = {\n  async GET(_req, ctx) {\n    const project = await db.projects.findOne({ id: ctx.params.id });\n    if (!project) {\n      return ctx.renderNotFound({\n        message: \"Project does not exist\",\n      });\n    }\n    return ctx.render(project);\n  },\n};\n\nexport default function ProjectPage(props: PageProps<Project>) {\n  return (\n    <div>\n      <h1>{props.data.name}</h1>\n      <p>{props.data.stars} stars</p>\n    </div>\n  );\n}\n```\n\nThe type parameter on the `PageProps`, `Handlers`, `Handler`, and `FreshContext`\ncan be used to enforce a TypeScript type to use for the render data. Fresh\nenforces during type checking that the types in all of these fields are\ncompatible within a single page.\n\n## Asynchronous routes\n\nAs a shortcut for combining a `GET` handler with a route, you can define your\nroute as `async`. An `async` route (a route that returns a promise) will be\ncalled with the `Request` and a `RouteContext` (similar to a `HandlerContext`).\nHere is the above example rewritten using this shortcut:\n\n```tsx routes/projects/[id].tsx\ninterface Project {\n  name: string;\n  stars: number;\n}\n\nexport default async function ProjectPage(_req, ctx: FreshContext) {\n  const project: Project | null = await db.projects.findOne({\n    id: ctx.params.id,\n  });\n\n  if (!project) {\n    return <h1>Project not found</h1>;\n  }\n\n  return (\n    <div>\n      <h1>{project.name}</h1>\n      <p>{project.stars} stars</p>\n    </div>\n  );\n}\n```\n"
  },
  {
    "path": "docs/1.x/concepts/deployment.md",
    "content": "---\ndescription: |\n  Fresh can be deployed to a variety of platforms easily.\n---\n\nWhile Fresh is designed to be deployed to [Deno Deploy][deno-deploy], it can be\ndeployed to any system or platform that can run a Deno based web server.\n\nHere are instructions for specific providers / systems:\n\n- [Deno Deploy](#deno-deploy)\n- [Docker](#docker)\n- [Self Contained Executable](#self-contained-executable)\n\n## Deno Deploy\n\nThe recommended way to deploy Fresh is by using Deno Deploy. Deno Deploy\nprovides a GitHub integration that can deploy your Fresh projects to its\nglobally distributed edge network in seconds, automatically.\n\nView [the getting started guide][deploy-to-production] for instructions on how\nto deploy Fresh to Deno Deploy.\n\n## Docker\n\nYou can deploy Fresh to any platform that can run Docker containers. Docker is a\ntool to containerize projects and portably run them on any supported platform.\n\nWhen packaging your Fresh app for Docker, it is important that you set the\n`DENO_DEPLOYMENT_ID` environment variable in your container. This variable needs\nto be set to an opaque string ID that represents the version of your application\nthat is currently being run. This could be a Git commit hash, or a hash of all\nfiles in your project. It is critical for the function of Fresh that this ID\nchanges when _any_ file in your project changes - if it doesn't, incorrect\ncaching **will** cause your project to not function correctly.\n\nHere is an example `Dockerfile` for a Fresh project:\n\n```dockerfile Dockerfile\nFROM denoland/deno:1.38.3\n\nARG GIT_REVISION\nENV DENO_DEPLOYMENT_ID=${GIT_REVISION}\n\nWORKDIR /app\n\nCOPY . .\nRUN deno cache main.ts\n\nEXPOSE 8000\n\nCMD [\"run\", \"-A\", \"main.ts\"]\n```\n\nTo build your Docker image inside of a Git repository:\n\n```sh Terminal\n$ docker build --build-arg GIT_REVISION=$(git rev-parse HEAD) -t my-fresh-app .\n```\n\nThen run your Docker container:\n\n```sh Terminal\n$ docker run -t -i -p 80:8000 my-fresh-app\n```\n\nTo deploy to a cloud provider, push it to a container registry and follow their\ndocumentation.\n\n- [Amazon Web Services][aws-container-registry]\n- [Google Cloud][gcp-container-registry]\n\n## Self Contained Executable\n\nWith Deno 2.1, you can create a self-contained executable of your Fresh project\nthat includes all assets and dependencies. This executable can run on any\nplatform without requiring Deno to be installed.\n\n```sh Terminal\n$ deno task build\n$ deno compile --include static --include _fresh --include deno.json -A main.ts\n```\n\n[aws-container-registry]: https://docs.aws.amazon.com/AmazonECS/latest/userguide/create-container-image.html#create-container-image-push-ecr\n[gcp-container-registry]: https://cloud.google.com/container-registry/docs/pushing-and-pulling\n[deno-deploy]: https://deno.com/deploy\n[deploy-to-production]: /docs/1.x/getting-started/deploy-to-production\n"
  },
  {
    "path": "docs/1.x/concepts/error-pages.md",
    "content": "---\ndescription: |\n  Error pages can be used to customize the page that is shown when an error occurs in the application.\n---\n\nFresh supports customizing the `404 Not Found`, and the\n`500 Internal Server Error` pages. These are shown when a request is made but no\nmatching route exists, and when a middleware, route handler, or page component\nthrows an error respectively.\n\n### 404: Not Found\n\nThe 404 page can be customized by creating a `_404.tsx` file in the `routes/`\nfolder. The file must have a default export that is a regular Preact component.\nA props object of type `PageProps` is passed in as an argument.\n\n```tsx routes/_404.tsx\nimport { PageProps } from \"$fresh/server.ts\";\n\nexport default function NotFoundPage({ url }: PageProps) {\n  return <p>404 not found: {url.pathname}</p>;\n}\n```\n\n#### Manually render 404 pages\n\nThe `_404.tsx` file will be invoked automatically when no route matches the URL.\nIn some cases, one needs to manually trigger the rendering of the 404 page, for\nexample when the route did match, but the requested resource does not exist.\nThis can be achieved with `ctx.renderNotFound`.\n\n```tsx routes/blog/[slug].tsx\nimport { Handlers, PageProps } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(req, ctx) {\n    const blogpost = await fetchBlogpost(ctx.params.slug);\n    if (!blogpost) {\n      return ctx.renderNotFound({\n        custom: \"prop\",\n      });\n    }\n    return ctx.render({ blogpost });\n  },\n};\n\nexport default function BlogpostPage({ data }) {\n  return (\n    <article>\n      <h1>{data.blogpost.title}</h1>\n      {/* rest of your page */}\n    </article>\n  );\n}\n```\n\nThis can also be achieved by throwing an error, if you're uninterested in\npassing specific data to your 404 page:\n\n```tsx routes/missing-page.tsx\nimport { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  GET(_req, _ctx) {\n    throw new Deno.errors.NotFound();\n  },\n};\n```\n\n### 500: Internal Server Error\n\nThe 500 page can be customized by creating a `_500.tsx` file in the `routes/`\nfolder. The file must have a default export that is a regular Preact component.\nA props object of type `PageProps` is passed in as an argument.\n\n```tsx routes/_500.tsx\nimport { PageProps } from \"$fresh/server.ts\";\n\nexport default function Error500Page({ error }: PageProps) {\n  return <p>500 internal error: {(error as Error).message}</p>;\n}\n```\n"
  },
  {
    "path": "docs/1.x/concepts/forms.md",
    "content": "---\ndescription: |\n  Robustly handle user inputs using HTML `<form>` elements client side, and form\n  submission handlers server side.\n---\n\nFor stronger resiliency and user experience, Fresh relies on native browser\nsupport for form submissions with the HTML `<form>` element.\n\nIn the browser, a `<form>` submit will send an HTML action (usually `GET` or\n`POST`) to the server, which responds with a new page to render.\n\n## POST request with `application/x-www-form-urlencoded`\n\nForms typically submit as a `GET` request with data encoded in the URL's search\nparameters, or as a `POST` request with either an\n`application/x-www-form-urlencoded` or `multipart/form-data` body.\n\nThis example demonstrates how to handle `application/x-www-form-urlencoded`\n`<form>` submissions:\n\n```tsx routes/subscribe.tsx\nimport { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(req, ctx) {\n    return await ctx.render();\n  },\n  async POST(req, ctx) {\n    const form = await req.formData();\n    const email = form.get(\"email\")?.toString();\n\n    // Add email to list.\n\n    // Redirect user to thank you page.\n    const headers = new Headers();\n    headers.set(\"location\", \"/thanks-for-subscribing\");\n    return new Response(null, {\n      status: 303, // See Other\n      headers,\n    });\n  },\n};\n\nexport default function Subscribe() {\n  return (\n    <>\n      <form method=\"post\">\n        <input type=\"email\" name=\"email\" value=\"\" />\n        <button type=\"submit\">Subscribe</button>\n      </form>\n    </>\n  );\n}\n```\n\nWhen the user submits the form, Deno will retrieve the `email` value using the\nrequest's `formData()` method, add the email to a list, and redirect the user to\na thank you page.\n\n## Handling file uploads\n\nFile uploads can be handled in a very similar manner to the example above. Note\nthat this time, we have to explicitly declare the form's encoding to be\n`multipart/form-data`.\n\n```tsx routes/subscribe.tsx\nimport { Handlers, type PageProps } from \"$fresh/server.ts\";\n\ninterface Props {\n  message: string | null;\n}\n\nexport const handler: Handlers<Props> = {\n  async GET(req, ctx) {\n    return await ctx.render({\n      message: null,\n    });\n  },\n  async POST(req, ctx) {\n    const form = await req.formData();\n    const file = form.get(\"my-file\") as File;\n\n    if (!file) {\n      return ctx.render({\n        message: `Please try again`,\n      });\n    }\n\n    const name = file.name;\n    const contents = await file.text();\n\n    console.log(contents);\n\n    return ctx.render({\n      message: `${name} uploaded!`,\n    });\n  },\n};\n\nexport default function Upload(props: PageProps<Props>) {\n  const { message } = props.data;\n  return (\n    <>\n      <form method=\"post\" encType=\"multipart/form-data\">\n        <input type=\"file\" name=\"my-file\" />\n        <button type=\"submit\">Upload</button>\n      </form>\n      {message ? <p>{message}</p> : null}\n    </>\n  );\n}\n```\n\n## A note of caution\n\nThese examples are simplified to demonstrate how Deno and Fresh handle HTTP\nrequests. In the Real World™, you'll want to validate your data (_especially the\nfile type_) and protect against cross-site request forgery. Consider yourself\nwarned.\n"
  },
  {
    "path": "docs/1.x/concepts/index.md",
    "content": "---\ndescription: |\n  This chapter goes over some fundamental concepts of Fresh.\n---\n\nThis chapter goes over some fundamental concepts of Fresh. It covers the\noverarching architecture design of Fresh applications, as well as reference\ndocumentation about the various features of Fresh.\n"
  },
  {
    "path": "docs/1.x/concepts/islands.md",
    "content": "---\ndescription: |\n  Islands enable client side interactivity in Fresh. They are hydrated on the client in addition to being rendered on the server.\n---\n\nIslands enable client side interactivity in Fresh. Islands are isolated Preact\ncomponents that are rendered on the server and then hydrated on the client. This\nis different from all other components in Fresh, as they are usually rendered on\nthe server only.\n\nIslands are defined by creating a file in the `islands/` folder in a Fresh\nproject. The name of this file must be a PascalCase or kebab-case name of the\nisland.\n\n```tsx islands/my-island.tsx\nimport { useSignal } from \"@preact/signals\";\n\nexport default function MyIsland() {\n  const count = useSignal(0);\n\n  return (\n    <div>\n      Counter is at {count}.{\" \"}\n      <button onClick={() => (count.value += 1)}>+</button>\n    </div>\n  );\n}\n```\n\nAn island can be used in a page like a regular Preact component. Fresh will take\ncare of automatically re-hydrating the island on the client.\n\n```tsx route/index.tsx\nimport MyIsland from \"../islands/my-island.tsx\";\n\nexport default function Home() {\n  return <MyIsland />;\n}\n```\n\n## Passing JSX to islands\n\nIslands support passing JSX elements via the `children` property.\n\n```tsx islands/my-island.tsx\nimport { useSignal } from \"@preact/signals\";\nimport { ComponentChildren } from \"preact\";\n\ninterface Props {\n  children: ComponentChildren;\n}\n\nexport default function MyIsland({ children }: Props) {\n  const count = useSignal(0);\n\n  return (\n    <div>\n      Counter is at {count}.{\" \"}\n      <button onClick={() => (count.value += 1)}>+</button>\n      {children}\n    </div>\n  );\n}\n```\n\nThis allows you to pass static content rendered by the server to an island in\nthe browser.\n\n```tsx routes/index.tsx\nimport MyIsland from \"../islands/my-island.tsx\";\n\nexport default function Home() {\n  return (\n    <MyIsland>\n      <p>This text is rendered on the server</p>\n    </MyIsland>\n  );\n}\n```\n\nYou can also create shared components in your `components/` directory, which can\nbe used in both static content and interactive islands. When these components\nare used within islands, interactivity can be added, such as `onClick` handlers\n(using an `onClick` handler on a button outside of an island will not fire).\n\n```tsx islands/my-island.tsx\nimport { useSignal } from \"@preact/signals\";\nimport { ComponentChildren } from \"preact\";\nimport Card from \"../components/Card.tsx\";\nimport Button from \"../components/Button.tsx\";\n\ninterface Props {\n  children: ComponentChildren;\n}\n\nexport default function MyIsland({ children }: Props) {\n  const count = useSignal(0);\n\n  return (\n    <Card>\n      Counter is at {count}.{\" \"}\n      <Button onClick={() => (count.value += 1)}>+</Button>\n      {children}\n    </Card>\n  );\n}\n```\n\n## Passing other props to islands\n\nPassing props to islands is supported, but only if the props are serializable.\nFresh can serialize the following types of values:\n\n- Primitive types `string`, `boolean`, `bigint`, and `null`\n- Most `number`s (`Infinity`, `-Infinity`, and `NaN` are silently converted to\n  `null`)\n- Plain objects with string keys and serializable values\n- Arrays containing serializable values\n- Uint8Array\n- JSX Elements (restricted to `props.children`)\n- Preact Signals (if the inner value is serializable)\n\nCircular references are supported. If an object or signal is referenced multiple\ntimes, it is only serialized once and the references are restored upon\ndeserialization. Passing complex objects like `Date`, custom classes, or\nfunctions is not supported.\n\nDuring server side rendering, Fresh annotates the HTML with special comments\nthat indicate where each island will go. This gives the code sent to the client\nenough information to put the islands where they are supposed to go without\nrequiring hydration for the static children of interactive islands. No\nJavascript is sent to the client when no interactivity is needed.\n\n```html Response body\n<!--frsh-myisland_default:default:0-->\n<div>\n  Counter is at 0.\n  <button>+</button>\n  <!--frsh-slot-myisland_default:children-->\n  <p>This text is rendered on the server</p>\n  <!--/frsh-slot-myisland_default:children-->\n</div>\n<!--/frsh-myisland_default:default:0-->\n```\n\n### Nesting islands\n\nIslands can be nested within other islands as well. In that scenario they act\nlike a normal Preact component, but still receive the serialized props if any\nwere present.\n\n```tsx islands/other-island.tsx\nimport { useSignal } from \"@preact/signals\";\nimport { ComponentChildren } from \"preact\";\n\ninterface Props {\n  children: ComponentChildren;\n  foo: string;\n}\n\nfunction randomNumber() {\n  return Math.floor(Math.random() * 100);\n}\n\nexport default function OtherIsland({ children, foo }: Props) {\n  const number = useSignal(randomNumber());\n\n  return (\n    <div>\n      <p>String from props: {foo}</p>\n      <p>\n        <button onClick={() => (number.value = randomNumber())}>Random</button>\n        {\" \"}\n        number is: {number}.\n      </p>\n    </div>\n  );\n}\n```\n\nIn essence, Fresh allows you to mix static and interactive parts in your app in\na way that's most optimal for your app. We'll keep sending only the JavaScript\nthat is needed for the islands to the browser.\n\n```tsx route/index.tsx\nimport MyIsland from \"../islands/my-island.tsx\";\nimport OtherIsland from \"../islands/other-island.tsx\";\n\nexport default function Home() {\n  return (\n    <div>\n      <MyIsland>\n        <OtherIsland foo=\"this prop will be serialized\" />\n      </MyIsland>\n      <p>Some more server rendered text</p>\n    </div>\n  );\n}\n```\n\n## Rendering islands on client only\n\nWhen using client-only APIs, like `EventSource` or `navigator.getUserMedia`,\nthis component will not run on the server as it will produce an error like:\n\n```\nAn error occurred during route handling or page rendering. ReferenceError: EventSource is not defined\n    at Object.MyIsland (file:///Users/someuser/fresh-project/islandsmy-island.tsx:6:18)\n    at m (https://esm.sh/v129/preact-render-to-string@6.2.0/X-ZS8q/denonext/preact-render-to-string.mjs:2:2602)\n    at m (https://esm.sh/v129/preact-render-to-string@6.2.0/X-ZS8q/denonext/preact-render-to-string.mjs:2:2113)\n    ....\n```\n\nUse the [`IS_BROWSER`](https://deno.land/x/fresh/runtime.ts?doc=&s=IS_BROWSER)\nflag as a guard to fix the issue:\n\n```tsx islands/my-island.tsx\nimport { IS_BROWSER } from \"$fresh/runtime.ts\";\n\nexport function MyIsland() {\n  // Return any prerenderable JSX here which makes sense for your island\n  if (!IS_BROWSER) return <div></div>;\n\n  // All the code which must run in the browser comes here!\n  // Like: EventSource, navigator.getUserMedia, etc.\n  return <div></div>;\n}\n```\n"
  },
  {
    "path": "docs/1.x/concepts/layouts.md",
    "content": "---\ndescription: |\n  Add a layout to provide common meta tags, context for application sub routes, and common layout.\n---\n\nA layout is defined in a `_layout.tsx` file in any sub directory (at any level)\nunder the `routes/` folder. It must contain a default export that is a regular\nPreact component. Only one such layout is allowed per sub directory.\n\n```txt-files Project structure\n<project root>\n└── routes\n    ├── sub\n    │   ├── page.tsx\n    │   └── index.tsx\n    ├── other\n    │   ├── _layout.tsx  # will be applied on top of `routes/_layout.tsx`\n    │   └── page.tsx\n    ├── _layout.tsx  # will be applied to all routes\n    └── _app.tsx\n```\n\nThe component to be wrapped is received via props, in addition to a few other\nthings. This allows for the introduction of a global container functioning as a\ntemplate which can be conditioned based on state and params. Note that any state\nset by middleware is available via `props.state`.\n\n```tsx routes/sub/_layout.tsx\nimport { PageProps } from \"$fresh/server.ts\";\n\nexport default function Layout({ Component, state }: PageProps) {\n  // do something with state here\n  return (\n    <div class=\"layout\">\n      <Component />\n    </div>\n  );\n}\n```\n\n## Async layouts\n\nIn case you need to fetch data asynchronously before rendering the layout, you\ncan use an async layout to do so.\n\n```tsx routes/sub/_layout.tsx\nimport { FreshContext } from \"$fresh/server.ts\";\n\nexport default async function Layout(req: Request, ctx: FreshContext) {\n  // do something with state here\n  const data = await loadData();\n\n  return (\n    <div class=\"layout\">\n      <p>{data.greeting}</p>\n      <ctx.Component />\n    </div>\n  );\n}\n```\n\n### Define helper\n\nTo make it a little quicker to write async layouts, Fresh ships with a\n`defineLayout` helper which automatically infers the correct types for the\nfunction arguments.\n\n```tsx routes/greet/_layout.tsx\nimport { defineLayout } from \"$fresh/server.ts\";\n\nexport default defineLayout(async (req, ctx) => {\n  const data = await loadData();\n\n  return (\n    <div class=\"layout\">\n      <p>{data.greeting}</p>\n      <ctx.Component />\n    </div>\n  );\n});\n```\n\n## Opting out of layout inheritance\n\nSometimes you want to opt out of the layout inheritance mechanism for a\nparticular route. This can be done via route configuration. Picture a directory\nstructure like this:\n\n```txt-files Project structure\n└── <root>/routes\n    ├── sub\n    │   ├── _layout_.tsx\n    │   ├── special.tsx  # should not inherit layouts\n    │   └── index.tsx\n    └── _layout.tsx\n```\n\nTo make `routes/sub/special.tsx` opt out of rendering layouts we can set\n`skipInheritedLayouts: true`.\n\n```tsx routes/sub/special.tsx\nimport { RouteConfig } from \"$fresh/server.ts\";\n\nexport const config: RouteConfig = {\n  skipInheritedLayouts: true, // Skip already inherited layouts\n};\n\nexport default function MyPage() {\n  return <p>Hello world</p>;\n}\n```\n\nYou can skip already inherited layouts inside a layout file:\n\n```tsx routes/special/_layout.tsx\nimport { LayoutConfig } from \"$fresh/server.ts\";\n\nexport const config: LayoutConfig = {\n  skipInheritedLayouts: true, // Skip already inherited layouts\n};\n\nexport default function MyPage() {\n  return <p>Hello world</p>;\n}\n```\n"
  },
  {
    "path": "docs/1.x/concepts/middleware.md",
    "content": "---\ndescription: |\n  Add middleware routes to intercept requests or responses for analytics purposes, access control, or anything else.\n---\n\nA middleware is defined in a `_middleware.ts` file. It will intercept the\nrequest in order for you to perform custom logic before or after the route\nhandler. This allows modifying or checking requests and responses. Common\nuse-cases for this are logging, authentication, and performance monitoring.\n\nEach middleware gets passed a `next` function in the context argument that is\nused to trigger child handlers. The `ctx` also has a `state` property that can\nbe used to pass arbitrary data to downstream (or upstream) handlers. This\n`state` is included in `PageProps` by default, which is available to both the\nspecial [\\_app](/docs/1.x/concepts/app-wrapper.md) wrapper and normal\n[routes](/docs/1.x/concepts/routes.md). `ctx.state` is normally set by modifying\nits properties, e.g. `ctx.state.loggedIn = true`, but you can also replace the\nentire object like `ctx.state = { loggedIn: true }`.\n\n```ts routes/_middleware.ts\nimport { FreshContext } from \"$fresh/server.ts\";\n\ninterface State {\n  data: string;\n}\n\nexport async function handler(\n  req: Request,\n  ctx: FreshContext<State>,\n) {\n  ctx.state.data = \"myData\";\n  const resp = await ctx.next();\n  resp.headers.set(\"server\", \"fresh server\");\n  return resp;\n}\n```\n\n```ts routes/myHandler.ts\nexport const handler: Handlers<any, { data: string }> = {\n  GET(_req, ctx) {\n    return new Response(`middleware data is ${ctx.state.data}`);\n  },\n};\n```\n\nMiddlewares are scoped and can be layered. This means a project can have\nmultiple middlewares, each covering a different set of routes. If multiple\nmiddlewares cover a route, they will all be run, in order of specificity (least\nspecific first).\n\nFor example, take a project with the following routes:\n\n```txt-files Project Structure\n└── <root>/routes\n    ├── _middleware.ts\n    ├── index.ts\n    └── admin\n        ├── _middleware.ts\n        └── index.ts\n        └── signin.ts\n```\n\nFor a request to `/` the request will flow like this:\n\n1. The `routes/_middleware.ts` middleware is invoked.\n2. Calling `ctx.next()` will invoke the `routes/index.ts` handler.\n\nFor a request to `/admin` the request flows like this:\n\n1. The `routes/_middleware.ts` middleware is invoked.\n2. Calling `ctx.next()` will invoke the `routes/admin/_middleware.ts`\n   middleware.\n3. Calling `ctx.next()` will invoke the `routes/admin/index.ts` handler.\n\nFor a request to `/admin/signin` the request flows like this:\n\n1. The `routes/_middleware.ts` middleware is invoked.\n2. Calling `ctx.next()` will invoke the `routes/admin/_middleware.ts`\n   middleware.\n3. Calling `ctx.next()` will invoke the `routes/admin/signin.ts` handler.\n\nA single middleware file can also define multiple middlewares (all for the same\nroute) by exporting an array of handlers instead of a single handler. For\nexample:\n\n```ts routes/_middleware.ts\nexport const handler = [\n  async function middleware1(req, ctx) {\n    // do something\n    return ctx.next();\n  },\n  async function middleware2(req, ctx) {\n    // do something\n    return ctx.next();\n  },\n];\n```\n\nIt should be noted that `middleware` has access to route parameters. If you're\nrunning a fictitious `routes/[tenant]/admin/_middleware.ts` like this:\n\n```ts routes/[tenant]/admin/_middleware.ts\nimport { FreshContext } from \"$fresh/server.ts\";\n\nexport async function handler(_req: Request, ctx: FreshContext) {\n  const currentTenant = ctx.params.tenant;\n  // do something with the tenant\n  const resp = await ctx.next();\n  return resp;\n}\n```\n\nand the request is to `mysaas.com/acme/admin/`, then `currentTenant` will have\nthe value of `acme` in your middleware.\n\n## Middleware Destination\n\nTo set the stage for this section, let's focus on the part of `FreshContext`\nthat looks like this:\n\n```ts fresh 🍋\nexport interface FreshContext<State = Record<string, unknown>> {\n  ...\n  next: () => Promise<Response>;\n  state: State;\n  destination: router.DestinationKind;\n  remoteAddr: {\n    transport: \"tcp\" | \"udp\";\n    hostname: string;\n    port: number;\n  };\n  ...\n}\n```\n\nand `router.DestinationKind` is defined like this:\n\n```ts fresh 🍋\nexport type DestinationKind = \"internal\" | \"static\" | \"route\" | \"notFound\";\n```\n\nThis is useful if you want your middleware to only run when a request is headed\nfor a `route`, as opposed to something like `http://localhost:8001/favicon.ico`.\n\n### Example\n\nInitiate a new Fresh project (`deno run -A -r https://fresh.deno.dev/`) and then\ncreate a `_middleware.ts` file in the `routes` folder like this:\n\n```ts routes/_middleware.ts\nimport { FreshContext } from \"$fresh/server.ts\";\n\nexport async function handler(req: Request, ctx: FreshContext) {\n  console.log(ctx.destination);\n  console.log(req.url);\n  const resp = await ctx.next();\n  return resp;\n}\n```\n\nIf you start up your server (`deno task start`) you'll see the following:\n\n```sh Terminal\nTask start deno run -A --watch=static/,routes/ dev.ts\nWatcher Process started.\nThe manifest has been generated for 4 routes and 1 islands.\n\n 🍋 Fresh ready\n    Local: http://localhost:8000/\n\nroute\nhttp://localhost:8000/\ninternal\nhttp://localhost:8000/_frsh/js/3c7400558fc00915df88cb181036c0dbf73ab7f5/deserializer.js\ninternal\nhttp://localhost:8000/_frsh/js/3c7400558fc00915df88cb181036c0dbf73ab7f5/signals.js\ninternal\nhttp://localhost:8000/_frsh/js/3c7400558fc00915df88cb181036c0dbf73ab7f5/plugin-twind-main.js\ninternal\nhttp://localhost:8000/_frsh/js/3c7400558fc00915df88cb181036c0dbf73ab7f5/main.js\ninternal\nhttp://localhost:8000/_frsh/js/3c7400558fc00915df88cb181036c0dbf73ab7f5/island-counter.js\ninternal\nhttp://localhost:8000/_frsh/refresh.js\nstatic\nhttp://localhost:8000/logo.svg?__frsh_c=3c7400558fc00915df88cb181036c0dbf73ab7f5\ninternal\nhttp://localhost:8000/_frsh/alive\ninternal\nhttp://localhost:8000/_frsh/js/3c7400558fc00915df88cb181036c0dbf73ab7f5/chunk-PDMKJVJ5.js\ninternal\nhttp://localhost:8000/_frsh/js/3c7400558fc00915df88cb181036c0dbf73ab7f5/chunk-UGFDDSOV.js\ninternal\nhttp://localhost:8000/_frsh/js/3c7400558fc00915df88cb181036c0dbf73ab7f5/chunk-RCK7U3UF.js\n```\n\nThat first `route` request is for when `Fresh` responds with the root level\n`index.tsx` route. The rest, as you can see, are either `internal` or `static`\nrequests. You can use `ctx.destination` to filter these out if your middleware\nis only supposed to deal with routes.\n\n## Middleware Redirects\n\nIf you want to redirect a request from a middleware, you can do so by returning:\n\n```ts routes/_middleware.ts\nexport function handler(req: Request): Response {\n  return Response.redirect(\"https://example.com\", 307);\n}\n```\n\n`307` stands for temporary redirect. You can also use `301` for permanent\nredirect. You can also redirect to a relative path by doing:\n\n```ts routes/_middleware.ts\nexport function handler(req: Request): Response {\n  return new Response(\"\", {\n    status: 307,\n    headers: { Location: \"/my/new/relative/path\" },\n  });\n}\n```\n"
  },
  {
    "path": "docs/1.x/concepts/partials.md",
    "content": "---\ndescription: |\n  Partials allow areas of a page to be updated without causing the browser to reload the page. They enable optimized fine grained UI updates and can be used to do client-side navigation.\n---\n\nPartials allow areas of the page to be updated with new content by the server\nwithout causing the browser to reload the page. They make your website feel more\napp-like because only the parts of the page that need to be updated will be\nupdated.\n\n## Enabling partials\n\nPartials are enabled by adding a `f-client-nav` attribute to an HTML element and\nwrapping one or more areas in the page with a\n`<Partial name=\"my-partial\">`-component.\n\nThe quickest way to get started is to enable partials for every page in\n`routes/_app.tsx` by making the following changes.\n\n```diff routes/_app.tsx\n  import { PageProps } from \"$fresh/server.ts\";\n+ import { Partial } from \"$fresh/runtime.ts\";\n\n  export default function App({ Component }: PageProps) {\n    return (\n      <html>\n        <head>\n          <meta charset=\"utf-8\" />\n          <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n          <title>My Fresh app</title>\n        </head>\n-       <body>\n+       <body f-client-nav>\n+         <Partial name=\"body\">\n            <Component />\n+         </Partial>\n        </body>\n      </html>\n    );\n  }\n```\n\nBy adding the `f-client-nav` attribute, we enable partials for every element\nbeneath the `<body>`-tag. To mark an area of the page as a partial we wrap it\nwith a `<Partial>`-component with a unique name.\n\nBehind the scenes, when the user clicks an `<a>`-tag, Fresh fetches the new page\nand only pulls out the relevant content out of the HTML response. When it finds\na matching partial area it will update the content inside the partial.\n\n> [info]: The `name` prop of the `<Partial>` component is expected to be unique\n> among Partials. That's how Fresh knows which parts of the response need to go\n> on the current page.\n\n> [info]: Passing `f-client-nav={false}` disables client side navigation for all\n> elements below the current node.\n\n### Optimizing partial requests\n\nBy default, with `f-client-nav` set, Fresh fetches the full next page and only\npicks out the relevant parts of the response. We can optimize this pattern\nfurther by only rendering the parts we need, instead of always rendering the\nfull page. This is done by adding the `f-partial` attribute to a link.\n\n```diff routes/_app.tsx\n- <a href=\"/docs/routes\">Routes</a>\n+ <a href=\"/docs/routes\" f-partial=\"/partials/docs/routes\">Routes</a>\n```\n\nWhen the `f-partial` attribute is present, Fresh will navigate to the page URL\ndefined in the `href` attribute, but fetch the updated UI from the URL specified\nin `f-partial` instead. This can be a highly optimized route that only delivers\nthe content you care about.\n\nLet's use a typical documentation page layout as an example. It often features a\nmain content area and a sidebar of links to switch between pages of the\ndocumentation (marked green here).\n\n![A sketched layout of a typical documentation page with the sidebar on the left composed of green links and a main content area on the right. The main content area is labeled as Partial docs-content](/docs/1.x/fresh-partial-docs.png)\n\nThe code for such a page (excluding styling) might look like this:\n\n```tsx routes/docs/[id].tsx\nexport default defineRoute(async (req, ctx) => {\n  const content = await loadContent(ctx.params.id);\n\n  return (\n    <div>\n      <aside>\n        <a href=\"/docs/page1\">Page 1</a>\n        <a href=\"/docs/page2\">Page 2</a>\n      </aside>\n      <Partial name=\"docs-content\">\n        {content}\n      </Partial>\n    </div>\n  );\n});\n```\n\nAn optimal route that only renders the content instead of the outer layout with\nthe sidebar might look like this respectively.\n\n```tsx routes/partials/docs/[id].tsx\nimport { defineRoute, RouteConfig } from \"$fresh/server.ts\";\nimport { Partial } from \"$fresh/runtime.ts\";\n\n// We only want to render the content, so disable\n// the `_app.tsx` template as well as any potentially\n// inherited layouts\nexport const config: RouteConfig = {\n  skipAppWrapper: true,\n  skipInheritedLayouts: true,\n};\n\nexport default defineRoute(async (req, ctx) => {\n  const content = await loadContent(ctx.params.id);\n\n  // Only render the new content\n  return (\n    <Partial name=\"docs-content\">\n      {content}\n    </Partial>\n  );\n});\n```\n\nBy adding the `f-partial` attribute we tell Fresh to fetch the content from our\nnewly added `/partials/docs/[id].tsx` route.\n\n```diff routes/docs/[id].tsx\n  <aside>\n-   <a href=\"/docs/page1\">Page 1</a>\n-   <a href=\"/docs/page2\">Page 2</a>\n+   <a href=\"/docs/page1\" f-partial=\"/partials/docs/page1\">Page 1</a>\n+   <a href=\"/docs/page2\" f-partial=\"/partials/docs/page2\">Page 2</a>\n  </aside>\n```\n\nWith this in place, Fresh will navigate to the new page when clicking any of the\ntwo links and _only_ load the content rendered by our optimized partial route.\n\n> Currently, `f-partial` is scoped to `<a>`, `<button>` and `<form>` elements.\n> This might be extended to more elements in the future.\n\n## Sending multiple Partials at the same time\n\nA neat aspect of partials in Fresh is that a response can return as many\npartials as desired. That way you can update multiple unrelated areas on your\npage in one single HTTP response. A scenario where this is useful are online\nshops for example.\n\n```tsx routes/partials/cart.tsx\nexport default function AddToCartPartial() {\n  return (\n    <>\n      <Partial name=\"cart-items\" mode=\"append\">\n        {/* Render the new cart item here */}\n      </Partial>\n      <Partial name=\"total-price\">\n        <p>Total: {totalPrice} €</p>\n      </Partial>\n    </>\n  );\n}\n```\n\nBoth partials will be applied to the current page.\n\n## Replacement mode\n\nBy default the whole content inside a partial will be replaced, but there are\nscenarios where you want to prepend or append new content instead. This can be\nachieved by adding the `mode` prop to a `Partial` component.\n\n- `replace` - Swap out the content of the existing partial (default)\n- `prepend` - Insert the new content before the existing content\n- `append` - Insert the new content after the existing content\n\nPersonally, we’ve found that the `append` mode is really useful when you have an\nUI which displays log messages or similar list-like data.\n\n```tsx routes/log.tsx\nexport default function LogView() {\n  const lines = getNewLogLines();\n\n  return (\n    <Partial name=\"logs-list\" mode=\"append\">\n      {lines.map((line) => {\n        return <li key={line}>{line}</li>;\n      })}\n    </Partial>\n  );\n}\n```\n\n> [info]: When picking the `prepend` or `append` mode, make sure to add keys to\n> the elements.\n\n## Bypassing or disabling Partials\n\nIf you want to exempt a particular element from triggering a partial request\nlike on a particular link, form or button, you can opt out of it by setting\n`f-client-nav={false}` on the element or one of the ancestor elements.\n\n```tsx routes/index.tsx\n<body f-client-nav>\n  {/* This will cause a partial navigation */}\n  <a href=\"/docs/page1\">With partials</a>\n\n  {/* This WONT cause a partial navigation */}\n  <a href=\"/docs/page1\" f-client-nav={false}>No partials</a>\n\n  {/* This WONT cause a partial navigation on any elements below */}\n  <div f-client-nav={false}>\n    <div>\n      <a href=\"/docs/page1\">No partials</a>\n    </div>\n  </div>\n</body>;\n```\n\nWhenever an element is clicked Fresh checks if it has the `f-client-nav`\nattribute and if it is set to `true`. If the element itself doesn't have such an\nattribute, it will check if any of the ancestor elements has it. If an element\nwas found with a truthy `f-client-nav` attribute a partial request will be\ntriggered. If there is no such attribute or if it's set to `false`, no partial\nrequest will occur.\n"
  },
  {
    "path": "docs/1.x/concepts/plugins.md",
    "content": "---\ndescription: Plugins can add new functionality to Fresh without requiring significant complexity.\n---\n\nPlugins can dynamically add new functionality to Fresh without exposing\nsignificant complexity to the user. Users can add plugins by importing and\ninitializing them in their `main.ts` file:\n\n```ts main.ts\nimport { start } from \"$fresh/server.ts\";\nimport manifest from \"./fresh.gen.ts\";\n\nimport twindPlugin from \"$fresh/plugins/twind.ts\";\nimport twindConfig from \"./twind.config.js\";\n\nawait start(manifest, {\n  plugins: [\n    // This line configures Fresh to use the first-party twind plugin.\n    twindPlugin(twindConfig),\n  ],\n});\n```\n\nCurrently, the only available first-party plugin is the Twind plugin.\nThird-party plugins are also supported - they can be imported from any HTTP\nserver, like any other Deno module.\n\nPlugin hooks are executed in the order that the plugins are defined in the\n`plugins` array. This means that the first plugin in the array will be executed\nfirst, and the last plugin in the array will be executed last. For many plugins,\nthis does not matter, but for some plugins it may.\n\n## Creating a plugin\n\nFresh plugins are in essence a collection of hooks that allow the plugin to hook\ninto various systems inside of Fresh.\n\nA Fresh plugin is just a JavaScript object that conforms to the\n[Plugin](https://deno.land/x/fresh/server.ts?s=Plugin) interface. The only\nrequired property of a plugin is its name. Names must only contain the\ncharacters `a`-`z`, and `_`.\n\n```ts my-plugin.ts\nimport { Plugin } from \"$fresh/server.ts\";\n\nconst plugin: Plugin = {\n  name: \"my_plugin\",\n};\n```\n\nA plugin containing only a name is technically valid, but not very useful. To be\nable to do anything with a plugin, it must register some hooks, middlewares, or\nroutes.\n\n### Hook: `render`\n\nThe render hook allows plugins to:\n\n- Control timing of the synchronous render of a page.\n- Inject additional CSS and JS into the rendered page.\n\nThis is commonly used to set thread local variables for the duration of the\nrender (for example preact global context, preact option hooks, or for style\nlibraries like Twind). After render is complete, the plugin can inject inline\nCSS and JS modules (with attached state) into the page.\n\nThe render hook is called with the\n[`PluginRenderContext`](https://deno.land/x/fresh/server.ts?s=PluginRenderContext)\nobject, which contains a `render()` method. This method must be invoked during\nthe render hook to actually render the page. It is a terminal error to not call\nthe `render()` method during the render hook.\n\nThe `render()` method returns a\n[`PluginRenderFunctionResult`](https://deno.land/x/fresh/server.ts?s=PluginRenderFunctionResult)\nobject which contains the HTML text of the rendered page, as well as a boolean\nindicating whether the page contains any islands that will be hydrated on the\nclient.\n\nThe `render` hook needs to synchronously return a\n[`PluginRenderResult`](https://deno.land/x/fresh/server.ts?s=PluginRenderResult)\nobject. Additional CSS and JS modules can be added to be injected into the page\nby adding them to `styles`, `links` and `scripts` arrays in this object. The\nplugin can also replace the HTML in side the `<body>`-element of the page by\nincluding a `htmlText` string in this object.\n\n`styles` are injected into the `<head>` of the page as inline CSS. Each entry\ncan define the CSS text to inject, as well as an optional `id` for the style\ntag, and an optional `media` attribute for the style tag.\n\n`links` are injected into the `<head>` of the page as `<link>` tags. A link tag\nis created for each entry, with attributes from the entry's properties.\n\n`scripts` define JavaScript/TypeScript modules to be injected into the page. The\npossibly loaded modules need to be defined up front in the `Plugin#entrypoints`\nproperty. Each defined module must be a JavaScript/TypeScript module that has a\ndefault export of a function that takes one (arbitrary) argument, and returns\nnothing (or a promise resolving to nothing). Fresh will call this function with\nthe state defined in the `scripts` entry. The state can be any arbitrary JSON\nserializable JavaScript value.\n\nFor an example of a plugin that uses the `render` hook, see the first-party\n[Twind plugin](https://github.com/denoland/fresh/blob/1.x/plugins/twind.ts).\n\n### Hook: `renderAsync`\n\nThis hook is largely the same as the `render` hook, with a couple of key\ndifferences to make asynchronous style and script generation possible. It must\nasynchronously return its\n[`PluginRenderResult`](https://deno.land/x/fresh/server.ts?s=PluginRenderResult),\neither from an `async/await` function or wrapped within a promise.\n\nThe render hook is called with the\n[`PluginAsyncRenderContext`](https://deno.land/x/fresh/server.ts?s=PluginAsyncRenderContext)\nobject, which contains a `renderAsync()` method. This method must be invoked\nduring the render hook to actually render the page. It is a terminal error to\nnot call the `renderAsync()` method during the render hook.\n\nThis is useful for when plugins are generating styles and scripts with\nasynchronous dependencies based on the `htmlText`. Unlike the synchronous render\nhook, async render hooks for multiple pages can be running at the same time.\nThis means that unlike the synchronous render hook, you can not use global\nvariables to propagate state between the render hook and the renderer.\n\nThe `renderAsync` hooks start before any page rendering occurs, and finish after\nall rendering is complete -- they wrap around the underlying JSX->string\nrendering, plugin `render` hooks, and the\n[`RenderFunction`](https://deno.land/x/fresh/server.ts?s=RenderFunction) that\nmay be provided to Fresh's `start` entrypoint in the `main.ts` file.\n\n### Hook: `buildStart`\n\nThis hook is run at the start of the Fresh\n[ahead-of-time build task](/docs/1.x/concepts/ahead-of-time-builds). It may be\nsynchronous or asynchronous.\n\nThe build start hook is called with the\n[`ResolvedFreshConfig`](https://deno.land/x/fresh/src/server/types.ts?s=ResolvedFreshConfig)\nobject, which contains the full Fresh configuration.\n\nThis hook may be used to generate precompiled static assets. Any files saved to\nthe `static` subfolder of `config.build.outDir` (typically `_fresh`) will be\nserved the same as other [static files](/docs/1.x/concepts/static-files).\n\n### Hook: `buildEnd`\n\nThis hook is run at the end of the Fresh\n[ahead-of-time build task](/docs/1.x/concepts/ahead-of-time-builds). It may be\nsynchronous or asynchronous.\n\n### Routes and Middlewares\n\nYou can create routes and middlewares that get loaded and rendered like the\nnormal [routes](/docs/1.x/concepts/routes) and\n[middlewares](/docs/1.x/concepts/middleware).\n\nThe plugin routes and middlewares need a defined path in the format of a file\nname without a filetype inside the routes directory(E.g. `blog/index`,\n`blog/[slug]`).\n\nFor more examples see the [Concepts: Routing](/docs/1.x/concepts/routing) page.\n\nTo create a middleware you need to create a `MiddlewareHandler` function.\n\nAnd to create a route you can create both a Handler and/or component.\n\nBelow is an example plugin that creates a route and middleware\n\n```ts my-route-and-middleware-plugin.ts\nimport { MiddlewareHandlerContext, Plugin } from \"$fresh/server.ts\";\nimport { handler as testMiddleware } from \"./sample_routes/_middleware.ts\";\nimport { SimpleRoute } from \"./sample_routes/simple-route.tsx\";\nexport type { Options };\n\ninterface Options {\n  title: string;\n}\nexport type PluginMiddlewareState = {\n  num: number;\n  test: string;\n};\n\nconst twoPointlessMiddlewares = [\n  async (\n    _req: Request,\n    ctx: MiddlewareHandlerContext<PluginMiddlewareState>,\n  ) => {\n    ctx.state.num = ctx.state.num === undefined ? 1 : ctx.state.num + 1;\n    return await ctx.next();\n  },\n  async (\n    _req: Request,\n    ctx: MiddlewareHandlerContext<PluginMiddlewareState>,\n  ) => {\n    ctx.state.num = ctx.state.num === undefined ? 1 : ctx.state.num + 1;\n    return await ctx.next();\n  },\n];\n\nexport default function routePlugin(\n  options: Options,\n): Plugin<PluginMiddlewareState> {\n  return {\n    name: \"routePlugin\",\n    middlewares: [{\n      middleware: { handler: testMiddleware },\n      path: \"/\",\n    }, {\n      middleware: {\n        handler: twoPointlessMiddlewares,\n      },\n      path: \"lots-of-middleware\",\n    }],\n    routes: [\n      { path: \"no-leading-slash-here\", component: SimpleRoute },\n    ],\n  };\n}\n```\n\n### Islands\n\nIslands from plugins can be loaded by specifying a list of file paths in your\nplugin. Those files will be treated by Fresh as if they had been placed inside\nthe `islands/` directory. They will be processed and bundled for the browser in\nthe same way.\n\n```tsx my-island-plugin.ts\nimport { Plugin } from \"$fresh/server.ts\";\n\nexport default function myIslandPlugin(): Plugin {\n  return {\n    name: \"my-island-plugin\",\n    islands: {\n      baseLocation: import.meta.url,\n      paths: [\n        \"./plugin/MyPluginIsland.tsx\",\n        \"./plugin/OtherPluginIsland.tsx\",\n      ],\n    },\n  };\n}\n```\n"
  },
  {
    "path": "docs/1.x/concepts/routes.md",
    "content": "---\ndescription: |\n  Routes are the basic building block of Fresh applications. They are used to define the behaviour the application when a given path is requested.\n---\n\nAt their core, routes describe how a request for a given path should be handled,\nand what the response should be. To do this, routes have two main parts: the\nhandler, and the component. A route can have either one, or both, but never\nneither.\n\nThe handler is a function that is called for every request to the route. It\nneeds to return a response that is then sent to the client. The response could\nbe anything: a plain text string, a JSON object, an HTML page, a WebSocket\nconnection, a streaming file, or pretty much anything else. The handler is\npassed a `render` function that it can call to invoke rendering a component.\n\nThe component is the template for a page. It is a JSX element that is rendered\non the server. The page component gets passed props that can be used by it to\ndetermine exactly what should be rendered. By default components receive props\nconsisting of: the request URL, the matching route (as a string), the matches\nfrom the URL pattern match, any state set by middleware, and any data passed to\nthe handler's `render` function.\n\n## Handler route\n\nLet's look at a basic route that returns a plain text string:\n\n```tsx routes/plain.tsx\nimport { FreshContext, Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  GET(_req: Request, _ctx: FreshContext) {\n    return new Response(\"Hello World\");\n  },\n};\n```\n\nTo define a handler, one needs to export a `handler` function or object from the\nroute module. If the handler is an object, each key in the object is the name of\nthe HTTP method that the handler should be called for. For example the `GET`\nhandler above is called for `GET` requests. If the handler is a function, it is\ncalled for all requests regardless of the method. If an HTTP method does not\nhave a corresponding handler, a 405 HTTP error is returned.\n\n## Component route\n\nNow, let's render some HTML using the route component:\n\n```tsx routes/html.tsx\nimport { PageProps } from \"$fresh/server.ts\";\n\nexport default function Page(props: PageProps) {\n  return <div>You are on the page '{props.url.href}'.</div>;\n}\n```\n\nThe page component needs to be the default export of the route module. It is\npassed props that can be used to render the page.\n\nAs you can see in the second example, if no handler is explicitly defined a\ndefault handler is used that just renders out the page component if present. You\ncan also override the default handler though to modify how exactly rendering\nshould work.\n\n## Mixed handler and component route\n\nIn the below example, a custom handler is used to add a custom header to the\nresponse after rendering the page component.\n\n```tsx routes/html.tsx\nimport { FreshContext, Handlers, PageProps } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(_req: Request, ctx: FreshContext) {\n    const resp = await ctx.render();\n    resp.headers.set(\"X-Custom-Header\", \"Hello World\");\n    return resp;\n  },\n};\n\nexport default function Page(props: PageProps) {\n  return <div>You are on the page '{props.url.href}'.</div>;\n}\n```\n\n## Async route components\n\nHaving a separate route handler and component function is nice, when you want to\ntest these in isolation, but can become a bit cumbersome to maintain. They\nrequire some additional indirection of declaring an interface for the component\n`Data` when you're passing it around through `ctx.render()`.\n\n```tsx routes/page.tsx\ninterface Data {\n  foo: number;\n}\n\nexport const handler: Handlers<Data> = {\n  async GET(req, ctx) {\n    const value = await loadFooValue();\n    return ctx.render({ foo: value });\n  },\n};\n\nexport default function MyPage(props: PageProps<Data>) {\n  return <p>foo is: {props.data.foo}</p>;\n}\n```\n\nWhen a route has both a component and a `GET` handler, they are typically very\nclosely coupled. With async route components you can merge the two together and\navoid having to create the `Data` interface boilerplate.\n\n```tsx routes/page.tsx\n// Async route component\nexport default async function MyPage(req: Request, ctx: RouteContext) {\n  const value = await loadFooValue();\n  return <p>foo is: {value}</p>;\n}\n```\n\nThe code gets a little shorter with async route components. Conceptually, you\ncan think of async route components inlining the `GET` handler into the\ncomponent function. Note, that you can still add additional HTTP handlers in the\nsame file like before.\n\n```tsx routes/page.tsx\nexport const handler: Handlers = {\n  async POST(req) {\n    // ... do something here\n  },\n};\n\nexport default async function MyPage(req: Request, ctx: RouteContext) {\n  const value = await loadFooValue();\n  return <p>foo is: {value}</p>;\n}\n```\n\n### Returning Response objects\n\nQuite often a route handler needs to render a 404 page or bail out of rendering\nin another manner. This can be done by returning a `Response` object.\n\n```tsx route/page.tsx\n// Async route component\nexport default async function MyPage(req: Request, ctx: RouteContext) {\n  const value = await loadFooValue();\n\n  // Return 404 if `value` is null\n  if (value === null) {\n    return ctx.renderNotFound();\n  }\n\n  // Returning a response object directly works too\n  if (value === \"redirect\") {\n    const headers = new Headers();\n    headers.set(\"location\", \"/some-other-page\");\n    return new Response(null, {\n      status: 302,\n      headers,\n    });\n  }\n\n  return <p>foo is: {value}</p>;\n}\n```\n\n### Define helper\n\nTo make it a little quicker to write async routes, Fresh ships with a\n`defineRoute` helper which automatically infers the correct types for the\nfunction arguments.\n\n```tsx routes/hello.tsx\nimport { defineRoute } from \"$fresh/server.ts\";\n\nexport default defineRoute(async (req, ctx) => {\n  const data = await loadData();\n\n  return (\n    <div class=\"page\">\n      <h1>Hello {data.name}</h1>\n    </div>\n  );\n});\n```\n"
  },
  {
    "path": "docs/1.x/concepts/routing.md",
    "content": "---\ndescription: |\n  File based routing is the simplest way to do routing in Fresh apps. Additionally custom patterns can be configured per route.\n---\n\nRouting is the mechanism that determines what route a given incoming request is\nhandled by. Fresh routes requests based on their URL path. By default routes\nspecify which paths they are invoked for using the name of the file. Routes can\nalso define a custom [URL pattern][urlpattern] to match against for more\nadvanced use cases.\n\nThe file based routing in Fresh is very similar to the file based routing seen\nin other frameworks, namely Next.js. File names are used to determine which\nroute a given request should be handled by. The pattern is determined based on\nthe path of the file on disk, relative to the `routes/` directory.\n\nFile names are mapped to route patterns as follows:\n\n- File extensions are ignored.\n- Literals in the file path are treated as string literals to match.\n- Files named `<path>/index.<ext>` behave identically to a file named\n  `<path>.<ext>`.\n- Path segments can be made dynamic by surrounding an identifier with `[` and\n  `]`.\n- Paths where the last path segment follows the structure `[...<ident>]` are\n  treated as having a wildcard suffix.\n\nHere is a table of file names, which route patterns they map to, and which paths\nthey might match:\n\n| File name                   | Route pattern          | Matching paths                          |\n| --------------------------- | ---------------------- | --------------------------------------- |\n| `index.ts`                  | `/`                    | `/`                                     |\n| `about.ts`                  | `/about`               | `/about`                                |\n| `blog/index.ts`             | `/blog`                | `/blog`                                 |\n| `blog/[slug].ts`            | `/blog/:slug`          | `/blog/foo`, `/blog/bar`                |\n| `blog/[slug]/comments.ts`   | `/blog/:slug/comments` | `/blog/foo/comments`                    |\n| `old/[...path].ts`          | `/old/:path*`          | `/old/foo`, `/old/bar/baz`              |\n| `docs/[[version]]/index.ts` | `/docs{/:version}?`    | `/docs`, `/docs/latest`, `/docs/canary` |\n\nAdvanced use-cases can require that a more complex pattern be used for matching.\nA custom [URL pattern][urlpattern] can be specified in the route configuration.\nThis pattern will be used instead of the file path based pattern:\n\n```ts routes/x.ts\nimport { RouteConfig } from \"$fresh/server.ts\";\n\nexport const config: RouteConfig = {\n  routeOverride: \"/x/:module@:version/:path*\",\n};\n\n// ...\n```\n\n## Route Groups\n\nWhen working with [layouts](/docs/1.x/concepts/layouts) or\n[middlewares](/docs/1.x/concepts/middleware), you'll sometimes come across a\nsituation where you want your routes to inherit from a layout other than what's\nsuggested by the URL segment.\n\nLet's illustrate that with an example:\n\n```txt Example page layout\n/about -> layout A\n/career -> layout A\n/archive -> layout B\n/contact -> layout B\n```\n\nWithout any way to group routes this is a problem because every route segment\ncan only have one `_layout` file.\n\n```txt-files Project structure\n<project root>\n└── routes\n    ├── _layout.tsx  # applies to all routes here :(\n    ├── about.tsx\n    ├── career.tsx\n    ├── archive.tsx\n    └── contact.tsx\n```\n\nWe can solve this problem with route groups. A route group is a folder which has\na name that is wrapped in parentheses. For example `(info)` would be considered\na route group and so would `(marketing)`. This enables us to group related\nroutes in a folder and use a different `_layout` file for each group.\n\n```txt-files Project structure\n└── <root>/routes\n    ├── (marketing)\n    │   ├── _layout.tsx  # only applies to about.tsx and career.tsx\n    │   ├── about.tsx\n    │   └── career.tsx\n    └── (info)\n        ├── _layout.tsx  # only applies to archive.tsx and contact.tsx\n        ├── archive.tsx\n        └── contact.tsx\n```\n\n> [warn]: Be careful about routes in different groups which match to the same\n> URL. Such scenarios will lead to ambiguity as to which route file should be\n> picked.\n>\n> ```txt-files Project structure\n> └── <root>/routes\n>     ├── (group-1)\n>     │   └── about.tsx  # Bad: Maps to same `/about` url\n>     └── (group-2)\n>         └── about.tsx  # Bad: Maps to same `/about` url\n> ```\n\n[urlpattern]: https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API\n\n## Co-location\n\nIf you want to store components and islands closer to their routes, you may want\nto use co-location.\n\nWhen the name of a route group folder starts with an underscore, like\n`(_components)`, Fresh will ignore that folder and it’s effectively treated as\nprivate. This means you can use these private route folders to store components\nrelated to a particular route.\n\nFollowing the above example, say you have some components you only want to use\nin your marketing pages, you could create a route group folder `(_components)`\nto house these.\n\nThe one special name is `(_islands)` which tells Fresh to treat all files in\nthat folder as an island.\n\n```txt-files Project structure\n└── <root>/routes\n    ├── (marketing)\n    │   ├── _layout.tsx\n    │   ├── about.tsx\n    │   ├── career.tsx\n    │   ├── (_components)\n    │   │   └── newsletter-cta.tsx\n    │   └── (_islands)\n    │       └── interactive-stats.tsx # Fresh treats this as an island\n    └── shop\n        ├── (_components)\n        │   └── product-card.tsx\n        └── (_islands)\n            └── cart.tsx # Fresh treats this as an island\n```\n\nCombined together, this gives you the ability to organise your code on a feature\nbasis and put all related components, islands or anything else into a shared\nfolder.\n"
  },
  {
    "path": "docs/1.x/concepts/server-components.md",
    "content": "---\ndescription: |\n  Fresh's architecture is designed to leverage server components by default.\n---\n\nIf you've read about Fresh's [architecture](/docs/1.x/concepts/architecture)\nthen you know that it's based on the islands architecture pattern. The flip side\nof this is that everything else is, by default, a server component. When you\n[create a route](/docs/1.x/getting-started/create-a-route), all of the\ncomponents used are rendered on the server. No JavaScript is sent to the client,\nunless you specifically include something from the `/islands/` folder.\n\nInternally, Fresh's rendering heavily leverages\n[preact-render-to-string](https://github.com/preactjs/preact-render-to-string).\nThis is the exact library mentioned on Preact's\n[Server-Side Rendering](https://preactjs.com/guide/v10/server-side-rendering/)\narticle.\n"
  },
  {
    "path": "docs/1.x/concepts/server-configuration.md",
    "content": "---\ndescription: |\n  The ability to configure the core Fresh server leads to its flexibility.\n---\n\nIn this page we discuss how the server can be configured during startup.\n\nThe signature of the primary method looks like this:\n\n```ts main.ts\nexport async function start(manifest: Manifest, config: FreshConfig = {});\n```\n\n## Configuration\n\n`Manifest` comes from `fresh.gen.ts`, so nothing to do there. `config` is where\nthings get interesting.\n[`FreshConfig`](https://deno.land/x/fresh/server.ts?s=FreshConfig) looks like\nthis:\n\n```ts fresh 🍋\nexport interface FreshConfig {\n  build?: {\n    /**\n     * The directory to write generated files to when `dev.ts build` is run.\n     * This can be an absolute path, a file URL or a relative path.\n     */\n    outDir?: string;\n    /**\n     * This sets the target environment for the generated code. Newer\n     * language constructs will be transformed to match the specified\n     * support range. See https://esbuild.github.io/api/#target\n     * @default {\"es2022\"}\n     */\n    target?: string | string[];\n  };\n  render?: RenderFunction;\n  plugins?: Plugin[];\n  staticDir?: string;\n  router?: RouterOptions;\n  server?: Partial<Deno.ServeTlsOptions>;\n}\n```\n\nAnd for completeness here are the remaining two types:\n\n```ts fresh 🍋\nexport type RenderFunction = (\n  ctx: RenderContext,\n  render: InnerRenderFunction,\n) => void | Promise<void>;\n\nexport interface RouterOptions {\n  /**\n   *  Controls whether Fresh will append a trailing slash to the URL.\n   *  @default {false}\n   */\n  trailingSlash?: boolean;\n  /**\n   *  Configures the pattern of files to ignore in islands and routes.\n   *\n   *  By default Fresh will ignore test files,\n   *  for example files with a `.test.ts` or a `_test.ts` suffix.\n   *\n   *  @default {/(?:[^/]*_|[^/]*\\.|)test\\.(?:ts|tsx|mts|js|mjs|jsx|)\\/*$/}\n   */\n  ignoreFilePattern?: RegExp;\n  /**\n   * Serve fresh from a base path instead of from the root.\n   *   \"/foo/bar\" -> http://localhost:8000/foo/bar\n   * @default {undefined}\n   */\n  basePath?: string;\n}\n```\n\n## Build\n\n### outDir\n\nAs the comment suggests, this can be used to configure where generated files are\nwritten:\n\n```tsx dev.ts\nawait dev(import.meta.url, \"./main.ts\", {\n  build: {\n    outDir: Deno.env.get(\"FRESH_TEST_OUTDIR\") ?? undefined,\n  },\n});\n```\n\n### target\n\nThis should be a valid ES Build target.\n\n```tsx dev.ts\nawait dev(import.meta.url, \"./main.ts\", {\n  build: {\n    target: \"es2015\",\n  },\n});\n```\n\n## Plugins\n\nSee the [docs](/docs/1.x/concepts/plugins) on this topic for more detail. But as\na quick example, you can do something like this to load plugins:\n\n```ts main.ts\nawait start(manifest, { plugins: [twindPlugin(twindConfig)] });\n```\n\n## StaticDir\n\nThis allows you to specify the location where your site's static assets are\nstored. Here's an example:\n\n```ts main.ts\nawait start(manifest, { staticDir: \"./custom_static\" });\n```\n\n## Render\n\nThis is by far the most complicated option currently available. It allows you to\nconfigure how your components get rendered.\n\n## RouterOptions\n\n### TrailingSlash\n\nBy default Fresh uses URLs like `https://www.example.com/about`. If you'd like,\nyou can configure this to `https://www.example.com/about/` by using the\n`trailingSlash` setting.\n\n```ts main.ts\nawait start(manifest, { router: { trailingSlash: true } });\n```\n\n### ignoreFilePattern\n\nBy default Fresh ignores test files which are co-located next routes and\nislands. If you want, you can change the pattern Fresh uses ignore these files\n\n### basePath\n\nThis setting allows you to serve a Fresh app from sub-path of a domain. A value\nof `/foo/bar` would serve the app from `http://localhost:8000/foo/bar` instead\nof `http://localhost:8000/` for example.\n\nThe `basePath` will be automatically applied to absolute links in your app. For\nexample, when the `basePath` is `/foo/bar`, linking to `/about` will\nautomatically become `/foo/bar/about`.\n\n```tsx\n<a href=\"/about\">About</a>;\n```\n\nRendered HTML:\n\n```html\n<a href=\"/foo/bar/about\">About</a>\n```\n\nThe `basePath` is also applied to the `src` and `srcset` attribute of\n`<img>`-tags, the `href` attribute of `<link>` and the `src` attribute of\n`<script>` tags.\n\n## Server\n\nNow that Deno has stabilized\n[Deno.serve](https://docs.deno.com/api/deno/~/Deno.serve) and Fresh has switched\nto using this API, all server configuration options are embedded in `server`\ninside the `FreshConfig`. The fully expanded set of parameters looks like this:\n\n```ts\nserver: {\n  /** Server private key in PEM format */\n  cert: string;\n\n  /** Cert chain in PEM format */\n  key: string;\n\n  /** The port to listen on.\n   *\n   * @default {8000} */\n  port?: number;\n\n  /** A literal IP address or host name that can be resolved to an IP address.\n   *\n   * __Note about `0.0.0.0`__ While listening `0.0.0.0` works on all platforms,\n   * the browsers on Windows don't work with the address `0.0.0.0`.\n   * You should show the message like `server running on localhost:8080` instead of\n   * `server running on 0.0.0.0:8080` if your program supports Windows.\n   *\n   * @default {\"0.0.0.0\"} */\n  hostname?: string;\n\n  /** An {@linkcode AbortSignal} to close the server and all connections. */\n  signal?: AbortSignal;\n\n  /** Sets `SO_REUSEPORT` on POSIX systems. */\n  reusePort?: boolean;\n\n  /** The handler to invoke when route handlers throw an error. */\n  onError?: (error: unknown) => Response | Promise<Response>;\n\n  /** The callback which is called when the server starts listening. */\n  onListen?: (params: { hostname: string; port: number }) => void;\n}\n```\n\nUse these to configure your server as you see fit.\n"
  },
  {
    "path": "docs/1.x/concepts/static-files.md",
    "content": "---\ndescription: |\n  Fresh has built-in support for serving static files. This is useful for serving images, CSS, and other static assets.\n---\n\nFresh automatically serves static assets placed in a `static/` directory in the\nproject root. These assets are served at the root of the webserver, with a\nhigher priority than routes. This means that if a given request matches a file\nin the `static/` folder, it is always served, even if there is a route that\nwould also match the request.\n\nStatic asset responses automatically get a `content-type` header assigned based\non the file extension of the file on disk. Assets are also automatically\nstreamed from disk to the client to improve performance and efficiency for both\nuser and server.\n\nFresh also adds an `etag` header to assets automatically and handles the\n`If-None-Match` header for incoming requests.\n\n### Caching\n\nBy default, no caching headers are added to assets. This can be disadvantageous\nin many scenarios, so Fresh makes it easy to serve assets with long cache\nlifetimes too.\n\nThe first approach to do this is manual. The client runtime exports an `asset`\nfunction that takes an absolute path to the static asset and returns a \"locked\"\nversion of this path that contains a build ID for cache busting. When the asset\nis requested at this \"locked\" path, it will be served with a cache lifetime of\none year.\n\n```tsx routes/page.tsx\nimport { asset } from \"$fresh/runtime.ts\";\n\nexport default function Page() {\n  return (\n    <p>\n      <a href={asset(\"/brochure.pdf\")}>View brochure</a>\n    </p>\n  );\n}\n```\n\nFresh also does this automatically for `src` and `srcset` attributes in `<img>`\nand `<source>` HTML tags. These will automatically use \"locked\" paths if Fresh\ndeems it safe to do so. You can always opt out of this behaviour per tag, by\nadding the `data-fresh-disable-lock` attribute.\n\n```tsx routes/user.tsx\n{/* Locked URL source */}\n<img src=\"/user.png\" />;\n\n{/* Preserve URL source and disable lock */}\n<img src=\"/user.png\" data-fresh-disable-lock />;\n```\n"
  },
  {
    "path": "docs/1.x/concepts/updating.md",
    "content": "---\ndescription: |\n  New versions of Fresh are regularly released. This page explains how to update your project.\n---\n\nFresh consists of multiple pieces which are independently versioned and\nreleased.\n\n- Fresh (https://deno.land/x/fresh)\n- Preact (https://esm.sh/preact)\n- preact-render-to-string (https://esm.sh/preact-render-to-string)\n\nSome plugins also have their own dependencies that can be updated independently.\n\n- Twind (https://esm.sh/twind) (for the twind plugin)\n\nFor the most part these pieces can be updated independently. Certain versions of\nFresh may require a minimum version of a given dependency. This is documented\nbelow.\n\n| Fresh version | Preact            | preact-render-to-string | Deno      |\n| ------------- | ----------------- | ----------------------- | --------- |\n| 1.0.0-1.0.2   | >=10.8.1 <11.0.0  | >=5.2.0 <6.0.0          | >= 1.23.0 |\n| 1.1.0-1.1.5   | >=10.8.1 <11.0.0  | >=5.2.0 <6.0.0          | >= 1.25.0 |\n| 1.2.0         | >=10.15.0 <11.0.0 | >=6.1.0                 | >= 1.25.0 |\n\n## Updating dependencies\n\nTo update your dependencies, you have two options:\n\n- Run the Fresh updater to update your project dependencies.\n- Manually update the dependency versions in your `deno.json` file.\n\n### Auto updater\n\nThe auto updater is a command line tool that will update your project's\n`deno.json` file to the latest versions of Fresh and its dependencies. It may\nalso contain code mods for your project that will update your code to the latest\nrecommended patterns for Fresh projects.\n\nTo run the auto updater, run the following command from the root of your\nproject:\n\n```sh Terminal\n$ deno run -A -r https://fresh.deno.dev/update\n```\n\nYou will be prompted to confirm the changes that will be made to your project.\n\n### Manual update\n\nTo manually update your project's dependencies, you can edit the `deno.json`\nfile in the root of your projects directory. Dependency versions are encoded\ninto the URLs in this file. For example, here is how to update a project from\nFresh 1.0.2 to 1.1.3, and update Preact to the latest version:\n\n```diff deno.json\n  {\n    \"imports\": {\n-     \"$fresh/\": \"https://deno.land/x/fresh@1.0.2/\",\n+     \"$fresh/\": \"https://deno.land/x/fresh@1.1.5/\",\n\n-     \"preact\": \"https://esm.sh/preact@10.8.1\",\n-     \"preact/\": \"https://esm.sh/preact@10.8.1/\",\n+     \"preact\": \"https://esm.sh/preact@10.11.0\",\n+     \"preact/\": \"https://esm.sh/preact@10.11.0/\",\n\n-     \"preact-render-to-string\": \"https://esm.sh/*preact-render-to-string@5.2.0\",\n+     \"preact-render-to-string\": \"https://esm.sh/*preact-render-to-string@6.1.0\",\n\n      \"twind\": \"https://esm.sh/twind@0.16.17\",\n      \"twind/\": \"https://esm.sh/twind@0.16.17/\"\n    }\n  }\n```\n\n## Automatic update checks\n\nFresh will periodically check if a new Fresh version is available if it's\nrunning outside of CI. This happens once per day and can be disabled by setting\nthe `FRESH_NO_UPDATE_CHECK=true` environment variable.\n\n## Code mods\n\nCode mods are small scripts that can be run to update your project's code to\nmatch the latest recommended patterns for Fresh projects. Code mods can be run\nthrough the auto updater. Sometimes the code mod can not cover all cases, so you\nmay need to manually update some code. This section explains the code mods\ncurrently available.\n\n### Classical JSX -> Automatic JSX\n\n> This code mod is only available in Fresh 1.1.0 and above.\n\nThe classical JSX transform that relies on a `/** @jsx h */` pragma is no longer\nthe recommended way to use JSX in Fresh projects. Instead, starting with version\n1.1.0, Fresh projects should use the automatic JSX transform that requires no\nJSX pragma or preact import.\n\n```diff routes/hello-world.tsx\n- /** @jsx h */\n- import { h } from \"preact\";\n\n  export default function Page() {\n    return <div>Hello world!</div>;\n  }\n```\n\nThis code mod will update your deno.json file to include the relevant compiler\noptions to enable the automatic JSX transform. It will then go through your\nproject and remove any `/** @jsx h */` pragmas and `import { h } from \"preact\"`\nstatements.\n\n### Classic twind -> Twind plugin\n\n> This code mod is only available in Fresh 1.1.0 and above.\n\nFresh version 1.1.0 introduced a new plugin for using twind with Fresh. This\nplugin is much nicer to use than the raw twind integration that was previously\navailable.\n\nThis code mod will update your project to use the new twind plugin. It will\nupdate your `main.ts` file to import the twind plugin and add it to the plugins\narray. It will also update your files to remove many unnecessary uses of the\n`tw` function, and remove unnecessary twind imports. While the code mod can\nhandle most cases, you may need to manually update some code. Additionally you\nwill need to manually update your `twind.config.ts` if you use a custom\nconfiguration.\n"
  },
  {
    "path": "docs/1.x/examples/active-links.md",
    "content": "---\ndescription: |\n  Style active links with ease in Fresh\n---\n\nFresh automatically enhances the accessibility of `<a>` elements by adding the\naria-current attribute when rendering links that match the current URL. This\nattribute is recognized by assistive technologies and clearly indicates the\ncurrent page within a set of pages.\n\n- `aria-current=\"page\"` - Added to links with an exact path match, enhancing\n  accessibility by indicating the current page to assistive technologies.\n\nAs we aim to improve accessibility, we encourage the use of `aria-current` for\nstyling current links where applicable.\n\n## Styling with CSS\n\nThe `aria-current` attribute is easily styled with CSS using attribute\nselectors, providing a native way to visually differentiate the active link.\n\n```css static/styles.css\n/* Give links pointing to the current page a green color */\na[aria-current=\"page\"] {\n  color: green;\n}\n\n/* Color all ancestor links of the current page */\na[aria-current=\"true\"] {\n  color: peachpuff;\n}\n```\n\n## Tailwind / Twind\n\nIn Tailwind or similar CSS frameworks like Twind, you can apply styles to\nelements with the ﻿aria-current attribute using bracket notation in your class\ndefinitions. However, the specific syntax varies slightly between Tailwind and\nTwind. For Tailwind, use the syntax:\n\n```tsx component/Menu.tsx\nfunction Menu() {\n  return (\n    <a href=\"/foo\" class=\"aria-[current]:text-green-600\">\n      Link to some page\n    </a>\n  );\n}\n```\n\nFor Twind, the syntax is:\n\n```tsx component/Menu.tsx\nfunction Menu() {\n  return (\n    <a href=\"/foo\" class=\"[aria-current]:text-green-600\">\n      Link to some page\n    </a>\n  );\n}\n```\n\n### Twind Plugin\n\nThe original twind plugin (`import twindPlugin from \"$fresh/plugins/twind.ts\";`)\nsupports the above style:\n\n```tsx routes/page.tsx\nclass=\"[aria-current='page']:text-green-600\"\n```\n\n### TwindV1 Plugin\n\nThe new twind plugin (`import twindPlugin from \"$fresh/plugins/twindv1.ts\";`)\nrequires a slightly different syntax (note the position of the left bracket):\n\n```tsx routes/page.tsx\nclass=\"aria-[current='page']:text-green-600\"\n```\n"
  },
  {
    "path": "docs/1.x/examples/authentication-with-supabase.md",
    "content": "---\ndescription: |\n  Learn how to implement the PKCE authentication flow using Supabase.\n---\n\nFresh is a great tool for quickly building lightweight, server-side rendered web\napps and Supabase provides an easy way to add authentication (and/or a\nPostgreSQL database backend) to your app.\n\nIn this example, we'll create a small app that implements the PKCE\nauthentication flow using Supabase.\n\nThe PKCE authentication flow is designed specifically for applications that\ncannot store a client secret, such as native mobile apps or server-side rendered\nweb apps. You can read up on the specifics of PKCE\n[here](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce)\nor have a look at\n[its specification](https://datatracker.ietf.org/doc/html/rfc7636). Our example\nis based on the information you can piece together from the\n[Supabase documentation](https://supabase.com/docs/guides/auth/server-side/oauth-with-pkce-flow-for-ssr)\non the topic.\n\nThe purpose of the example app we're building here is to showcase the basic\nbuilding blocks of an implementation. As such, it is limited in functionality\nand purposefully leaves out things like\n[password resets](https://supabase.com/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr),\n[proper error handling](https://fresh.deno.dev/docs/1.x/concepts/error-pages) as\nwell as validating input form data. You can find the\n[full code here](https://github.com/morlinbrot/supa-fresh-pkce), where the\nmissing functionality is implemented.\n\n## Supabase\n\nFirst of all, we need a Supabase account\n[which can be created for free here](https://supabase.com/). A handy way to\nsupply the credentials to our app is via `.env` file (never check in `.env`\nfiles to version control).\n\n```txt .env.example\nSUPABASE_URL=https://<projectName>.supabase.co\nSUPABASE_ANON_KEY=<api_key>\n```\n\nUpdate the imports section of your `deno.json` file to include the following:\n\n```json deno.json\n\"imports\": {\n  \"supabase\": \"npm:@supabase/supabase-js@2\",\n  \"supabase/ssr\": \"npm:@supabase/ssr\",\n}\n```\n\nSince Deno 1.38, we reading .env files is built-in and can be enabled with the\n`--env` flag. Here's the complete command to run our app:\n\n```shell\ndeno run --unstable-kv --allow-env --allow-read --allow-write --allow-run --allow-net --watch=static/,routes/ dev.ts\n```\n\n### `@supabase/ssr`\n\nSupabase provides the `@supabase/ssr` package for working with its API in an SSR\ncontext. It exposes the `createServerClient` method that we can use on the\nserver side. Set it up like so:\n\n```ts lib/supabase.ts\nimport { deleteCookie, getCookies, setCookie } from \"$std/http/cookie.ts\";\nimport { assert } from \"$std/assert/assert.ts\";\nimport { type CookieOptions, createServerClient } from \"supabase/ssr\";\n\nexport function createSupabaseClient(\n  req: Request,\n  // Keep this optional parameter in mind, we'll get back to it.\n  resHeaders = new Headers(),\n) {\n  const SUPABASE_URL = Deno.env.get(\"SUPABASE_URL\");\n  const SUPABASE_ANON_KEY = Deno.env.get(\"SUPABASE_ANON_KEY\");\n\n  assert(\n    SUPABASE_URL && SUPABASE_ANON_KEY,\n    \"SUPABASE URL and SUPABASE_ANON_KEY environment variables must be set.\",\n  );\n\n  return createServerClient(SUPABASE_URL, SUPABASE_ANON_KEY, {\n    auth: { flowType: \"pkce\" },\n    cookies: {\n      get(name: string) {\n        return decodeURIComponent(getCookies(req.headers)[name]);\n      },\n      set(name: string, value: string, options: CookieOptions) {\n        setCookie(resHeaders, {\n          name,\n          value: encodeURIComponent(value),\n          ...options,\n        });\n      },\n      remove(name: string, options: CookieOptions) {\n        deleteCookie(resHeaders, name, options);\n      },\n    },\n  });\n}\n```\n\nNote: We are specifying the `flowType` to be `pkce` and that we're using\n[`encodeURIComponent()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent)\nto serialize and store the session object as a cookie.\n\nCrucially, _we need to create a new instance of this client for each request!_\n\n## Sign Up\n\nIn our endpoints, we can now use this client to talk to the Supabase API. Here's\nthe `/api/sign-up` handler:\n\n```ts routes/api/sign-up.ts\nimport { FreshContext, Handlers } from \"$fresh/server.ts\";\nimport { createSupabaseClient } from \"lib/supabase.ts\";\n\nexport const handler: Handlers = {\n  async POST(req: Request, _ctx: FreshContext) {\n    const form = await req.formData();\n    const email = form.get(\"email\");\n    const password = form.get(\"password\");\n\n    const headers = new Headers();\n    headers.set(\"location\", \"/sign-in\"); // Redirect to /sign-in on success.\n\n    const supabase = createSupabaseClient(req);\n    const { error } = await supabase.auth.signUp({\n      email: String(email),\n      password: String(password),\n    });\n\n    if (error) throw error; // Have a look at the full app for proper error handling.\n\n    return new Response(null, { status: 303, headers });\n  },\n};\n```\n\nCreate a form to call our API endpoint and render it at `/sign-up`:\n\n```tsx routes/sign-up.tsx\nexport default function SignUpPage() {\n  return (\n    <form action=\"/api/sign-up\" method=\"post\">\n      <input autofocus type=\"email\" name=\"email\" />\n      <input type=\"password\" name=\"password\" />\n      <button type=\"submit\">Submit</button>\n    </form>\n  );\n}\n```\n\n## Confirmation\n\nTo complete the sign-up process, we need a `/confirm` route to intercept\nsuccessful email confirmations:\n\n```ts routes/api/confirm.ts\nimport { Handlers } from \"$fresh/server.ts\";\nimport { createSupabaseClient } from \"lib/supabase.ts\";\n\nexport const handler: Handlers = {\n  async GET(req: Request) {\n    const { searchParams } = new URL(req.url);\n    const token_hash = searchParams.get(\"token_hash\");\n    const type = searchParams.get(\"type\") as EmailOtpType | null;\n    const next = searchParams.get(\"next\") ?? \"/welcome\";\n\n    const redirectTo = new URL(req.url);\n    redirectTo.pathname = next;\n\n    if (token_hash && type) {\n      const supabase = createSupabaseClient(req);\n      const { error } = await supabase.auth.verifyOtp({ type, token_hash });\n      if (error) throw error; // Have a look at the full app for proper error handling.\n    }\n\n    redirectTo.searchParams.delete(\"next\");\n    return Response.redirect(redirectTo);\n  },\n};\n```\n\nHave a look at the Supabase docs on the\n[details on how to configure email templates and other endpoints](https://supabase.com/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr)\nlike `/password-reset` you would need for a full implementation.\n\n## Sign In\n\nThe `/api/sign-in` route is pretty straight-forward, too:\n\n```ts routes/api/sign-in.ts\nimport { Handlers } from \"$fresh/server.ts\";\nimport { createSupabaseClient } from \"lib/supabase.ts\";\n\nexport const handler: Handlers = {\n  async POST(req) {\n    const form = await req.formData();\n    const email = form.get(\"email\")!;\n    const password = form.get(\"password\")!;\n\n    const headers = new Headers();\n    headers.set(\"location\", \"/\");\n\n    const supabase = createSupabaseClient(req, headers);\n    const { error } = await supabase.auth.signInWithPassword({\n      email,\n      password,\n    });\n\n    if (error) throw error; // Have a look at the full app for proper error handling.\n\n    return new Response(null, { status: 303, headers });\n  },\n};\n```\n\nNote: We're passing `headers` this time. The Supabase client will set the\nsession as a cookie for us, which we will want to pick up in the middleware that\nwe are writing next.\n\n## Middleware\n\nWe can now write a middleware that will check the auth status of any request,\nguarding any protected routes. You can read up on middlewares and where to put\nthem [in the docs](https://fresh.deno.dev/docs/1.x/concepts/middleware).\n\n```ts routes/_middleware.ts\nimport { FreshContext } from \"$fresh/server.ts\";\nimport { createSupabaseClient } from \"lib/supabase.ts\";\n\nexport const handler = [\n  async function authMiddleware(req: Request, ctx: FreshContext) {\n    const url = new URL(req.url);\n    const headers = new Headers();\n    headers.set(\"location\", \"/\");\n\n    const supabase = createSupabaseClient(req, headers);\n    // Note: Always use `getUser` instead of `getSession` as this calls the Supabase API and revalidates the token.\n    const { error, data: { user } } = await supabase.auth.getUser();\n\n    const isProtectedRoute = url.pathname.includes(\"secret\");\n\n    // Don't mind 401 as it just means no credentials were provided. E.g. There was no session cookie.\n    if (error && error.status !== 401) throw error; // Have a look at the full app for proper error handling.\n\n    if (isProtectedRoute && !user) {\n      return new Response(null, { status: 303, headers });\n    }\n\n    ctx.state.user = user;\n\n    return ctx.next();\n  },\n];\n```\n\nThat's it! These are the building blocks for implementing the PKCE\nauthentication flow in a Fresh app using Supabase. Again, have a look at the\n[full code here](https://github.com/morlinbrot/supa-fresh-pkce) for a fully\nfeatured version of the app.\n"
  },
  {
    "path": "docs/1.x/examples/changing-the-src-dir.md",
    "content": "---\ndescription: |\n  Change the source directory to effectively manage your project.\n---\n\nWhen you initialize a project with `deno run -A -r https://fresh.deno.dev`,\nyou'll end up with a project like the following:\n\n```txt-files Project Structure\n<project root>\n├── README.md\n├── components\n│   └── Button.tsx\n├── deno.json\n├── dev.ts\n├── fresh.gen.ts\n├── islands\n│   └── Counter.tsx\n├── main.ts\n├── routes\n│   ├── greet\n│   │   ├── [name].tsx\n│   ├── api\n│   │   └── joke.ts\n│   ├── _404.tsx\n│   └── index.tsx\n└── static\n    ├── favicon.ico\n    └── logo.svg\n```\n\n## Using a `src` directory\n\nIf you'd like your code to live in an `src` directory (or any other directory of\nyour choosing), then you'll need to do the following things:\n\n1. Move all your files, except `deno.json` and `README.md`, to the `src`\n   directory.\n2. Modify the `start` task in `deno.json` to point to the new directory.\n\nHere's what the diff of `deno.json` looks like:\n\n```diff deno.json\n {\n   \"lock\": false,\n   \"tasks\": {\n-    \"start\": \"deno run -A --watch=static/,routes/ dev.ts\"\n+    \"start\": \"deno run -A --watch=src/static/,src/routes/ src/dev.ts\"\n   },\n   \"imports\": {\n     \"$fresh/\": \"file:///Users/reed/code/fresh/\",\n```\n\nThe resulting file structure looks like this:\n\n```txt-files Project Structure\n<project root>\n├── README.md\n├── deno.json\n└── src\n    ├── components\n    │   └── Button.tsx\n    ├── dev.ts\n    ├── fresh.gen.ts\n    ├── islands\n    │   └── Counter.tsx\n    ├── main.ts\n    ├── routes\n    │   ├── greet\n    │   │   ├── [name].tsx\n    │   ├── api\n    │   │   └── joke.ts\n    │   ├── _404.tsx\n    │   └── index.tsx\n    └── static\n        ├── favicon.ico\n        └── logo.svg\n```\n\nSuccess! Your code now lives elsewhere.\n"
  },
  {
    "path": "docs/1.x/examples/client-side-components-and-libraries.md",
    "content": "---\ndescription: |\n  Client side components and libraries\n---\n\nSome components depend on client environments, browser-specific features, or\ndynamic user interactions, making them incompatible or non-functional during\nserver-side rendering.\n\nBy employing conditional rendering and state management techniques, we can\nensure graceful handling of library or data loading, improving workflow and\nusability of such components.\n\nLet's explore an example utilizing this solution with Leaflet, a popular mapping\nlibrary, in a Fresh application. The objective is to ensure the proper rendering\nof Leaflet components on the client side while gracefully handling them on the\nserver side.\n\nThe full code is available at the end of the page\n\n## Explanation\n\nThe first step is creating the context variable to enhance usability across\nvarious components within a Fresh application. By initializing these variables\nwith a null value and integrating type references, developers can streamline the\nuse of client side features while adapting to scenarios where server side\nrendering might not be feasible.\n\n> [warn]: Proper typing might not be easily available, so we might need to\n> define our own types or not use types at all.\n\n```ts context.ts\nexport const leafletContext = createContext<typeof Leaflet | null>(null);\n```\n\nThen, we should implement a Provider Component, this will handle loading and\npassing down values to be used in other components, other than that, we also\nneed to handle the server side case as well.\n\nIn this example, for the server side we are simply rendering a placeholder in\nplace of our component tree. As for the context value, we are using html tags to\ninject the library on the window and a onLoad callback to set the value of our\nstate, and this value will be handled/shared with our other components.\n\n> [warn]: Be careful with providers, the manner in which they load/inject both\n> script and css may cause issues. Leaflet, for instance, will throw errors if\n> we try to load it again.\n\n```tsx context.ts\nfunction LeafletProvider(props: { children: ComponentChildren }) {\n  if (!IS_BROWSER) {\n    return (\n      <p>Leaflet must be loaded on the client. No children will render</p>\n    );\n  }\n  const value = useSignal<typeof Leaflet | null>(null)\n  return (\n    <>\n      {/* Load Leaflet CSS */}\n      <link\n        rel=\"stylesheet\"\n        href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\"\n        integrity=\"sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=\"\n        crossorigin=\"\"\n      />\n      {/* Load Leaflet JS */}\n      <script\n        onLoad={() => value.value = window.L}\n        src=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js\"\n        integrity=\"sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=\"\n        crossorigin=\"\"\n      />\n      {/* Provide Leaflet context to children */}\n      <leafletContext.Provider value={value}>\n        {props.children}\n      </LeafletContext.Provider>\n    </>\n  );\n}\n```\n\nIn order to utilize the context, call the useContext hook with the context\nvariable this will give us access to the value set in the Provider. Handling\ncases where the context has not loaded values yet is a good practice as well, in\nthis way we can have a smooth integration and manipulation of client-side data\nand logic on our server-side code.\n\n```tsx component/Map.tsx\nfunction MapComponent() {\n  const leaf = useContext(leafletContext);\n  if (!leaf) return <p>Context not ready. Component placeholder</p>;\n  useEffect(() => {\n    const map = leaf.map(\"map\").setView(leaf.latLng(0, 0), 2);\n    leaf.tileLayer(\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\", {\n      attribution:\n        '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors',\n    }).addTo(map);\n  });\n  return <div id=\"map\" class=\"relative w-[80vw] h-[50vh]\" />;\n}\n```\n\nHere is an example island encapsulating both the provider and component in order\nto demonstrate a simple usage. In real cases, it's usually better to add the\nProvider directly to our Page and then use Components that depend on that\nprovider inside it.\n\n```tsx islands/MapIsland.tsx\nexport default function MapIsland() {\n  return (\n    <LeafletProvider>\n      <MapComponent />\n    </LeafletProvider>\n  );\n}\n```\n\n## Full code:\n\n```tsx islands/MapIsland.tsx\nimport * as Leaflet from \"https://esm.sh/v135/@types/leaflet@1.9.4/index.d.ts\";\nimport { IS_BROWSER } from \"$fresh/runtime.ts\";\nimport { useContext, useEffect } from \"preact/hooks\";\nimport { ComponentChildren, createContext } from \"preact\";\nimport { useSignal } from \"@preact/signals\";\n\n// Create a context to hold Leaflet data/functions\nconst LeafletContext = createContext<typeof Leaflet | null>(null);\n\n// LeafletProvider component manages Leaflet loading and context\nfunction LeafletProvider(props: { children: ComponentChildren }) {\n  if (!IS_BROWSER) {\n    return <p>Leaflet must be loaded on the client. No children will render</p>;\n  }\n  const value = useSignal<typeof Leaflet | null>(null);\n  return (\n    <>\n      {/* Load Leaflet CSS */}\n      <link\n        rel=\"stylesheet\"\n        href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\"\n        integrity=\"sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=\"\n        crossorigin=\"\"\n      />\n      {/* Load Leaflet JS */}\n      <script\n        onLoad={() => value.value = window.L}\n        src=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js\"\n        integrity=\"sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=\"\n        crossorigin=\"\"\n      />\n      {/* Provide Leaflet context to children */}\n      <LeafletContext.Provider value={value}>\n        {props.children}\n      </LeafletContext.Provider>\n    </>\n  );\n}\n\n// MapComponent utilizes Leaflet context for rendering the map\nfunction MapComponent() {\n  const leaf = useContext(LeafletContext);\n  if (!leaf) return <div>Component placeholder</div>;\n  useEffect(() => {\n    const map = leaf.map(\"map\").setView(leaf.latLng(0, 0), 2);\n    leaf.tileLayer(\"https://tile.openstreetmap.org/{z}/{x}/{y}.png\", {\n      attribution:\n        '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors',\n    }).addTo(map);\n  });\n  return <div id=\"map\" class=\"relative w-[80vw] h-[50vh]\" />;\n}\n\n// MapIsland is the parent component integrating LeafletProvider and MapComponent\nexport default function MapIsland() {\n  return (\n    <LeafletProvider>\n      <MapComponent />\n    </LeafletProvider>\n  );\n}\n```\n"
  },
  {
    "path": "docs/1.x/examples/creating-a-crud-api.md",
    "content": "---\ndescription: |\n  Use HTTP CRUD methods to perform operations on resources. Learn how to use HTTP handlers to create a RESTful API.\n---\n\nThe MDN [docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) are a\ngreat resource to learn more about HTTP methods. We'll touch on the four\nfundamental methods necessary to create a basic CRUD (create, read, update,\ndelete) API here. Additionally, we'll briefly mention CORS requests and how\n`OPTIONS` comes into play.\n\nUsing HTTP methods is a common way to create a REST API. Fresh supports common\nHTTP methods in handlers out of the box. Async HTTP requests are also supported.\nRead more about custom handlers [here](/docs/getting-started/custom-handlers).\n\nIn this example we'll be creating a small API that uses\n[Deno KV](https://deno.com/kv) to store users in a database.\n\nOur project structure will look like this (in addition to the rest of the Fresh\ncode from a new project):\n\n```txt-files Project Structure\n<project root>\n└── routes\n    └── api\n        └── users\n            ├── [id].ts\n            └── index.ts\n```\n\nIn each section about a method, only the relevant handler will be shown. The\nfull files are available at the bottom for reference.\n\n## POST\n\n`POST` (create) is used to create a resource.\n\n```tsx routes/api/users/index.ts\nexport const handler: Handlers<User | null> = {\n  async POST(req, _ctx) {\n    const user = (await req.json()) as User;\n    const userKey = [\"user\", user.id];\n    const ok = await kv.atomic().set(userKey, user).commit();\n    if (!ok) throw new Error(\"Something went wrong.\");\n    return new Response(JSON.stringify(user));\n  },\n};\n```\n\nTest this with Postman (or your favorite client) with a URL like\n`http://localhost:8000/api/users` and a method of `POST`. Make sure to have a\npayload like:\n\n```json Request body\n{\n  \"id\": \"2\",\n  \"name\": \"TestUserName\"\n}\n```\n\nYou should receive the same thing back:\n\n```json Response body\n{ \"id\": \"2\", \"name\": \"TestUserName\" }\n```\n\n## GET\n\n`GET` (read) is used to retrieve a resource and is by far the most common HTTP\nmethod. You can use `GET` to fetch database content, markdown, or static files.\n\n```tsx routes/api/users/[id].ts\nexport const handler: Handlers<User | null> = {\n  async GET(_req, ctx) {\n    const id = ctx.params.id;\n    const key = [\"user\", id];\n    const user = (await kv.get<User>(key)).value!;\n    return new Response(JSON.stringify(user));\n  },\n};\n```\n\nLet's practice retrieving our user! A `GET` request to\n`http://localhost:8000/api/users/2` should return:\n\n```json Response body\n{ \"id\": \"2\", \"name\": \"TestUserName\" }\n```\n\n## PUT (and PATCH)\n\n`PUT` (update) and `PATCH` are used to update a resource. While similar, there\nare differences and you should use the one that best suits your use case. Read\nmore about HTTP methods on\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods).\n\nThe short version of it: `PUT` requires the entire object to be submitted, while\n`PATCH` requires only the properties that are different to be submitted.\n\nAn example of an update endpoint using `PUT`:\n\n```tsx routes/api/users/[id].ts\nexport const handler: Handlers<User | null> = {\n  async PUT(req, ctx) {\n    const id = ctx.params.id;\n    const user = (await req.json()) as User;\n    const userKey = [\"user\", id];\n    const userRes = await kv.get(userKey);\n    if (!userRes.value) return new Response(`no user with id ${id} found`);\n    const ok = await kv.atomic().check(userRes).set(userKey, user).commit();\n    if (!ok) throw new Error(\"Something went wrong.\");\n    return new Response(JSON.stringify(user));\n  },\n};\n```\n\nTime to change their name. We'll now `PUT` a request to\n`http://localhost:8000/api/users/2` like:\n\n```json Request body\n{\n  \"id\": \"2\",\n  \"name\": \"New Name\"\n}\n```\n\nWe should receive:\n\n```json Response body\n{ \"id\": \"2\", \"name\": \"New Name\" }\n```\n\nIf, on the other hand, we chose to implement this as a `PATCH` operation, the\nrequest would just involve the changed property like this:\n\n```json Response body\n{\n  \"name\": \"New Name\"\n}\n```\n\nNo need to send in the id in this case.\n\n## DELETE\n\n`DELETE` (delete) is used to delete a resource.\n\n```tsx routes/api/users/[id].ts\nexport const handler: Handlers<User | null> = {\n  async DELETE(_req, ctx) {\n    const id = ctx.params.id;\n    const userKey = [\"user\", id];\n    const userRes = await kv.get(userKey);\n    if (!userRes.value) return new Response(`no user with id ${id} found`);\n    const ok = await kv.atomic().check(userRes).delete(userKey).commit();\n    if (!ok) throw new Error(\"Something went wrong.\");\n    return new Response(`user ${id} deleted`);\n  },\n};\n```\n\nTry sending `DELETE` to `http://localhost:8000/api/users/2` without a body.\nWe'll get back:\n\n```txt Response body\nuser 2 deleted\n```\n\n## OPTIONS\n\nOptions can be used for some advanced cases, including implementing preflight\nrequest checks for complex CORS use cases. See more on the\n[CORS documentation](/docs/1.x/examples/dealing-with-cors).\n\n## Full File Reference\n\n<details>\n<summary><code>[id].ts</code></summary>\n\n```ts routes/api/users/[id].ts\nimport { Handlers } from \"$fresh/server.ts\";\n\ntype User = {\n  id: string;\n  name: string;\n};\n\nconst kv = await Deno.openKv();\n\nexport const handler: Handlers<User | null> = {\n  async GET(_req, ctx) {\n    const id = ctx.params.id;\n    const key = [\"user\", id];\n    const user = (await kv.get<User>(key)).value!;\n    return new Response(JSON.stringify(user));\n  },\n  async DELETE(_req, ctx) {\n    const id = ctx.params.id;\n    const userKey = [\"user\", id];\n    const userRes = await kv.get(userKey);\n    if (!userRes.value) return new Response(`no user with id ${id} found`);\n    const ok = await kv.atomic().check(userRes).delete(userKey).commit();\n    if (!ok) throw new Error(\"Something went wrong.\");\n    return new Response(`user ${id} deleted`);\n  },\n  async PUT(req, ctx) {\n    const id = ctx.params.id;\n    const user = (await req.json()) as User;\n    const userKey = [\"user\", id];\n    const userRes = await kv.get(userKey);\n    if (!userRes.value) return new Response(`no user with id ${id} found`);\n    const ok = await kv.atomic().check(userRes).set(userKey, user).commit();\n    if (!ok) throw new Error(\"Something went wrong.\");\n    return new Response(JSON.stringify(user));\n  },\n};\n```\n\n</details>\n\n<details>\n<summary><code>index.ts</code></summary>\n\n```ts routes/api/users/index.ts\nimport { Handlers } from \"$fresh/server.ts\";\n\ntype User = {\n  id: string;\n  name: string;\n};\n\nconst kv = await Deno.openKv();\n\nexport const handler: Handlers<User | null> = {\n  async GET(_req, _ctx) {\n    const users = [];\n    for await (const res of kv.list({ prefix: [\"user\"] })) {\n      users.push(res.value);\n    }\n    return new Response(JSON.stringify(users));\n  },\n  async POST(req, _ctx) {\n    const user = (await req.json()) as User;\n    const userKey = [\"user\", user.id];\n    const ok = await kv.atomic().set(userKey, user).commit();\n    if (!ok) throw new Error(\"Something went wrong.\");\n    return new Response(JSON.stringify(user));\n  },\n};\n```\n\n</details>\n"
  },
  {
    "path": "docs/1.x/examples/dealing-with-cors.md",
    "content": "---\ndescription: |\n  CORS enabling routes in your Fresh project.\n---\n\nSo you've encountered some CORS problems and are on the hunt for the solution?\nYou're in the right spot.\n\nHere's a good [resource](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)\ntalking about CORS in general, in case you don't fully understand what's wrong.\n\n## Simple CORS -- Middleware\n\nAs per the above link, \"simple\" requests involve `GET`, `HEAD`, or `POST`\nrequests. You can CORS enable all the routes affected by some `middleware` by\ndoing the following:\n\n```ts routes/_middleware.ts\nimport { FreshContext } from \"$fresh/server.ts\";\n\nexport async function handler(req: Request, ctx: FreshContext) {\n  const origin = req.headers.get(\"Origin\") || \"*\";\n  const resp = await ctx.next();\n  const headers = resp.headers;\n\n  headers.set(\"Access-Control-Allow-Origin\", origin);\n  headers.set(\"Access-Control-Allow-Credentials\", \"true\");\n  headers.set(\n    \"Access-Control-Allow-Headers\",\n    \"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With\",\n  );\n  headers.set(\n    \"Access-Control-Allow-Methods\",\n    \"POST, OPTIONS, GET, PUT, DELETE\",\n  );\n\n  return resp;\n}\n```\n\n## Complex CORS -- Middleware\n\nWhat about for one of the other HTTP methods? Then you'll need to be able to\ndeal with \"preflight requests\". Let's imagine you're trying to support a\n`DELETE` route. Then you'd need to do something like this:\n\n```ts routes/_middleware.ts\nimport { FreshContext } from \"$fresh/server.ts\";\n\nexport async function handler(req: Request, ctx: FreshContext) {\n  if (req.method == \"OPTIONS\") {\n    const resp = new Response(null, {\n      status: 204,\n    });\n    const origin = req.headers.get(\"Origin\") || \"*\";\n    const headers = resp.headers;\n    headers.set(\"Access-Control-Allow-Origin\", origin);\n    headers.set(\"Access-Control-Allow-Methods\", \"DELETE\");\n    return resp;\n  }\n  const origin = req.headers.get(\"Origin\") || \"*\";\n  const resp = await ctx.next();\n  const headers = resp.headers;\n\n  headers.set(\"Access-Control-Allow-Origin\", origin);\n  headers.set(\"Access-Control-Allow-Credentials\", \"true\");\n  headers.set(\n    \"Access-Control-Allow-Headers\",\n    \"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With\",\n  );\n  headers.set(\n    \"Access-Control-Allow-Methods\",\n    \"POST, OPTIONS, GET, PUT, DELETE\",\n  );\n\n  return resp;\n}\n```\n\nThese complex results require a two step process:\n\n1. the browser makes an `OPTIONS` request to find out about the allowed methods\n2. the browser makes the actual request\n\nSo you can see the middleware has some special handling to deal with `OPTIONS`\nrequests.\n\n## CORS in Routes\n\nOf course there's no reason why you need to use middleware in order to solve\nthis. The headers can be set directly in the\n[handler](/docs/1.x/getting-started/custom-handlers) as well.\n"
  },
  {
    "path": "docs/1.x/examples/handling-complex-routes.md",
    "content": "---\ndescription: |\n  Sometimes URL based routing isn't enough.\n---\n\nThe page on [routing](/docs/1.x/concepts/routing) hints at complex routing based\non URL patterns using a `RouteConfig` object. Let's dive into this in a bit more\ndetail.\n\nA `RouteConfig` has a `routeOverride` string property, which makes use of the\n[URL Pattern API](https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API).\nHere you can define named groups, wildcards, regex groups, and other bits.\n\n## Simple Route Config\n\nLet's look at the example from the routing page more closely. We'll flesh out\nthe handler so that we end up with something like the following:\n\n```ts routes/x.tsx\nimport { FreshContext, RouteConfig } from \"$fresh/server.ts\";\n\nexport const handler = {\n  GET(_req: Request, { params }: FreshContext) {\n    console.log(params);\n    return new Response(params.path);\n  },\n};\n\nexport const config: RouteConfig = {\n  routeOverride: \"/x/:module@:version/:path*\",\n};\n```\n\nNow if we hit the server with a request like\n`http://localhost:8000/x/bestModule@1.33.7/asdf`, then logging the params will\nshow the following:\n\n```txt Console output\n{\n  module: \"bestModule\",\n  version: \"1.33.7\",\n  path: \"asdf\"\n}\n```\n\n## Complex Route Config\n\nLet's look at something a bit more complex:\n\n```ts routes/api.tsx\nimport { FreshContext, RouteConfig } from \"$fresh/server.ts\";\n\nexport const handler = {\n  GET(_req: Request, { params }: FreshContext) {\n    console.log(params);\n    return new Response(params.path);\n  },\n};\n\nexport const config: RouteConfig = {\n  routeOverride: \"/api/db/:resource(jobs?|bar)/:id(\\\\d+)?\",\n};\n```\n\nValues are available via `params.resource` and `params.id`.\n\nHere are some example URLs that match this:\n\n- `/api/db/bar/1`\n- `/api/db/jobs/1`\n- `/api/db/job/1`\n- `/api/db/job`\n- `/api/db/jobs`\n- `/api/db/bar`\n\nHere are some that don't:\n\n- `/api/db/other/123`\n- `/api/db/jobs/abc`\n- `/api/db`\n\n## Regex\n\nAt this point is should be clear that this is essentially an exercise in\nunderstanding regex. There are [numerous](https://regexr.com/)\n[resources](https://regex101.com/) [available](https://chat.openai.com/) for\ngetting assistance with regex.\n"
  },
  {
    "path": "docs/1.x/examples/index.md",
    "content": "---\ndescription: |\n  In this chapter of the Fresh documentation, you can find examples of features that you may like in your Fresh project.\n---\n\nIn this chapter of the Fresh documentation, you can find examples of features\nthat you may like in your Fresh project. If there's a specific example you'd\nlike to see here, please open\n[a GitHub discussion](https://github.com/denoland/fresh/discussions/new?category=ideas).\n\n- [Modifying the `<head>`](./examples/modifying-the-head)\n- [Setting the language](./examples/setting-the-language)\n- [Writing tests](./examples/writing-tests)\n- [Changing the source directory](./examples/changing-the-src-dir)\n- [Initializing the server](./examples/init-the-server)\n- [Using Fresh canary version](./examples/using-fresh-canary-version)\n- [Dealing with CORS](./examples/dealing-with-cors)\n- [Creating a CRUD API](./examples/creating-a-crud-api)\n- [Handling complex routes](./examples/handling-complex-routes)\n- [Rendering markdown](./examples/rendering-markdown)\n- [Sharing state between islands](./examples/sharing-state-between-islands)\n- [Using CSP](./examples/using-csp)\n"
  },
  {
    "path": "docs/1.x/examples/init-the-server.md",
    "content": "---\ndescription: |\n  For when you have some complicated setup that needs to be performed once.\n---\n\nLet's pretend you've just initialized a new Fresh project. You want to do some\ncomplicated setup that runs once, before the server is started. This is,\nfortunately, quite easy. Here's how. Modify your `fresh.config.ts` like this:\n\n```diff fresh.config.ts\n import twindConfig from \"./twind.config.ts\";\n+import { Context } from \"./routes/_middleware.ts\";\n+\n+await Context.init();\n\n export default defineConfig({\n   plugins: [twindPlugin(twindConfig)],\n```\n\nSo your full `fresh.config.ts` should look like this:\n\n```ts fresh.config.ts\nimport { defineConfig } from \"$fresh/server.ts\";\nimport twindPlugin from \"$fresh/plugins/twind.ts\";\nimport twindConfig from \"./twind.config.ts\";\nimport { Context } from \"./routes/_middleware.ts\";\n\nawait Context.init();\n\nexport default defineConfig({\n  plugins: [twindPlugin(twindConfig)],\n});\n```\n\nBut what's going on in this new `_middleware.ts` we've created?\n\n```ts routes/_middleware.ts\nimport { FreshContext } from \"$fresh/server.ts\";\n\nexport interface State {\n  context: Context;\n}\n\nexport class Context {\n  private static context: Context;\n  private complicatedStartupValue: number;\n\n  public constructor() {\n    console.log(\"i'm logged during initialization, and not during handling!\");\n    // presumably this involves connecting to a\n    // database or doing some heavy computation\n    this.complicatedStartupValue = 42;\n  }\n\n  public static async init() {\n    Context.context = new Context();\n  }\n\n  public static instance() {\n    if (this.context) return this.context;\n    else throw new Error(\"Context is not initialized!\");\n  }\n}\n\nexport async function handler(\n  _req: Request,\n  ctx: FreshContext<State>,\n) {\n  ctx.state.context = Context.instance();\n  if (ctx.destination === \"route\") {\n    console.log(\"i'm logged during a request!\");\n    console.log(ctx.state.context);\n  }\n  const resp = await ctx.next();\n  return resp;\n}\n```\n\nSo now in this `handler` (or any other `handler` functions you create) you can\nhave access to the complicated initialization step by calling\n`Context.instance()`.\n\n## Proving it out\n\n### Dev\n\nWhen you run `deno task start` you should see the following output:\n\n```txt Terminal output\nTask start deno run -A --watch=static/,routes/ dev.ts\nWatcher Process started.\ni'm logged during initialization, and not during handling!\nThe manifest has been generated for 6 routes and 1 islands.\n\n 🍋 Fresh ready\n    Local: http://localhost:8000/\n```\n\nGoing to `http://localhost:8000/` should produce:\n\n```txt Terminal output\ni'm logged during a request!\nContext { complicatedStartupValue: 42 }\n```\n\n### Build\n\nWhen you run `deno task build` you should see:\n\n```txt Terminal output\nTask build deno run -A dev.ts build\ni'm logged during initialization, and not during handling!\nThe manifest has been generated for 6 routes and 1 islands.\nAssets written to: /path/to/my/project/_fresh\n```\n\nThere's no handling of routes associated with this, but note that the\ninitialization occurred.\n\n### Preview\n\nFinally when you run `deno task preview` you should see:\n\n```txt Terminal output\nTask preview deno run -A main.ts\ni'm logged during initialization, and not during handling!\nUsing snapshot found at /Users/reed/code/temp/1763/_fresh\n\n 🍋 Fresh ready\n    Local: http://localhost:8000/\n```\n\nGoing to `http://localhost:8000/` should produce:\n\n```txt Terminal output\ni'm logged during a request!\nContext { complicatedStartupValue: 42 }\n```\n"
  },
  {
    "path": "docs/1.x/examples/migrating-to-tailwind.md",
    "content": "---\ndescription: |\n  Migrating from twind to Tailwind CSS\n---\n\nStarting with version 1.6 Fresh comes with a proper Tailwind CSS plugin out of\nthe box. When you create a new Fresh project, checking the Tailwind CSS option\nwill now install the Tailwind CSS plugin instead of twind like it did before.\n\n## Requirements before migrating\n\nThe tailwind plugin requires Fresh's\n[ahead of time builds](/docs/1.x/concepts/ahead-of-time-builds) to be set up,\notherwise it won't work. Make sure to switch your projects to ahead of time\nbuilds in your project before continuing this guide. If your project is already\nconfigured to use ahead of time builds, then you're good to go.\n\n## Migrating to Tailwind CSS\n\n1. Create a `<project>/tailwind.config.ts` file in your project folder:\n\n```ts tailwind.config.ts\nimport { type Config } from \"tailwindcss\";\n\nexport default {\n  content: [\n    \"{routes,islands,components}/**/*.{ts,tsx}\",\n  ],\n} satisfies Config;\n```\n\n2. Create a css file in your static directory `<project>/static/styles.css`:\n\n```css static/styles.css\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n```\n\n3. Add the created stylesheet in your HTML in `<project>/routes/_app.tsx`:\n\n```diff routes/_app.tsx\n  import { AppProps } from \"$fresh/server.ts\";\n  \n  export default function App({ Component }: AppProps) {\n    return (\n      <html>\n        <head>\n          <meta charset=\"utf-8\" />\n          <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n          <title>My Fresh Project</title>\n+         <link rel=\"stylesheet\" href=\"/styles.css\" />\n        </head>\n        <body>\n          <Component />\n      </body>\n      </html>\n    );\n  }\n```\n\n4. Replace the `twind` plugin with `tailwind`\n\n```diff fresh.config.ts\n  import { defineConfig } from \"$fresh/server.ts\";\n- import twind from \"$fresh/plugins/twind.ts\";\n+ import tailwind from \"$fresh/plugins/tailwind.ts\";\n\n  export default defineConfig({\n-   plugins: [twind()],\n+   plugins: [tailwind()],\n  });\n```\n\n5. Update your `deno.json` file and add the following `tailwindcss` imports. To\n   make the\n   [vscode Tailwind CSS extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)\n   work, we also need to set `\"nodeModulesDir\": \"manual\"`. This will create a\n   `node_modules` directory in your project folder when you run `deno install`.\n\n```diff deno.json\n  {\n+   \"nodeModulesDir\": \"manual\",\n    \"imports\": {\n      \"$fresh/\": \"https://deno.land/x/fresh@1.5.2/\",\n      \"preact\": \"https://esm.sh/preact@10.22.0\",\n      \"preact/\": \"https://esm.sh/preact@10.22.0/\",\n-     \"twind\": \"https://esm.sh/twind@0.16.19\",\n-     \"twind/\": \"https://esm.sh/twind@0.16.19/\"\n+     \"tailwindcss\": \"npm:tailwindcss@3.4.1\"\n    }\n  }\n```\n\n6. Add `node_modules` to your `.gitignore` or create one if the file is not\n   present in your project root directory.\n\n```diff .gitignore\n+ node_modules/\n```\n\nThat's it! Now you can use Tailwind CSS in your project.\n\n> [info]: If you're a vscode user, be sure to install the\n> [official Tailwind CSS extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)\n> to get full intellisense support. For it to work you also need to set\n> `\"nodeModulesDir\": \"manual\"` in your `deno.json`.\n\n> [warn]: Tailwind CSS doesn't support the grouping syntax from twind:\n> `text(lg uppercase gray-100)`. These need to be rewritten to their expanded\n> values like `text-lg uppercase text-gray-100`. Selecting `data-*` or `aria-*`\n> attributes works a little different with Tailwind CSS as well.\n>\n> | Twind                       | Tailwind CSS                |\n> | --------------------------- | --------------------------- |\n> | `[data-current]:bg-red-600` | `data-[current]:bg-red-300` |\n> | `[aria-current]:bg-red-600` | `aria-[current]:bg-red-300` |\n\n> [warn]: Tailwind CSS does not allow you to generate and apply CSS classes\n> dynamically, which means you need to explicitly specify the class you want to\n> apply. In other words, to use dynamic classes, you need to ensure that they\n> are present in the final CSS file.\n>\n> | Twind                               | Tailwind CSS                                                   |\n> | ----------------------------------- | -------------------------------------------------------------- |\n> | ``<a class={`link-${color}`}></a>`` | ``<a class={color === 'blue' ?`link-blue`:`link-green`}></a>`` |\n\n## Frequently Asked Questions (FAQ)\n\n### What are the differences between twind and Tailwind CSS?\n\nTwind is a project that tries to enable you to use Tailwind-like styling\ncapabilities in a single script that can also be used in the browser. The key\ndifference between the two is that twind generates CSS on the fly on every\nrequest and was shipped to the browser to make newly generated classes by\nislands work in Fresh. Overall, this wasn't an ideal setup for building\nperformant sites.\n\nIn contrast to that, Tailwind CSS extracts generates the resulting CSS file\nahead of time, which only happens once per deployment. There is no runtime\ncomponent needed, which makes your Fresh project respond faster to requests.\n\nDuring the Tailwind CSS v2 days twind pushed a lot of great ideas like allowing\nany number to be used for classes like `opacity-82` and others, but it hasn't\nkept up with recent developments of Tailwind CSS. In fact, twind has been\nunmaintained for more than a year by now. We never could get autocompletion with\ntwind to work either.\n\n### Why did Fresh use twind instead of Tailwind CSS?\n\nWhen Fresh was originally built, Deno didn't support npm modules or node APIs.\nThis meant that Tailwind CSS didn't work with Deno. Now, many years later, Deno\ndoes ship with support for both of that and we can use the same npm\n`tailwindcss` module as everyone else.\n"
  },
  {
    "path": "docs/1.x/examples/modifying-the-head.md",
    "content": "---\ndescription: |\n  Add components like <title> or <meta> to a <head> tag using Fresh's <Head> component.\n---\n\nWe can use the `<Head />` component in `$fresh/runtime.ts` to add elements as\nchildren of the `<head>` element. By adding elements as children of Fresh's\n`<Head />` tag, these automatically get injected into the `<head>` element of\nthe web page. Some uses include:\n\n- Setting the document title using `<title>`\n- Specifying page metadata using `<meta>`\n- Linking to resources like stylesheets using `<link>`\n- Including third-party JavaScript code using `<script>`\n\n```tsx routes/index.tsx\nimport { Head } from \"$fresh/runtime.ts\";\n\nexport default function Home() {\n  return (\n    <>\n      <Head>\n        <meta charset=\"UTF-8\" />\n        <title>Fresh App</title>\n        <meta\n          name=\"description\"\n          content=\"This is a brief description of Fresh\"\n        />\n        <link rel=\"stylesheet\" href=\"styles.css\" />\n        <script src=\"script.js\"></script>\n      </Head>\n      <div class=\"p-4 mx-auto max-w-screen-md\">\n        <h1>Hello World</h1>\n      </div>\n    </>\n  );\n}\n```\n\n## Avoiding duplicate tags\n\nYou might end up with duplicate tags, when multiple `<Head />` components are\nrendered on the same page. This can happen when you render `<Head />` in a route\nand another `<Head />` in another component for example.\n\n```tsx routes/page-a.tsx\n<Head>\n  <meta name=\"og:title\" content=\"This is a title\" />\n</Head>;\n```\n\n```tsx components/MyTitle.tsx\n<Head>\n  <meta name=\"og:title\" content=\"Other title\" />\n</Head>;\n```\n\nTo ensure that the tag is not duplicated, Fresh supports setting the `key` prop.\nBy giving matching elements the same `key` prop, only the last one will be\nrendered.\n\n```diff routes/page-a.tsx\n  <Head>\n-   <meta name=\"og:title\" content=\"This is a title\" />\n+   <meta name=\"og:title\" content=\"This is a title\" key=\"title\" />\n  </Head>\n```\n\n```diff components/MyTitle.tsx\n  <Head>\n-   <meta name=\"og:title\" content=\"Other title\" />\n+   <meta name=\"og:title\" content=\"Other title\" key=\"title\" />\n  </Head>\n```\n\nThe rendered page will only include the `<meta>`-tag with `\"Other title\"`.\n\n> [info]: The `<title>`-tag is automatically deduplicated, even without a `key`\n> prop.\n"
  },
  {
    "path": "docs/1.x/examples/rendering-markdown.md",
    "content": "---\ndescription: |\n  How to render markdown on your Fresh site.\n---\n\nWhat if you want to render some markdown on your site? There are a few\npossibilities:\n\n1. the markdown is coming from a remote source\n2. the markdown is defined in a string\n3. the markdown is on a file\n\nThe following file uses\n[dynamic routing](https://fresh.deno.dev/docs/getting-started/dynamic-routes) to\nhandle the three cases. It's assumed this file is called `[slug].tsx`:\n\n```ts routes/[slug].tsx\nimport { Handlers, PageProps } from \"$fresh/server.ts\";\nimport { extract } from \"$std/front_matter/yaml.ts\";\nimport { CSS, render } from \"$gfm\";\nimport { Head } from \"$fresh/runtime.ts\";\n\ninterface Page {\n  markdown: string;\n  data: Record<string, unknown>;\n}\n\nexport const handler: Handlers<Page> = {\n  async GET(_req, ctx) {\n    let rawMarkdown = \"\";\n    if (ctx.params.slug === \"remote\") {\n      const resp = await fetch(\n        `https://raw.githubusercontent.com/denoland/fresh/main/docs/latest/introduction/index.md`,\n      );\n      if (resp.status !== 200) {\n        return ctx.render(undefined);\n      }\n      rawMarkdown = await resp.text();\n    } else if (ctx.params.slug === \"string\") {\n      rawMarkdown = `---\ndescription: test\n---\n\n## big text\n\nLook, it's working. _This is in italics._\n      \n      `;\n    } else if (ctx.params.slug === \"file\") {\n      rawMarkdown = await Deno.readTextFile(\"text.md\");\n    } else {\n      return ctx.render(undefined);\n    }\n    const { attrs, body } = extract(rawMarkdown);\n    return ctx.render({ markdown: body, data: attrs });\n  },\n};\n\nexport default function MarkdownPage({ data }: PageProps<Page | null>) {\n  if (!data) {\n    return <h1>File not found.</h1>;\n  }\n\n  return (\n    <>\n      <Head>\n        <style dangerouslySetInnerHTML={{ __html: CSS }} />\n      </Head>\n      <main>\n        <div>{JSON.stringify(data.data)}</div>\n        <div\n          class=\"markdown-body\"\n          dangerouslySetInnerHTML={{ __html: render(data?.markdown) }}\n        />\n      </main>\n    </>\n  );\n}\n```\n\nThe contents of the `text.md` file are the following:\n\n```md text.md\n---\ndescription: testFromText\n---\n\n# Really Big Text\n\n**bold**\n```\n\nYou'll also need to import the `Github Flavored Markdown` module:\n\n```sh Terminal\ndeno add jsr:@deno/gfm\n```\n\nAndy has a helpful [post](https://deno.com/blog/build-a-blog-with-fresh) on the\nDeno Blog which goes into a slightly more realistic example.\n"
  },
  {
    "path": "docs/1.x/examples/rendering-raw-html.md",
    "content": "---\ndescription: |\n  How to render raw HTML in Fresh.\n---\n\nText content in Fresh is always escaped, whether serverside rendered or rendered\nin islands. While this generally desired, it can create issues in certain\nsituations.\n\n## Warning\n\nThe TL;DR is to use Preact's `dangerouslySetInnerHTML`. As the name implies, it\nshould not be used lightly.\n\nSetting arbitrary HTML can be dangerous. Make sure you trust the source.\nRendering user-supplied HTML to the DOM makes your site vulnerable to cross-\nsite scripting. The markup must first be sanitizied, or better yet, something\nyou trust.\n\n## Example: Rendering JSON-LD\n\nSuppose we need to add some microdata markup to a page. The following will\nresult in **escaped characters, and will not work**:\n\n```tsx components/json-ld.tsx\nconst json = `\n{\n  \"@context\": \"http://schema.org\",\n  \"@type\": \"PostalAddress\",\n  \"streetAddress\": \"8888 University Drive\",\n  \"addressLocality\": \"Burnaby\",\n  \"addressRegion\": \"British Columbia\"\n}\n`;\n\nexport default function JsonLd() {\n  return <script type=\"application/ld+json\">{json}</script>;\n}\n```\n\nInstead, we can use `dangerouslySetInnerHTML`:\n\n```tsx components/json-ld.tsx\nexport default function JsonLd() {\n  return (\n    <script\n      type=\"application/ld+json\"\n      dangerouslySetInnerHTML={{ __html: json }}\n    />\n  );\n}\n```\n\n## Another example: Code highlighting\n\nSyntax highlighters parse strings into HTML tags, allowing them to be\nindividually styled with CSS. We can build a simple Preact syntax highlighter\nlike so:\n\n```tsx components/code.tsx\nimport Prism from \"https://esm.sh/prismjs@1.29.0\";\n\ninterface Props {\n  code: string;\n  lang: string;\n}\n\nexport default function Code({ code, lang }: Props) {\n  const parsed = Prism.highlight(code, Prism.languages[lang], lang);\n\n  return (\n    <pre data-lang={lang} className={`language-${lang}`}>\n      <code\n        dangerouslySetInnerHTML={{\n          __html: parsed,\n        }}\n      />\n    </pre>\n  );\n}\n```\n\nOf course, we will also have to add some CSS to make this look nice.\n"
  },
  {
    "path": "docs/1.x/examples/setting-the-language.md",
    "content": "---\ndescription: |\n  Set the lang attribute in the <html> tag.\n---\n\nWhen you initialize a project with `deno run -A -r https://fresh.deno.dev`,\nyou'll end up with a `main.ts` like the following:\n\n```ts main.ts\n/// <reference no-default-lib=\"true\" />\n/// <reference lib=\"dom\" />\n/// <reference lib=\"dom.iterable\" />\n/// <reference lib=\"dom.asynciterable\" />\n/// <reference lib=\"deno.ns\" />\n\nimport { start } from \"$fresh/server.ts\";\nimport manifest from \"./fresh.gen.ts\";\n\nimport twindPlugin from \"$fresh/plugins/twind.ts\";\nimport twindConfig from \"./twind.config.ts\";\n\nawait start(manifest, { plugins: [twindPlugin(twindConfig)] });\n```\n\nThis is a great start if your site is in English, but let's say you want to\nchange the language, as per the `<html lang=asdf>` tag. Then you'll need to do\nsomething like this:\n\n```ts main.ts\n/// <reference no-default-lib=\"true\" />\n/// <reference lib=\"dom\" />\n/// <reference lib=\"dom.iterable\" />\n/// <reference lib=\"dom.asynciterable\" />\n/// <reference lib=\"deno.ns\" />\n\nimport { start } from \"$fresh/server.ts\";\nimport manifest from \"./fresh.gen.ts\";\n\nimport twindPlugin from \"$fresh/plugins/twind.ts\";\nimport twindConfig from \"./twind.config.ts\";\n\nawait start(manifest, {\n  plugins: [twindPlugin(twindConfig)],\n  render: (ctx, render) => {\n    ctx.lang = \"de\";\n    render();\n  },\n});\n```\n\nIf you're curious how this works, start by checking out `TemplateOptions` in\n`render.ts`.\n"
  },
  {
    "path": "docs/1.x/examples/sharing-state-between-islands.md",
    "content": "---\ndescription: |\n  When you need to have state shared between islands, this page provides a few recipes.\n---\n\nAll of this content is lifted from this great\n[example](https://fresh-with-signals.deno.dev/) by Luca. The source can be found\n[here](https://github.com/lucacasonato/fresh-with-signals).\n\n## Multiple Sibling Islands with Independent State\n\nImagine we have `Counter.tsx` like this:\n\n```tsx islands/Counter.tsx\nimport { useSignal } from \"@preact/signals\";\nimport { Button } from \"../components/Button.tsx\";\n\ninterface CounterProps {\n  start: number;\n}\n\n// This island is used to display a counter and increment/decrement it. The\n// state for the counter is stored locally in this island.\nexport default function Counter(props: CounterProps) {\n  const count = useSignal(props.start);\n  return (\n    <div class=\"flex gap-2 items-center w-full\">\n      <p class=\"flex-grow-1 font-bold text-xl\">{count}</p>\n      <Button onClick={() => count.value--}>-1</Button>\n      <Button onClick={() => count.value++}>+1</Button>\n    </div>\n  );\n}\n```\n\nNote how `useSignal` is within the `Counter` component. Then if we instantiate\nsome counters like this...\n\n```tsx routes/index.tsx\n<Counter start={3} />\n<Counter start={4} />\n```\n\nthey'll keep track of their own independent state. Not much sharing going on\nhere, yet.\n\n## Multiple Sibling Islands with Shared State\n\nBut we can switch things up by looking at a `SynchronizedSlider.tsx` like this:\n\n```tsx islands/SynchronizedSlider.tsx\nimport { Signal } from \"@preact/signals\";\n\ninterface SliderProps {\n  slider: Signal<number>;\n}\n\n// This island displays a slider with a value equal to the `slider` signal's\n// value. When the slider is moved, the `slider` signal is updated.\nexport default function SynchronizedSlider(props: SliderProps) {\n  return (\n    <input\n      class=\"w-full\"\n      type=\"range\"\n      min={1}\n      max={100}\n      value={props.slider.value}\n      onInput={(e) => (props.slider.value = Number(e.currentTarget.value))}\n    />\n  );\n}\n```\n\nNow if we were to do the following...\n\n```tsx routes/index.tsx\nexport default function Home() {\n  const sliderSignal = useSignal(50);\n  return (\n    <div>\n      <SynchronizedSlider slider={sliderSignal} />\n      <SynchronizedSlider slider={sliderSignal} />\n      <SynchronizedSlider slider={sliderSignal} />\n    </div>\n  );\n}\n```\n\nthey would all use the same value.\n\n## Independent Islands\n\nWe can also create a `signal` in a utility file and export it for consumption\nacross multiple places.\n\n```ts utils/cart.ts\nimport { signal } from \"@preact/signals\";\n\nexport const cart = signal<string[]>([]);\n```\n\n```tsx islands/AddToCart.tsx\nimport { Button } from \"../components/Button.tsx\";\nimport { cart } from \"../utils/cart.ts\";\n\ninterface AddToCartProps {\n  product: string;\n}\n\n// This island is used to add a product to the cart state.\nexport default function AddToCart(props: AddToCartProps) {\n  return (\n    <Button\n      onClick={() => (cart.value = [...cart.value, props.product])}\n      class=\"w-full\"\n    >\n      Add{cart.value.includes(props.product) ? \" another\" : \"\"} \"{props.product}\n      \" to cart\n    </Button>\n  );\n}\n```\n\n```tsx islands/Cart.tsx\nimport { Button } from \"../components/Button.tsx\";\nimport { cart } from \"../utils/cart.ts\";\nimport * as icons from \"../components/Icons.tsx\";\n\n// This island is used to display the cart contents and remove items from it.\nexport default function Cart() {\n  return (\n    <h1 class=\"text-xl flex items-center justify-center\">\n      Cart\n    </h1>\n\n    <ul class=\"w-full bg-gray-50 mt-2 p-2 rounded-sm min-h-[6.5rem]\">\n      {cart.value.length === 0 && (\n        <li class=\"text-center my-4\">\n          <div class=\"text-gray-400\">\n            <icons.Cart class=\"w-8 h-8 inline-block\" />\n            <div>\n              Your cart is empty.\n            </div>\n          </div>\n        </li>\n      )}\n      {cart.value.map((product, index) => (\n        <CartItem product={product} index={index} />\n      ))}\n    </ul>\n  );\n}\n\ninterface CartItemProps {\n  product: string;\n  index: number;\n}\n\nfunction CartItem(props: CartItemProps) {\n  const remove = () => {\n    const newCart = [...cart.value];\n    newCart.splice(props.index, 1);\n    cart.value = newCart;\n  };\n\n  return (\n    <li class=\"flex items-center justify-between gap-1\">\n      <icons.Lemon class=\"text-gray-500\" />\n      <div class=\"flex-1\">\n        {props.product}\n      </div>\n      <Button onClick={remove} aria-label=\"Remove\" class=\"border-none\">\n        <icons.X class=\"inline-block w-4 h-4\" />\n      </Button>\n    </li>\n  );\n}\n```\n\nNow we can add the islands to our site by doing the following:\n\n```tsx routes/cart.tsx\n<AddToCart product=\"Lemon\" />\n<AddToCart product=\"Lime\" />\n<Cart />\n```\n\nWhat happens as a result? The `cart` signal is shared across the two `AddToCart`\nislands _and_ the `Cart` island.\n"
  },
  {
    "path": "docs/1.x/examples/using-csp.md",
    "content": "---\ndescription: |\n  Change the source directory to effectively manage your project.\n---\n\nAs per the\n[MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP):\n\n> Content Security Policy (CSP) is an added layer of security that helps to\n> detect and mitigate certain types of attacks, including Cross-Site Scripting\n> (XSS) and data injection attacks. These attacks are used for everything from\n> data theft, to site defacement, to malware distribution.\n>\n> To enable CSP, you need to configure your web server to return the\n> Content-Security-Policy HTTP header. (Sometimes you may see mentions of the\n> X-Content-Security-Policy header, but that's an older version and you don't\n> need to specify it anymore.)\n\nFortunately Fresh has built in support for CSP. We don't need to worry about\nsetting headers ourselves. We just have to configure our routes correctly. Let's\ndive into a few examples to see how this works.\n\nFresh's CSP implementation supports the following\n\n<details>\n<summary>directives</summary>\n\n```ts fresh 🍋\nexport interface ContentSecurityPolicyDirectives {\n  // Fetch directives\n  /**\n   * Defines the valid sources for web workers and nested browsing contexts\n   * loaded using elements such as <frame> and <iframe>.\n   */\n  childSrc?: string[];\n  /**\n   * Restricts the URLs which can be loaded using script interfaces.\n   */\n  connectSrc?: string[];\n  /**\n   * Serves as a fallback for the other fetch directives.\n   */\n  defaultSrc?: string[];\n  /**\n   * Specifies valid sources for fonts loaded using @font-face.\n   */\n  fontSrc?: string[];\n  /**\n   * Specifies valid sources for nested browsing contexts loading using elements\n   * such as <frame> and <iframe>.\n   */\n  frameSrc?: string[];\n  /**\n   * Specifies valid sources of images and favicons.\n   */\n  imgSrc?: string[];\n  /**\n   * Specifies valid sources of application manifest files.\n   */\n  manifestSrc?: string[];\n  /**\n   * Specifies valid sources for loading media using the <audio> , <video> and\n   * <track> elements.\n   */\n  mediaSrc?: string[];\n  /**\n   * Specifies valid sources for the <object>, <embed>, and <applet> elements.\n   */\n  objectSrc?: string[];\n  /**\n   * Specifies valid sources to be prefetched or prerendered.\n   */\n  prefetchSrc?: string[];\n  /**\n   * Specifies valid sources for JavaScript.\n   */\n  scriptSrc?: string[];\n  /**\n   * Specifies valid sources for JavaScript <script> elements.\n   */\n  scriptSrcElem?: string[];\n  /**\n   * Specifies valid sources for JavaScript inline event handlers.\n   */\n  scriptSrcAttr?: string[];\n  /**\n   * Specifies valid sources for stylesheets.\n   */\n  styleSrc?: string[];\n  /**\n   * Specifies valid sources for stylesheets <style> elements and <link>\n   * elements with rel=\"stylesheet\".\n   */\n  styleSrcElem?: string[];\n  /**\n   * Specifies valid sources for inline styles applied to individual DOM\n   * elements.\n   */\n  styleSrcAttr?: string[];\n  /**\n   * Specifies valid sources for Worker, SharedWorker, or ServiceWorker scripts.\n   */\n  workerSrc?: string[];\n\n  // Document directives\n  /**\n   * Restricts the URLs which can be used in a document's <base> element.\n   */\n  baseUri?: string[];\n  /**\n   * Enables a sandbox for the requested resource similar to the <iframe>\n   * sandbox attribute.\n   */\n  sandbox?: string[];\n\n  // Navigation directives\n  /**\n   * Restricts the URLs which can be used as the target of a form submissions\n   * from a given context.\n   */\n  formAction?: string[];\n  /**\n   * Specifies valid parents that may embed a page using <frame>, <iframe>,\n   * <object>, <embed>, or <applet>.\n   */\n  frameAncestors?: string[];\n  /**\n   * Restricts the URLs to which a document can initiate navigation by any\n   * means, including <form> (if form-action is not specified), <a>,\n   * window.location, window.open, etc.\n   */\n  navigateTo?: string[];\n\n  /**\n   * The URI to report CSP violations to.\n   */\n  reportUri?: string;\n}\n```\n\n</details>\n\nFor our examples, we'll just be focused on `styleSrc`, but the technique can be\napplied to any of the directives.\n\nWe'll start off by having an example stylesheet defined like this:\n\n```css static/example.css\nh1 {\n  font-size: 25px;\n  font-weight: normal;\n  margin-top: 5px;\n  margin-left: 25px;\n}\n```\n\n## No CSP\n\nTo kick things off, we'll create the following control route which doesn't do\nanything with CSP. We include a stylesheet to confirm that our sheet correctly\nstyles the response.\n\n```tsx routes/noCSP.tsx\nimport { RouteContext } from \"$fresh/server.ts\";\n\nexport default function Home(req: Request, ctx: RouteContext) {\n  return (\n    <>\n      <h1>This page doesn't use CSP at all. Styles will be applied.</h1>\n      <link rel=\"stylesheet\" type=\"text/css\" href=\"example.css\" />\n    </>\n  );\n}\n```\n\nWe can hit `http://localhost:8000/noCSP` and we should see the following:\n\n```txt Response body\nThis page doesn't use CSP at all. Styles will be applied.\n```\n\n## Incorrect CSP\n\nLet's invoke the `useCSP` hook in our response to try to secure our page. Watch\nclosely, we're using the wrong URL! This will cause the browser to reject the\nstylesheet, due to the header that Fresh produces. We get a `(blocked:csp)`\nstatus when the browser tries to request this resource.\n\n```tsx routes/incorrectCSP.tsx\nimport { RouteConfig, RouteContext } from \"$fresh/server.ts\";\nimport { useCSP } from \"$fresh/runtime.ts\";\n\nexport default function Home(req: Request, ctx: RouteContext) {\n  useCSP((csp) => {\n    if (!csp.directives.styleSrc) {\n      csp.directives.styleSrc = [];\n    }\n    csp.directives.styleSrc.push(\"http://www.example.com\");\n  });\n  return (\n    <>\n      <h1>This page violates our configured CSP. Styles won't be applied.</h1>\n      <link rel=\"stylesheet\" type=\"text/css\" href=\"example.css\" />\n    </>\n  );\n}\n\nexport const config: RouteConfig = {\n  csp: true,\n};\n```\n\nWe can hit `http://localhost:8000/incorrectCSP` and we should see the following:\n\n```txt Response body\nThis page violates our configured CSP. Styles won't be applied.\n```\n\n## Correct CSP\n\nLet's fix our simple mistake and use the correct URL. Everything is working\ncorrectly here.\n\n```tsx routes/correctCSP.tsx\nimport { RouteConfig, RouteContext } from \"$fresh/server.ts\";\nimport { useCSP } from \"$fresh/runtime.ts\";\n\nexport default function Home(req: Request, ctx: RouteContext) {\n  useCSP((csp) => {\n    if (!csp.directives.styleSrc) {\n      csp.directives.styleSrc = [];\n    }\n    csp.directives.styleSrc.push(\"http://localhost:8000/example.css\");\n  });\n  return (\n    <>\n      <h1>This page adheres to our configured CSP. Styles will be applied.</h1>\n      <link rel=\"stylesheet\" type=\"text/css\" href=\"example.css\" />\n    </>\n  );\n}\n\nexport const config: RouteConfig = {\n  csp: true,\n};\n```\n\nWe can hit `http://localhost:8000/correctCSP` and we should see the following:\n\n```txt Response body\nThis page adheres to our configured CSP. Styles will be applied.\n```\n\n## No Route Config\n\nWhat happens if we forget to use a `RouteConfig` in our route?\n\n```tsx routes/cspNoRouteConfig.tsx\nimport { RouteContext } from \"$fresh/server.ts\";\nimport { useCSP } from \"$fresh/runtime.ts\";\n\nexport default function Home(req: Request, ctx: RouteContext) {\n  useCSP((csp) => {\n    if (!csp.directives.styleSrc) {\n      csp.directives.styleSrc = [];\n    }\n    csp.directives.styleSrc.push(\"http://www.example.com\");\n  });\n  return (\n    <>\n      <h1>\n        This page violates our configured CSP. But we don't have a{\" \"}\n        <code>RouteConfig</code>{\" \"}\n        enabled, so Fresh doesn't know to use the CSP. Styles will be applied.\n      </h1>\n      <link rel=\"stylesheet\" type=\"text/css\" href=\"example.css\" />\n    </>\n  );\n}\n```\n\nWe can hit `http://localhost:8000/cspNoRouteConfig` and we should see the\nfollowing:\n\n```txt Response body\nThis page violates our configured CSP. But we don't have a RouteConfig enabled, so Fresh doesn't know to use the CSP. Styles will be applied.\n```\n\n## Reporting\n\nLet's touch on the reporting aspect of CSP. CSP (and Fresh's framework) support\na `reportOnly` flag and a `reportUri` endpoint. This is a destination that\nshould be able to receive `POST` requests. If the `reportOnly` flag is enabled,\nthen the browser will ignore the CSP headers and log any issues to the\n`reportUri` destination.\n\n```tsx routes/incorrectCSPwithReport.tsx\nimport { RouteConfig, RouteContext } from \"$fresh/server.ts\";\nimport { useCSP } from \"$fresh/runtime.ts\";\n\nexport default function Home(req: Request, ctx: RouteContext) {\n  useCSP((csp) => {\n    csp.reportOnly = true;\n    if (!csp.directives.styleSrc) {\n      csp.directives.styleSrc = [];\n    }\n    csp.directives.reportUri = \"http://localhost:8000/reportHandler\";\n    csp.directives.styleSrc.push(\"http://www.example.com\");\n  });\n  return (\n    <>\n      <h1>\n        This page violates our configured CSP. But we're using \"reportOnly\".\n        Styles will be applied.\n      </h1>\n      <link rel=\"stylesheet\" type=\"text/css\" href=\"example.css\" />\n    </>\n  );\n}\n\nexport const config: RouteConfig = {\n  csp: true,\n};\n```\n\n```ts routes/reportHandler.ts\nimport { FreshContext } from \"$fresh/server.ts\";\n\nexport const handler = {\n  async POST(req: Request, _ctx: FreshContext) {\n    const body = await req.json();\n    const report = JSON.stringify(body, null, 2);\n\n    await Deno.writeTextFile(\"./csp-reports.txt\", report + \"\\n\", {\n      append: true,\n    });\n    return new Response(null, { status: 200 });\n  },\n};\n```\n\nWe can hit `http://localhost:8000/incorrectCSPwithReport` and we should see the\nfollowing:\n\n```txt Response body\nThis page violates our configured CSP. But we're using \"reportOnly\". Styles will be applied.\n```\n\nWe can then check our server and we'll see that `csp-reports.txt` has an entry\nlike this:\n\n```json csp-reports.txt\n{\n  \"csp-report\": {\n    \"document-uri\": \"http://localhost:8000/incorrectCSPwithReport\",\n    \"referrer\": \"http://localhost:8000/incorrectCSPwithReport\",\n    \"violated-directive\": \"style-src-elem\",\n    \"effective-directive\": \"style-src-elem\",\n    \"original-policy\": \"default-src 'none'; style-src 'unsafe-inline' http://www.example.com; report-uri http://localhost:8000/reportHandler; script-src 'nonce-0f2d8259315d40479e8c21979128ac0d'; connect-src 'self'\",\n    \"disposition\": \"report\",\n    \"blocked-uri\": \"http://localhost:8000/example.css\",\n    \"line-number\": 37,\n    \"source-file\": \"http://localhost:8000/incorrectCSPwithReport\",\n    \"status-code\": 200,\n    \"script-sample\": \"\"\n  }\n}\n```\n"
  },
  {
    "path": "docs/1.x/examples/using-fresh-canary-version.md",
    "content": "---\ndescription: |\n  For cases where the latest release doesn't fit your needs.\n---\n\nPretend you have a use case where you need to modify your project to use a\ncanary version of Fresh. Or you want to use a slightly different initialization\nscript. This page has you covered.\n\n## Canary Fresh in `deno.json`\n\n### Latest alpha version\n\nThe easiest way to use Fresh 2 canary is with the update command:\n\n```sh Terminal\ndeno run -A -r jsr:@fresh/update@2.0.0-alpha.35 .\n```\n\nThis will automatically update your `deno.json` to use the specified canary\nversion.\n\n### Specific commit\n\nIf you need a particular commit (for testing specific fixes or features):\n\n```diff deno.json\n   \"tasks\": {\n     \"update\": \"deno run -A -r jsr:@fresh/update .\"\n   },\n   \"imports\": {\n-    \"$fresh/\": \"jsr:@fresh/core@^2.0.0\",\n+    \"$fresh/\": \"https://raw.githubusercontent.com/denoland/fresh/your-commit-hash/\",\n     \"preact\": \"npm:preact@^10.26.9\",\n     \"@preact/signals\": \"npm:@preact/signals@^2.2.0\"\n   }\n```\n\nReplace `your-commit-hash` with your desired commit hash.\n\n### Forked Fresh\n\nFor testing your own fork or PR:\n\n```diff deno.json\n   \"tasks\": {\n     \"update\": \"deno run -A -r jsr:@fresh/update .\"\n   },\n   \"imports\": {\n-    \"$fresh/\": \"https://deno.land/x/fresh@1.7.3/\",\n+    \"$fresh/\": \"https://raw.githubusercontent.com/your-username/fresh/your-branch/\",\n     \"preact\": \"https://esm.sh/preact@10.26.9\",\n     \"preact/\": \"https://esm.sh/preact@10.22.0/\",\n   }\n```\n\n## Creating a new project\n\n### Using JSR\n\n```sh Terminal\ndeno run -A -r jsr:@fresh/init@2.0.0-alpha.35\n```\n\n### From local source\n\nIf you're developing Fresh itself:\n\n```sh Terminal\ndeno run -A -r ./init/src/init.ts\n```\n\n### Recommended reading\n\n- [Fresh v2 blog](https://deno.com/blog/fresh-2)\n- [Migration guide](../../migration-guide.md)\n"
  },
  {
    "path": "docs/1.x/examples/using-twind-v1.md",
    "content": "---\ndescription: |\n  With a few tweaks one can use twind v1\n---\n\nWhen you initialize a project with `deno run -A -r https://fresh.deno.dev`,\nyou'll end up with a `main.ts` like the following:\n\n```ts main.ts\n/// <reference no-default-lib=\"true\" />\n/// <reference lib=\"dom\" />\n/// <reference lib=\"dom.iterable\" />\n/// <reference lib=\"dom.asynciterable\" />\n/// <reference lib=\"deno.ns\" />\n\nimport \"$std/dotenv/load.ts\";\n\nimport { start } from \"$fresh/server.ts\";\nimport manifest from \"./fresh.gen.ts\";\nimport config from \"./fresh.config.ts\";\n\nawait start(manifest, config);\n```\n\nAnd the Fresh config is like this:\n\n```ts fresh.config.ts\nimport { defineConfig } from \"$fresh/server.ts\";\nimport twindPlugin from \"$fresh/plugins/twind.ts\";\nimport twindConfig from \"./twind.config.ts\";\n\nexport default defineConfig({\n  plugins: [twindPlugin(twindConfig)],\n});\n```\n\nLet's bump that up to v1:\n\n```diff fresh.config.ts\n import { defineConfig } from \"$fresh/server.ts\";\n-import twindPlugin from \"$fresh/plugins/twind.ts\";\n+import twindPlugin from \"$fresh/plugins/twindv1.ts\";\n import twindConfig from \"./twind.config.ts\";\n\n export default defineConfig({\n```\n\nThe twind config object has changed significantly in v1, so we must also change\n`twind.config.ts`. A good base looks like this (just replace whatever is there\nwith this):\n\n```ts twind.config.ts\nimport { defineConfig, Preset } from \"https://esm.sh/@twind/core@1.1.3\";\nimport presetTailwind from \"https://esm.sh/@twind/preset-tailwind@1.1.4\";\nimport presetAutoprefix from \"https://esm.sh/@twind/preset-autoprefix@1.0.7\";\n\nexport default {\n  ...defineConfig({\n    presets: [presetTailwind() as Preset, presetAutoprefix()],\n  }),\n  selfURL: import.meta.url,\n};\n```\n\n(Note: the `as Preset` cast is required to fix a typing issue with twind.)\n\nTo see what other presets exist, you can go to the\n[twind docs](https://twind.style/presets).\n"
  },
  {
    "path": "docs/1.x/examples/writing-tests.md",
    "content": "---\ndescription: |\n  You can write HTTP tests for your Fresh project by creating an application handler.\n---\n\nYou can write tests for your Fresh project by creating an application handler\nthrough\n[`createHandler()`](https://deno.land/x/fresh/server.ts?doc=&s=createHandler).\n\n## 1. Create your routes\n\n```tsx routes/index.tsx\nimport { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async POST(req) {\n    const form = await req.formData();\n\n    // Processing something\n\n    return new Response(null, {\n      status: 303,\n      headers: { location: \"/\" },\n    });\n  },\n};\n\nexport default function HomePage() {\n  return <div>Hello Deno!</div>;\n}\n```\n\n```tsx routes/foo.tsx\nexport default function FooPage() {\n  return <div>Hello Foo!</div>;\n}\n```\n\n## 2. Write your tests\n\n```ts tests/main_test.ts\nimport { createHandler, ServeHandlerInfo } from \"$fresh/server.ts\";\nimport manifest from \"../fresh.gen.ts\";\nimport config from \"../fresh.config.ts\";\nimport { assert, assertEquals } from \"$std/testing/asserts.ts\";\n\nconst CONN_INFO: ServeHandlerInfo = {\n  remoteAddr: { hostname: \"127.0.0.1\", port: 53496, transport: \"tcp\" },\n};\n\nDeno.test(\"HTTP assert test.\", async (t) => {\n  const handler = await createHandler(manifest, config);\n\n  await t.step(\"#1 GET /\", async () => {\n    const resp = await handler(new Request(\"http://127.0.0.1/\"), CONN_INFO);\n    assertEquals(resp.status, 200);\n  });\n\n  await t.step(\"#2 POST /\", async () => {\n    const formData = new FormData();\n    formData.append(\"text\", \"Deno!\");\n    const req = new Request(\"http://127.0.0.1/\", {\n      method: \"POST\",\n      body: formData,\n    });\n    const resp = await handler(req, CONN_INFO);\n    assertEquals(resp.status, 303);\n  });\n\n  await t.step(\"#3 GET /foo\", async () => {\n    const resp = await handler(new Request(\"http://127.0.0.1/foo\"), CONN_INFO);\n    const text = await resp.text();\n    assert(text.includes(\"<div>Hello Foo!</div>\"));\n  });\n});\n```\n\n## 3. Run the tests\n\n```sh Terminal\n$ deno test --allow-read --allow-env --allow-net\nrunning 1 test from ./tests/main_test.ts\nHTTP assert test. ...\n  #1 GET / ... ok (31ms)\n  #2 POST / ... ok (35ms)\n  #3 GET /foo ... ok (12ms)\nHTTP assert test. ... ok (118ms)\n\nok | 1 passed (3 steps) | 0 failed (236ms)\n```\n\n## createHandler in detail\n\nThis function is typed as follows:\n\n```ts fresh 🍋\nexport async function createHandler(\n  manifest: Manifest,\n  config: FreshConfig = {},\n): Promise<\n  (req: Request, connInfo?: ServeHandlerInfo) => Promise<Response>\n```\n\nWhen you're using it, you'll likely be importing the manifest from your project.\nYou can of course import the config (`fresh.config.ts`) as well, but you're also\nfree to provide your own bag of options.\n[`FreshConfig`](https://deno.land/x/fresh/server.ts?s=FreshConfig) is declared\nas follows:\n\n```ts fresh 🍋\nexport interface FreshConfig {\n  build?: {\n    outDir?: string;\n    target?: string | string[];\n  };\n  render?: RenderFunction;\n  plugins?: Plugin[];\n  staticDir?: string;\n  router?: RouterOptions;\n  server?: Partial<Deno.ServeTlsOptions>;\n}\n```\n\nFor more on how these work, see the page about\n[server configuration](/docs/1.x/concepts/server-configuration).\n"
  },
  {
    "path": "docs/1.x/getting-started/adding-interactivity.md",
    "content": "---\ndescription: |\n  Add JavaScript based interactivity to your project without sacrificing user\n  experience, by using Fresh's powerful islands system.\n---\n\nUp to now none of the pages in the demo project have contained any client side\nJavaScript. This is great for resiliency and performance, but it can also limit\nthe possibilities of interactivity. In many current generation web frameworks,\nyou get the choice of shipping no JavaScript to the client or shipping a\nrenderer for the entire page.\n\nThis is not very flexible, especially considering that most pages will only have\nsmall pieces of content that require interactivity. For example, an otherwise\nstatic page might need a little bit of JavaScript to power an image carousel or\n\"buy now\" button. This model is often called\n[islands architecture][islands-architecture]. This refers to a page having\nlittle \"islands\" of interactivity, in a sea of otherwise static content.\n\nFresh embraces this model. All pages are rendered server side, but you can\ncreate \"island components\" that are _also_ rendered client side. To do this,\nFresh projects have a special `islands/` folder. The modules in this folder each\nencapsulate a single island component. The name of the module should be the\n[pascal case][pascal-case] or [kebab case][kebab-case] name of the island\ncomponent. For example a counter component would be defined in the file\n`islands/Counter.tsx`. A buy now button could be defined in the file\n`islands/buy-now-button.tsx`.\n\nHere is an example of an island component that counts down to a specific time.\n\n```tsx islands/Countdown.tsx\nimport { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nconst timeFmt = new Intl.RelativeTimeFormat(\"en-US\");\n\n// The target date is passed as a string instead of as a `Date`, because the\n// props to island components need to be JSON (de)serializable.\nexport default function Countdown(props: { target: string }) {\n  const target = new Date(props.target);\n  const now = useSignal(new Date());\n\n  // Set up an interval to update the `now` date every second with the current\n  // date as long as the component is mounted.\n  useEffect(() => {\n    const timer = setInterval(() => {\n      if (now.value > target) {\n        clearInterval(timer);\n      }\n      now.value = new Date();\n    }, 1000);\n    return () => clearInterval(timer);\n  }, [props.target]);\n\n  const secondsLeft = Math.floor(\n    (target.getTime() - now.value.getTime()) / 1000,\n  );\n\n  // If the target date has passed, we stop counting down.\n  if (secondsLeft <= 0) {\n    return <span>🎉</span>;\n  }\n\n  // Otherwise, we format the remaining time using `Intl.RelativeTimeFormat` and\n  // render it.\n  return <span>{timeFmt.format(secondsLeft, \"seconds\")}</span>;\n}\n```\n\nTo include this in a page component, one can just use the component normally.\nFresh will take care of automatically mounting the island component on the\nclient with the correct props:\n\n```tsx routes/countdown.tsx\nimport Countdown from \"../islands/Countdown.tsx\";\n\nexport default function Page() {\n  const date = new Date();\n  date.setHours(date.getHours() + 1);\n  return (\n    <p>\n      The big event is happening <Countdown target={date.toISOString()} />.\n    </p>\n  );\n}\n```\n\nThe page that is rendered on the client now has an interactive countdown.\n\n[islands-architecture]: https://jasonformat.com/islands-architecture\n[pascal-case]: https://en.wiktionary.org/wiki/Pascal_case\n[kebab-case]: https://en.wiktionary.org/wiki/kebab_case\n"
  },
  {
    "path": "docs/1.x/getting-started/create-a-project.md",
    "content": "---\ndescription: |\n  Create a new Fresh project by running the Fresh project creation tool. This\n  scaffolds out the various files and folders a Fresh project needs.\n---\n\nNew Fresh projects can be created by using the Fresh project creation tool. It\nwill scaffold out a new project with some example files to get you started.\n\nTo create a new project, run:\n\n```sh Terminal\ndeno run -A -r https://fresh.deno.dev\ncd fresh-project\ndeno task start\n```\n\nThis will scaffold out the new project, then switch into the newly created\ndirectory, and then start the development server.\n\nThis will create a directory containing some files and directories. There are 4\nfiles that are strictly necessary to run a Fresh project:\n\n- **`dev.ts`**: This is the development entry point for your project. This is\n  the file that you run to start your project. This file doesn't need to be\n  called `dev.ts`, but this is the convention.\n- **`main.ts`**: This is the production entry point for your project. It is the\n  file that you link to Deno Deploy. This file doesn't actually need to be\n  `main.ts`, but this is the convention.\n- **`fresh.gen.ts`**: This is the manifest file that contains information about\n  your routes and islands. This file is automatically generated in development\n  based on your `routes/` and `islands/` folders.\n\nA **`deno.json`** file is also created in the project directory. This file does\ntwo things:\n\n- It defines the \"imports\" field. This is an [import map][import-map] that is\n  used to manage dependencies for the project. This allows for easy importing\n  and updating of dependencies.\n- It registers a \"start\" [task][task-runner] to run the project without having\n  to type a long `deno run` command.\n\nTwo important folders are also created that contain your routes and islands\nrespectively:\n\n- **`routes/`**: This folder contains all of the routes in your project. The\n  name of each file in this folder corresponds to the path where that page will\n  be accessed. Code inside of this folder is never directly shipped to the\n  client. You'll learn more about how routes work in the next section.\n- **`islands/`**: This folder contains all of the interactive islands in your\n  project. The name of each file corresponds to the name of the island defined\n  in that file. Code inside of this folder can be run from both client and\n  server. You'll learn more about islands later in this chapter.\n\nFinally a **`static/`** folder is created that contains static files that are\nautomatically served \"as is\". [Learn more about static files][static-files].\n\n[import-map]: https://docs.deno.com/runtime/fundamentals/modules\n[task-runner]: https://docs.deno.com/runtime/reference/cli/task\n[static-files]: ../concepts/static-files\n"
  },
  {
    "path": "docs/1.x/getting-started/create-a-route.md",
    "content": "---\ndescription: |\n  Create a new route to a Fresh project by creating a new file in the `routes/`\n  folder.\n---\n\nAfter getting the project running locally, the next step is to add a new route\nto the project. Routes encapsulate the logic for handling requests to a\nparticular path in your project. They can be used to handle API requests or\nrender HTML pages. For now we are going to do the latter.\n\nRoutes are defined as files in the `routes` directory. The file name of the\nmodule is important: it is used to determine the path that the route will\nhandle. For example, if the file name is `index.js`, the route will handle\nrequests to `/`. If the file name is `about.js`, the route will handle requests\nto `/about`. If the file name is `contact.js` and is placed inside of the\n`routes/about/` folder, the route will handle requests to `/about/contact`. This\nconcept is called _File-system routing_. You can learn more about it on the\n[_Concepts: Routing_][concepts-routing] page.\n\nRoute files that render HTML are JavaScript or TypeScript modules that export a\nJSX component as their default export. This component will be rendered for every\nrequest to the route's path. The component receives a few properties that can be\nused to customize the rendered output, such as the current route, the url of the\nrequest, state set by middleware, and handler data (more on the last two later).\n\nIn the demo project we'll create a route to handle the `/about` page. To do\nthis, one needs to create a new `routes/about.tsx` file. In this file, we can\ndeclare a component that should be rendered every time a user visits the page.\nThis is done with JSX.\n\n> [info]: To learn more about JSX, you can read [this article][jsx] in the React\n> documentation. Beware that Fresh does not use React, but rather\n> [Preact][preact], a lighter weight virtual dom library that works similar to\n> React.\n\n```tsx routes/about.tsx\nexport default function AboutPage() {\n  return (\n    <main>\n      <h1>About</h1>\n      <p>This is the about page.</p>\n    </main>\n  );\n}\n```\n\nThe new page will be visible at `http://localhost:8000/about`.\n\n<!-- You can find more in depth information about routes on the\n[_Concepts: Routes_][concepts-routes] documentation page. The following\npages in the _Getting Started_ guide will also explain more features of routes. -->\n\n[concepts-routing]: /docs/1.x/concepts/routing\n[jsx]: https://react.dev/learn/writing-markup-with-jsx\n[preact]: https://preactjs.com/\n\n<!-- [concepts-routes]: /docs/1.x/concepts/routes -->\n"
  },
  {
    "path": "docs/1.x/getting-started/custom-handlers.md",
    "content": "---\ndescription: |\n  Add custom handlers to a route to customize HTTP headers, implement API\n  routes, do data fetching for a rendered page, or handle form submissions.\n---\n\nRoutes actually consist of two parts: handlers, and the page component. Up to\nnow, only the page component has been discussed in this chapter.\n\nHandlers are functions in the form of `Request => Response` or\n`Request => Promise<Response>` that are called when a request is made to a\nparticular route. There can be one handler that covers all HTTP methods or one\nhandler per method.\n\nThe handler has access to the `Request` object that backs the request to the\nroute and must return a `Response` object. The response object can either be\ncreated manually (for example a JSON response for an API route), or it can be\ncreated by rendering the page component. By default, all routes that don't\ndefine a custom handler use a default handler that just renders the page\ncomponent.\n\nTo define a handler in a route module, one must export it as a named export with\nthe name `handler`. Handlers can have two forms: a plain function (catchall for\nall HTTP methods) or a plain object where each property is a function named by\nthe HTTP method it handles.\n\nHere is an example of a custom `GET` handler that renders the page component and\nthen adds a custom header to the response before returning it:\n\n```tsx routes/about.tsx\nimport { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(_req, ctx) {\n    const resp = await ctx.render();\n    resp.headers.set(\"X-Custom-Header\", \"Hello\");\n    return resp;\n  },\n};\n\nexport default function AboutPage() {\n  return (\n    <main>\n      <h1>About</h1>\n      <p>This is the about page.</p>\n    </main>\n  );\n}\n```\n\nNote that handlers do not need to call `ctx.render()`. This feature can be used\nto create API routes. Here is an API route that returns a random UUID as a JSON\nresponse:\n\n```ts routes/api/random-uuid.ts\nimport { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  GET(_req) {\n    const uuid = crypto.randomUUID();\n    return new Response(JSON.stringify(uuid), {\n      headers: { \"Content-Type\": \"application/json\" },\n    });\n  },\n};\n```\n\nHandlers can do much more, including fetching data from a database or external\nAPI and passing it to their route.\n"
  },
  {
    "path": "docs/1.x/getting-started/deploy-to-production.md",
    "content": "---\ndescription: |\n  Deploy a Fresh application to Deno Deploy in seconds, making it available on\n  the edge globally - resulting in fantastic user latency worldwide.\n---\n\nAs a final step in the getting started guide, we'll deploy the demo site to the\npublic internet using [Deno Deploy][deno-deploy]. Deno Deploy is a globally\ndistributed edge runtime built by the Deno company that allows developers to\nquickly and painlessly deploy web applications to the internet. Deno Deploy has\nedge nodes all over the world that serve traffic. Because of this, users\nworldwide have fantastic latency because their traffic is served from a server\nthat is physically close to them.\n\nTo deploy to Deno Deploy, we'll make use of the GitHub integration. To use this\nthe code needs to be pushed to a repository on GitHub. Once this has been done,\none must go to the [Deno Deploy dashboard][deno-deploy-dashboard] and create a\nnew project.\n\nClick on the \"New Project\" button and select the GitHub repository that contains\nthe Fresh project. Select the \"Fresh\" framework preset, and click on \"Advanced\noptions\". Enter `deno task build` in the \"Build command\" field. Press \"Create\nproject\".\n\nThe project will now deploy to Deno Deploy. After this is done, the project will\nbe available at https://$PROJECT_NAME.deno.dev.\n\nEvery time the code in the GitHub repository is updated, it will be deployed\neither as a preview or production deployment. Production deployments are only\ncreated for changes to the default/production branch (often `main`).\n\n[deno-deploy]: https://deno.com/deploy\n[deno-deploy-dashboard]: https://dash.deno.com/projects\n"
  },
  {
    "path": "docs/1.x/getting-started/dynamic-routes.md",
    "content": "---\ndescription: |\n  Create a dynamic route in Fresh by adding a dynamic segment to the route name\n  in the routes' file name on disk: `/greet/[name].tsx`.\n---\n\nThe `/about` route created on the last page is pretty static. It does not matter\nwhat query or path parameters are passed to the route, it will always render the\nsame page. Let's create a `/greet/:name` that will render a page with a greeting\nthat contains the name passed in the path.\n\nBefore diving in, a quick refresher on \"dynamic\" routes. Dynamic routes don't\njust match a single static path, but rather a whole bunch of different paths\nbased on a pattern. For example, the `/greet/:name` route will match the paths\n`/greet/Luca` and `/greet/John`, but not `/greet/Luca/John`.\n\nFresh supports dynamic routes out of the box through file system routing. To\nmake any path segment dynamic, just put square brackets around that segment in\nthe file name. For example the `/greet/:name` route maps to the file name\n`routes/greet/[name].tsx`.\n\nJust like the static `/about` route, the dynamic `/greet/:name` route will\nrender a page. The module must once again expose a component as a default\nexport. This time the component will receive the matched path segment properties\nas arguments in its `props` object though.\n\n```tsx routes/greet/[name].tsx\nimport { PageProps } from \"$fresh/server.ts\";\n\nexport default function GreetPage(props: PageProps) {\n  const { name } = props.params;\n  return (\n    <main>\n      <p>Greetings to you, {name}!</p>\n    </main>\n  );\n}\n```\n\nThe `PageProps` interface actually contains a bunch of useful properties that\ncan be used to customize the rendered output. Next to the matched url pattern\nparameters, the raw `url`, and the `route` name can also be found in here.\n\nNavigating to `http://localhost:8000/greet/Luca` will now render a page showing\n\"Greetings to you, Luca!\".\n\nThe [_Concepts: Routing_][concepts-routing] page has more information about\ndynamic routes, especially about how to create more advanced dynamic routes.\n\n[concepts-routing]: /docs/1.x/concepts/routing\n"
  },
  {
    "path": "docs/1.x/getting-started/form-submissions.md",
    "content": "---\ndescription: |\n  Robustly handle user inputs using HTML `<form>` elements client side, and form\n  submission handlers server side.\n---\n\nForms are a common mechanism for letting users interact with applications. In\nthe last few years it has become more and more common for web applications to\nmove form submission entirely to the client. This can have useful properties for\ninteractivity, but it is much worse for resiliency and user experience as a\nwhole. Browsers have great built in systems for form submission, revolving\naround the HTML `<form>` element.\n\nFresh builds the core of its form submission infrastructure around the native\n`<form>` element. This page explains how to use `<form>` in Fresh, and the next\nchapter explains how to progressively enhance your forms with client side\nJavaScript to make them more interactive.\n\nThe way forms work in the browser, is that they perform an HTML navigation\naction when the user submits the form. In most cases this means that when the\nform is submitted, a `GET` or `POST` request is sent to the server with the form\ndata, which then responds with a new page to render.\n\nFresh can handle both `GET` and `POST` requests through the\n[custom handlers][custom-handlers] feature of routes. The handlers can perform\nany necessary processing on the form data, and then pass data to the\n`ctx.render()` call to render a new page.\n\nHere is an example implementing a search form that filters an array of names\nserver side:\n\n```tsx routes/search.tsx\nimport { Handlers, PageProps } from \"$fresh/server.ts\";\n\nconst NAMES = [\"Alice\", \"Bob\", \"Charlie\", \"Dave\", \"Eve\", \"Frank\"];\n\ninterface Data {\n  results: string[];\n  query: string;\n}\n\nexport const handler: Handlers<Data> = {\n  GET(req, ctx) {\n    const url = new URL(req.url);\n    const query = url.searchParams.get(\"q\") || \"\";\n    const results = NAMES.filter((name) => name.includes(query));\n    return ctx.render({ results, query });\n  },\n};\n\nexport default function Page({ data }: PageProps<Data>) {\n  const { results, query } = data;\n  return (\n    <div>\n      <form>\n        <input type=\"text\" name=\"q\" value={query} class=\"border p-1\" />\n        <button type=\"submit\" class=\"ml-1 px-2 py-1 bg-gray-100 border\">\n          Search\n        </button>\n      </form>\n      <ul>\n        {results.map((name) => <li key={name}>{name}</li>)}\n      </ul>\n    </div>\n  );\n}\n```\n\nWhen the user submits the form, the browser will navigate to `/search` with the\nquery set as the `q` query parameter in the URL. The `GET` handler will then\nfilter the names array based on the query, and pass it to the page component for\nrendering.\n\n[Learn more about using forms in Fresh][concepts-forms].\n\n<!-- TODO(lucacasonato): link to todo app example when that is built again -->\n\n[custom-handlers]: /docs/1.x/getting-started/custom-handlers\n[concepts-forms]: /docs/1.x/concepts/forms\n"
  },
  {
    "path": "docs/1.x/getting-started/index.md",
    "content": "---\ndescription: |\n  In this chapter of the Fresh documentation, you'll be introduced to the\n  framework. Create a new project, run it locally, edit and create pages, fetch\n  data, handle user interactions, and deploy it.\n---\n\nIn this chapter of the Fresh documentation, you'll be introduced to the\nframework. You'll learn how to create a new project, run it locally, edit and\ncreate pages, fetch data, handle user interactions, and how to then deploy the\nproject to [Deno Deploy](https://deno.com/deploy).\n\nThe documentation assumes you have the latest version\n[Deno](https://docs.deno.com/runtime/#install-deno) installed on your system and\nset up your\n[Editor to work with Deno](https://docs.deno.com/runtime/getting_started/setup_your_environment/).\n"
  },
  {
    "path": "docs/1.x/getting-started/running-locally.md",
    "content": "---\ndescription: |\n  To start a Fresh project, just run `deno task start`. This will start the\n  project with default permission flags, in watch mode.\n---\n\nThe next step after scaffolding out a new project, is to actually start it. To\ndo this you can just `deno task start`. Environment variables will be\nautomatically read from `.env`.\n\n```sh Terminal\n$ deno task start\nWatcher Process started.\n 🍋 Fresh ready\n     Local: http://localhost:8000\n```\n\nIf you want to start manually without Deno task, `deno run` the `main.ts` with\nthe appropriate flags. You will need to provide permission flags for:\n\n- **`--allow-net`**: This is required to start the HTTP server.\n- **`--allow-read`**: This is required to read (static) files from disk.\n- **`--allow-env`**: This is required to read environment variables that can be\n  used to configure your project.\n- **`--allow-run`**: This is required to shell out to `deno` and `esbuild` under\n  the hood during development to do type stripping. In production this is done\n  using a WebAssembly binary.\n\nFor development, you also want to run with the [`--watch` flag][--watch], so the\nFresh server will automatically reload whenever you make a change to your code.\nBy default `--watch` only watches over files in your module graph. Some project\nfiles like static files are not part of the module graph, but you probably want\nto restart/reload whenever you make a change to them too. This can be done by\npassing the extra folder as an argument: `--watch=static/`. You should also add\n`routes/` to the watch list, so that the server restarts automatically whenever\nyou add a new route.\n\nIf you want to change the port or host, modify the config bag of the `start()`\ncall in `main.ts` to include an explicit port number:\n\n```ts main.ts\nawait start(manifest, { server: { port: 3000 } });\n```\n\nYou can also change the port by setting the `PORT` environment variable:\n\n```sh Terminal\n$ PORT=3000 deno task start\n```\n\nCombining all of this we get the following `deno run` command:\n\n```sh Terminal\n$ deno run --allow-net --allow-read --allow-env --allow-run --watch=static/,routes/ main.ts\nWatcher Process started.\n 🍋 Fresh ready\n     Local: http://localhost:3000\n```\n\nIf you now visit http://localhost:3000, you can see the running project. Try\nchange some of the text in `routes/index.tsx` and see how the page updates\nautomatically when you save the file.\n\n[--watch]: https://docs.deno.com/runtime/getting_started/command_line_interface/#watch-mode\n"
  },
  {
    "path": "docs/1.x/integrations/index.md",
    "content": "---\ndescription: |\n  Various projects can integrate into Fresh to easily add functionality to your\n  project.\n---\n\nThis section of the docs showcases modules or other projects that integrate with\nFresh. These integrations can be used to add functionality to your project.\n\n## `fresh_charts`\n\nFresh charts allows for easy integration of [Chart.js][chart-js] charts into\nyour Fresh project. It provides a `Chart` JSX component that can be used to\nrender charts on the server and client.\n\nA live demo can be found here: https://fresh-charts.deno.dev/\n\nDocumentation for the module can be found here: https://deno.land/x/fresh_charts\n\n[chart-js]: https://www.chartjs.org/ \"Chart.js\"\n\n## `fresh_marionette`\n\nFresh Marionette allows you to start writing end 2 end browser tests in your\nFresh projects with a single import. Then you can run those browser tests in a\nGitHub Actions workflow.\n\nDocumentation for the module can be found here:\nhttps://deno.land/x/fresh_marionette\n\nAn example project that runs the tests in a GitHub Actions workflow:\nhttps://marionette.deno.dev\n\nFresh Marionette works with VSCode too! - https://youtu.be/OG77NdqL164\n"
  },
  {
    "path": "docs/1.x/introduction/index.md",
    "content": "---\ndescription: |\n  Fresh is a full stack modern web framework for JavaScript and TypeScript\n  developers, designed to build high-quality, performant,\n  and personalized web applications.\n---\n\nFresh is a full stack modern web framework for JavaScript and TypeScript\ndevelopers. It's designed for building high-quality, performant, and\npersonalized web applications. You can use it to create your home page, a blog,\nan e-commerce shop, a large web application like GitHub or Twitter and more.\n\nAt its core, Fresh is a combination of a routing framework and templating engine\nthat renders pages on demand on the server. These server rendered pages can\ncontain areas that are made interactive on the client (also known as the\n[Island Architecture](https://jasonformat.com/islands-architecture)). Fresh uses\n[Preact][preact] as the JSX rendering engine.\n\nFresh projects can be deployed manually to any platform with [Deno][deno], but\nit is intended to be deployed to an edge runtime like [Deno Deploy][deno-deploy]\nfor the best experience.\n\nSome stand out features:\n\n- Zero config necessary\n- Tiny & fast (no client JS is required by the framework)\n- Optional client side hydration of individual components\n- Highly resilient because of progressive enhancement and use of native browser\n  features\n- TypeScript out of the box\n- File-system routing à la Next.js\n\n[preact]: https://preactjs.com\n[deno]: https://deno.com\n[deno-deploy]: https://deno.com/deploy\n"
  },
  {
    "path": "docs/canary/the-canary-version/index.md",
    "content": "---\ndescription: |\n  Learn more about Fresh canary releases\n---\n\nThe canary version represents the current development state of Fresh. It's\nintended for testing work in progress features before it lands in a stable\nrelease. Whenever new code is merged into the `main` branch on\n[GitHub](https://github.com/denoland/fresh) a new canary version is created that\nmatches the hash of the git commit.\n\nWith the addition of new features that are not yet in a stable release, we need\na place to update our documentation. That's what this section of the\ndocumentation is about. It contains all information about unreleased features\nand changes that will land in a stable release.\n"
  },
  {
    "path": "docs/latest/advanced/app-wrapper.md",
    "content": "---\ndescription: |\n  Add a global app wrapper to provide common meta tags or context for application routes.\n---\n\nThe app wrapper component is a Preact component that represents the outer\nstructure of the HTML document, typically up until the `<body>`-tag. It is only\nrendered on the server and never on the client. The passed `Component` value\nrepresents the children of this component.\n\n```tsx main.tsx\nfunction AppWrapper({ Component }) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charset=\"utf-8\" />\n        <title>My App</title>\n      </head>\n      <body>\n        <Component />\n      </body>\n    </html>\n  );\n}\n\napp.appWrapper(AppWrapper);\n```\n\nEvery [`ctx.render()`](/docs/concepts/context#render-1) call will include the\napp wrapper component by default, unless opted out.\n\nNote that only one app wrapper component is supported per\n[`App`](/docs/concepts/app) instance.\n"
  },
  {
    "path": "docs/latest/advanced/builder.md",
    "content": "---\ndescription: |\n  The Builder class is used to generate optimized assets for production.\n---\n\n> [warn]: The `Builder` class was used during the alpha phase of Fresh 2 before\n> the Fresh vite plugin was released. You can skip this page if you're using\n> vite.\n\nThe `Builder` class is used to generate production assets of your app. You'll\ntypically find it being created inside your project's `dev.ts` file.\n\n```ts dev.ts\nimport { Builder } from \"fresh/dev\";\n\nconst builder = new Builder({ target: \"safari12\" });\n\nif (Deno.args.includes(\"build\")) {\n  // This creates a production build\n  await builder.build();\n} else {\n  // This starts a development server with live reload\n  await builder.listen(() => import(\"./main.ts\"));\n}\n```\n\n## Options\n\nYou can customize the builder by passing options.\n\n```ts dev.ts\nconst builder = new Builder({\n  // Browser target for generated code. Maps to https://esbuild.github.io/api/#target\n  target?: string | string[];\n  // The root directory of the project. All other paths will be resolved\n  // against this if they're relative. (Default: `Deno.cwd()`)\n  root?: string;\n  // The path to your server entry point. (Default: `<root>/main.ts`)\n  serverEntry?: string;\n  // Where to write generated files when doing a production build.\n  // (default: `<root>/_fresh/`)\n  outDir?: string;\n  // Path to static file directory. (Default: `<root>/static/`)\n  staticDir?: string;\n  // Path to island directory. (Default: `<root>/islands`)\n  islandDir?: string;\n  // Path to routes directory. (Default: `<root>/routes`)\n  routeDir?: string;\n  // File paths which should be ignored \n  ignore?: RegExp[];\n  // Optionally generate production source maps\n  // See https://esbuild.github.io/api/#source-maps\n  sourceMap?: {\n    kind?: boolean | 'linked' | 'inline' | 'external' | 'both';\n    sourceRoot?: string;\n    sourcesContent?: boolean;\n  };\n})\n```\n\n## Registering islands\n\nThe builder is where you'll register files that contain islands. This is the\nsame API that Fresh uses internally.\n\n```ts dev.ts\nconst builder = new Builder();\n\n// Path to local island\nbuilder.registerIsland(\"path/to/my/Island.tsx\");\n// File urls work too\nbuilder.registerIsland(\"file:///path/to/my/Island.tsx\");\n// Also islands from jsr\nbuilder.registerIsland(\"jsr:@marvinh-test/fresh-island\");\n```\n\n## Adding build plugins\n\nThe `Builder` has a very simple processing mechanism for static files.\n\n```ts dev.ts\nbuilder.onTransformStaticFile({\n  pluginName: \"My cool plugin\",\n  filter: /\\.css$/,\n}, (args) => {\n  // Prepend `body { background: red }` to every `.css` file\n  const code = `body { background: red } ${args.text}`;\n\n  return {\n    content: code,\n    map: undefined, // Optional: source maps\n  };\n});\n```\n\n> [info]: Only static files in `static/` or the value you set `staticDir` to\n> will be processed. The builder won't process anything else.\n\n## Testing\n\nTesting applications with the `Builder` class involves creating a build snapshot\nand assigning that to each app instance.\n\n```ts my-app.test.ts\n// Best to do this once instead of for every test case for\n// performance reasons.\nconst builder = new Builder();\nconst applySnapshot = await builder.build({ snapshot: \"memory\" });\n\nfunction testApp() {\n  const app = new App()\n    .get(\"/\", () => new Response(\"hello\"))\n    .fsRoutes();\n\n  // Applies build snapshot to this app instance.\n  applySnapshot(app);\n  return app;\n}\n\nDeno.test(\"My Test\", async () => {\n  const handler = testApp().handler();\n\n  const response = await handler(new Request(\"http://localhost\"));\n  const text = await response.text();\n\n  if (text !== \"hello\") {\n    throw new Error(\"fail\");\n  }\n});\n```\n\n## Tailwindcss\n\n[Tailwindcss](https://tailwindcss.com/) is a utility-first CSS framework that\ngenerates CSS out of the class names that are used in JSX. Since we use\nTailwindcss ourselves here at Deno, Fresh ships with an official plugin for\nthat.\n\n### Usage\n\n1. Set `nodeModulesDir` in `deno.json` to `\"manual\"`\n\n```diff deno.json\n  {\n    \"name\": \"@example/my-cool-project\"\n+   \"nodeModulesDir\": \"manual\",\n    \"imports\": {\n      ...\n    }\n  }\n```\n\n2. Run `deno install jsr:@fresh/plugin-tailwind`\n3. Update `dev.ts`:\n\n```diff dev.ts\n  import { Builder } from \"fresh/dev\";\n+ import { tailwind } from \"@fresh/plugin-tailwind\";\n  \n  const builder = new Builder();\n+ tailwind(builder);\n```\n\n4. Add `@import \"tailwindcss\";` at the top of your main stylesheet.\n\nFor more information on how to use tailwindcss, check out\n[their documentation](https://tailwindcss.com/docs/styling-with-utility-classes).\n\n### Options\n\nYou can customize the tailwind plugin via the following options:\n\n```ts dev.ts\ntailwind(builder, app, {\n  // Exclude certain files from processing\n  exclude: [\"/admin/**\", \"*.temp.css\"],\n  // Force optimization (defaults to production mode)\n  optimize: true,\n  // Exclude base styles\n  base: null,\n});\n```\n\n### Tailwindcss v3\n\nIf can't update to the current version of tailwindcss we have a dedicated\n`@fresh/plugin-tailwindcss-v3` plugin that uses tailwindcss v3. That way you can\ndecided on your own when it's best to update to v4.\n\n```ts dev.ts\nimport { Builder } from \"fresh/dev\";\nimport { tailwind } from \"@fresh/plugin-tailwind-v3\";\n\ntailwind(builder, {});\n```\n"
  },
  {
    "path": "docs/latest/advanced/define.md",
    "content": "---\ndescription: |\n  Define helpers are a less TypeScripty way to declare middlewares, routes and layouts\n---\n\nDefine helpers can be used to shorten the amount of types you have to type\nyourself in code. They are entirely optional as some developers prefer the\nexplicitness of types, other's like the convenience of `define.*` helpers.\n\nWithout define helpers:\n\n```ts util.ts\nexport interface State {\n  foo: string;\n}\n```\n\n```ts middleware.ts\nimport type { State } from \"./util.ts\";\n\nexport async function myMiddleware(ctx: Context<State>): Promise<Response> {\n  return new Response(\"hello \" + ctx.state.foo);\n}\n\nexport async function otherMiddleware(ctx: Context<State>): Promise<Response> {\n  return new Response(\"other \" + ctx.state.foo);\n}\n```\n\nWith define helpers:\n\n```ts util.ts\nimport { createDefine } from \"fresh\";\n\n// Setup, do this once in a file and import it everywhere else.\nexport const define = createDefine<{ foo: string }>();\n```\n\n```ts middleware.ts\nimport { define } from \"./util.ts\";\n\n// Usage\nexport const myMiddleware = define.middleware((ctx) => {\n  return new Response(\"hello \" + ctx.state.foo);\n});\n\nexport const otherMiddleware = define.middleware((ctx) => {\n  return new Response(\"other \" + ctx.state.foo);\n});\n```\n\n## File routes\n\nThe `define.*` helpers include a `define.handler()` and `define.page()` function\nto make it easy for TypeScript to establish a relation between the two. That way\nyou can pass data from the handler to the component in a type-safe way.\n\n```tsx routes/index.tsx\nexport const handler = define.handlers({\n  GET(ctx) {\n    return { data: { foo: \"Deno\" } };\n  },\n});\n\n// When you type `props.data.*` you'll get autocompletion\nexport default define.page<typeof handler>((props) => {\n  return (\n    <div>\n      <h1>I like {props.data.foo}</h1>\n    </div>\n  );\n});\n```\n\nThere is also a `define.layout()` helper for layouts:\n\n```tsx\nexport default define.layout((props) => {\n  return (\n    <div>\n      <h1>I like {props.state.foo}</h1>\n    </div>\n  );\n});\n```\n"
  },
  {
    "path": "docs/latest/advanced/environment-variables.md",
    "content": "---\ndescription: |\n  Error pages can be used to customize the page that is shown when an error occurs in the application.\n---\n\nEnvironment variables in Deno are typically read via `Deno.env.get()` or\n`process.env.*` calls or via an `.env` file if the `--env-file` flag is used,\nsee\n[how to use Environment Variables in Deno](https://docs.deno.com/runtime/reference/env_variables/).\n\nOn top of that Fresh automatically inlines all environment variables whose names\nstart with `FRESH_PUBLIC_` during bundling of islands.\n\n> [info]: This inlining step occurs when building the app (`deno task build`).\n> Environment variables inside islands cannot be read at runtime.\n\nExample:\n\n```sh Terminal\n$ FRESH_PUBLIC_FOO=bar deno task dev\n```\n\n```tsx\nexport function MyIsland() {\n  const value = Deno.env.get(\"FRESH_PUBLIC_FOO\");\n  return <h1>{value}</h1>;\n}\n```\n\nThis code when bundled will be turned into this:\n\n```tsx\nexport function MyIsland() {\n  const value = \"bar\";\n  return <h1>{value}</h1>;\n}\n```\n\nThis way you can use specific environment variables in the browser.\n\n> [warn]: To make inlining work the code needs to be analyzable by our plugins.\n> This means that not all forms of reading an environment variable in Deno are\n> supported, even if it's perfectly valid JavaScript code.\n>\n> ```ts MyIsland.tsx\n> // CORRECT\n> Deno.env.get(\"FRESH_PUBLIC_FOO\");\n> process.env.FRESH_PUBLIC_FOO;\n>\n> // WRONG\n> const name = \"FRESH_PUBLIC_FOO\";\n> Deno.env.get(name);\n> process.env[name];\n>\n> // WRONG\n> const obj = Deno.env.toObject();\n> obj.FRESH_PUBLIC_FOO;\n> ```\n"
  },
  {
    "path": "docs/latest/advanced/error-handling.md",
    "content": "---\ndescription: |\n  Error pages can be used to customize the page that is shown when an error occurs in the application.\n---\n\nError pages are used to ensure that your app keeps working and display relevant\nfeedback to the one who made the request.\n\nFresh supports two kind of error pages:\n\n1. Generic error pages\n2. 404 Not found error pages\n\n> [tip]: Be sure to return the appropriate\n> [HTTP Status](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status)\n> code. This makes it a lot easier for clients of your app to act appropriately.\n> It also makes it easier to find failed requests when going through traces.\n\n## Generic error pages\n\nTo add an error page use [`app.onError()`](/docs/concepts/app#onerror).\n\n```ts main.ts\nconst app = new App()\n  .onError(\"*\", (ctx) => {\n    console.log(`Error: ${ctx.error}`);\n    return new Response(\"Oops!\", { status: 500 });\n  })\n  .get(\"/thrower\", () => {\n    throw new Error(\"fail\");\n  });\n```\n\nWhen you access `/thrower` the error will be caught and the `onError` callback\nwill be invoked.\n\nYou can also nest error pages:\n\n```ts main.ts\nconst app = new App()\n  // Top level error page\n  .onError(\"*\", (ctx) => {\n    return new Response(\"Oops!\", { status: 500 });\n  })\n  .onError(\"/foo/bar\", (ctx) => {\n    return new Response(\"nested error!\", { status: 500 });\n  })\n  .get(\"/foo/bar/thrower\", () => {\n    throw new Error(\"fail\");\n  });\n```\n\n## Not found error\n\nNot found errors are often treated differently than generic errors. You can both\ntreat them with the `.onError()` way, but by adding a specific `.notFound()`\nhandler, Fresh ensures that every 404 error will invoke this callback.\n\n```ts main.ts\nconst app = new App()\n  // Top level error page\n  .notFound((ctx) => {\n    return new Response(\"Page not found\", { status: 404 });\n  })\n  .get(\"/\", () => new Response(\"foo\"));\n```\n\nAccessing an unknown route like `/invalid` will trigger the `notFound`\nmiddleware. Contrary to generic error pages this handler cannot be nested.\n\n## Throwing HTTP errors\n\nIf you need to bail out of execution and need to respond with a particular HTTP\nerror code, you can use Fresh's `HttpError` class.\n\n```ts middleware/auth.ts\nimport { HttpError } from \"fresh\";\n\nasync function authMiddleware(ctx) {\n  const user = ctx.state.user;\n\n  // Check if user is authenticated, throw 404 error if not\n  if (!isAuthenticated(user)) {\n    throw new HttpError(404);\n  }\n\n  return await ctx.next();\n}\n```\n\nYou can check the status code of the thrown `HttpError` in your error handler:\n\n```ts main.ts\napp.onError((ctx) => {\n  if (ctx.error instanceof HttpError) {\n    const status = ctx.error.status;\n    return new Response(\"oops\", { status });\n  }\n\n  // ...\n});\n```\n"
  },
  {
    "path": "docs/latest/advanced/forms.md",
    "content": "---\ndescription: |\n  Robustly handle user inputs using HTML `<form>` elements client side, and form\n  submission handlers server side.\n---\n\nFor stronger resiliency and user experience, Fresh relies on native browser\nsupport for form submissions with the HTML `<form>` element.\n\nIn the browser, a `<form>` submit will send an HTML action (usually `GET` or\n`POST`) to the server, which responds with a new page to render.\n\n## POST request with `application/x-www-form-urlencoded`\n\nForms typically submit as a `GET` request with data encoded in the URL's search\nparameters, or as a `POST` request with either an\n`application/x-www-form-urlencoded` or `multipart/form-data` body.\n\nThis example demonstrates how to handle `application/x-www-form-urlencoded`\n`<form>` submissions:\n\n```tsx routes/subscribe.tsx\nimport { define } from \"../utils.ts\";\n\nexport const handlers = define.handlers({\n  async GET(ctx) {\n    return { data: {} };\n  },\n  async POST(ctx) {\n    const form = await ctx.req.formData();\n    const email = form.get(\"email\")?.toString();\n\n    // Add email to list.\n\n    // Redirect user to thank you page.\n    const headers = new Headers();\n    headers.set(\"location\", \"/thanks-for-subscribing\");\n    return new Response(null, {\n      status: 303, // See Other\n      headers,\n    });\n  },\n});\n\nexport default define.page<typeof handlers>(function Subscribe() {\n  return (\n    <>\n      <form method=\"post\">\n        <input type=\"email\" name=\"email\" value=\"\" />\n        <button type=\"submit\">Subscribe</button>\n      </form>\n    </>\n  );\n});\n```\n\nWhen the user submits the form, Deno will retrieve the `email` value using the\nrequest's `formData()` method, add the email to a list, and redirect the user to\na thank you page.\n\n## Handling file uploads\n\nFile uploads can be handled in a very similar manner to the example above. Note\nthat this time, we have to explicitly declare the form's encoding to be\n`multipart/form-data`.\n\n```tsx routes/subscribe.tsx\nimport { define } from \"../utils.ts\";\n\nexport const handler = define.handlers({\n  async GET(ctx) {\n    return { data: { message: null } };\n  },\n  async POST(ctx) {\n    const form = await ctx.req.formData();\n    const file = form.get(\"my-file\") as File;\n\n    if (!file) {\n      return { data: { message: \"Please try again\" } };\n    }\n\n    const name = file.name;\n    const contents = await file.text();\n\n    console.log(contents);\n\n    return { data: { message: `${name} uploaded!` } };\n  },\n});\n\nexport default define.page<typeof handlers>(function Upload(props) {\n  const { message } = props.data;\n  return (\n    <>\n      <form method=\"post\" encType=\"multipart/form-data\">\n        <input type=\"file\" name=\"my-file\" />\n        <button type=\"submit\">Upload</button>\n      </form>\n      {message ? <p>{message}</p> : null}\n    </>\n  );\n});\n```\n\n## A note of caution\n\nThese examples are simplified to demonstrate how Deno and Fresh handle HTTP\nrequests. In the Real World™, you'll want to validate your data (_especially the\nfile type_) and protect against cross-site request forgery. Consider yourself\nwarned.\n"
  },
  {
    "path": "docs/latest/advanced/head.md",
    "content": "---\ndescription: Modify the document head in Fresh\n---\n\nThe\n[`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/head)-element\nis a crucial element in HTML to set metadata for a page. It allows you to:\n\n- Set the document title with `<title>`\n- Specify page metadata with `<meta>`\n- Link to resources like stylesheets with `<link>`\n- Include JavaScript code with `<script>`\n\n> [info]: The outer HTML structure including `<head>` is typically created\n> inside `_app.tsx`.\n\n## Passing metadata from `ctx.state`\n\nFor simple scenarios passing metadata along from a handler or a middleware by\nwriting to `ctx.state` is often sufficient.\n\n```tsx routes/_app.tsx\nimport { define } from \"../util.ts\";\n\nexport default define.page((ctx) => {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charset=\"utf-8\" />\n        <title>{ctx.state.title ?? \"Welcome!\"}</title>\n      </head>\n      <body>\n        <ctx.Component />\n      </body>\n    </html>\n  );\n});\n```\n\n## Using the `<Head>`-component\n\nFor more complex scenarios, or to set page metadata from islands, Fresh ships\nwith the `<Head>`-component.\n\n> [info]: The `<Head>` component is not dynamic by default. It will not\n> automatically update the document title or other head elements on the client\n> side when component state changes. The head elements are set during server\n> rendering or initial page load.\n\n```tsx routes/about.tsx\nimport { Head } from \"fresh/runtime\";\n\nexport default define.page((ctx) => {\n  return (\n    <div>\n      <Head>\n        <title>About me</title>\n      </Head>\n      <h1>About me</h1>\n      <p>I like Fresh!</p>\n    </div>\n  );\n});\n```\n\n### Avoiding duplicate tags\n\nYou might end up with duplicate tags, when multiple `<Head />` components are\nrendered on the same page. Fresh will employ the following strategies to find\nthe matching element:\n\n1. For `<title>` elements Fresh will set `document.title` directly\n2. Check if an element with the same `key` exists\n3. Check if an element with the same `id` attribute\n4. Only for `<meta>` elements: Check if there is a `<meta>` element with the\n   same `name` attribute\n5. No matching element was found, Fresh will create a new one and append it to\n   `<head>`\n\n> [info]: The `<title>`-tag is automatically deduplicated, even without a `key`\n> prop.\n"
  },
  {
    "path": "docs/latest/advanced/index.md",
    "content": "---\ndescription: |\n  This chapter goes over some advanced concepts of Fresh.\n---\n\nThis section of the documentation describes advanced functionality of Fresh.\n"
  },
  {
    "path": "docs/latest/advanced/layouts.md",
    "content": "---\ndescription: \"Create re-usable layouts across routes\"\n---\n\nLayouts are plain Preact components that are inherited based on the matching\npattern. When you have a section on your site where all pages share the same\nHTML structure and only the content changes, a layout is a neat way to abstract\nthis. Layouts only ever render on the server. The passed `Component` value\nrepresents the children of this component.\n\n```tsx main.tsx\nfunction PageLayout({ Component }) {\n  return (\n    <div>\n      <Component />\n      <aside>Here is some sidebar content</aside>\n    </div>\n  );\n}\n\nconst app = new App()\n  .layout(\"*\", PageLayout)\n  .get(\"/\", (ctx) => ctx.render(<h1>hello</h1>));\n```\n\nIf you browse to the `/` route, Fresh will render the following HTML\n\n```html Response body\n<div>\n  <h1>hello world</h1>\n  <aside>Here is some sidebar content</aside>\n</div>\n```\n\n## Options\n\nAdd a layout and ignore all previously inherited ones.\n\n```ts main.ts\napp.layout(\"/foo/bar\", MyComponent, { skipInheritedLayouts: true });\n```\n\nIgnore the app wrapper component:\n\n```ts main.ts\napp.layout(\"/foo/bar\", MyComponent, { skipAppWrapper: true });\n```\n"
  },
  {
    "path": "docs/latest/advanced/partials.md",
    "content": "---\ndescription: |\n  Partials allow areas of a page to be updated without causing the browser to reload the page. They enable optimized fine grained UI updates and can be used to do client-side navigation.\n---\n\nPartials allow areas of the page to be updated with new content by the server\nwithout causing the browser to reload the page. They make your website feel more\napp-like because only the parts of the page that need to be updated will be\nupdated.\n\n## Enabling partials\n\nPartials are enabled by adding a `f-client-nav` attribute to an HTML element and\nwrapping one or more areas in the page with a\n`<Partial name=\"my-partial\">`-component.\n\nThe quickest way to get started is to enable partials for every page in\n`routes/_app.tsx` by making the following changes.\n\n```diff routes/_app.tsx\n  import { define } from \"../utils.ts\";\n+ import { Partial } from \"fresh/runtime\";\n\n  export default define.page(function App({ Component }) {\n    return (\n      <html>\n        <head>\n          <meta charset=\"utf-8\" />\n          <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n          <title>My Fresh app</title>\n        </head>\n-       <body>\n+       <body f-client-nav>\n+         <Partial name=\"body\">\n            <Component />\n+         </Partial>\n        </body>\n      </html>\n    );\n  });\n```\n\nBy adding the `f-client-nav` attribute, we enable partials for every element\nbeneath the `<body>`-tag. To mark an area of the page as a partial we wrap it\nwith a `<Partial>`-component with a unique name.\n\nBehind the scenes, when the user clicks an `<a>`-tag, Fresh fetches the new page\nand only pulls out the relevant content out of the HTML response. When it finds\na matching partial area it will update the content inside the partial.\n\n> [info]: The `name` prop of the `<Partial>` component is expected to be unique\n> among Partials. That's how Fresh knows which parts of the response need to go\n> on the current page.\n\n> [info]: Passing `f-client-nav={false}` disables client side navigation for all\n> elements below the current node.\n\n### Optimizing partial requests\n\nBy default, with `f-client-nav` set, Fresh fetches the full next page and only\npicks out the relevant parts of the response. We can optimize this pattern\nfurther by only rendering the parts we need, instead of always rendering the\nfull page. This is done by adding the `f-partial` attribute to a link.\n\n```diff routes/_app.tsx\n- <a href=\"/docs/routes\">Routes</a>\n+ <a href=\"/docs/routes\" f-partial=\"/partials/docs/routes\">Routes</a>\n```\n\nWhen the `f-partial` attribute is present, Fresh will navigate to the page URL\ndefined in the `href` attribute, but fetch the updated UI from the URL specified\nin `f-partial` instead. This can be a highly optimized route that only delivers\nthe content you care about.\n\nLet's use a typical documentation page layout as an example. It often features a\nmain content area and a sidebar of links to switch between pages of the\ndocumentation (marked green here).\n\n![A sketched layout of a typical documentation page with the sidebar on the left composed of green links and a main content area on the right. The main content area is labeled as Partial docs-content](/docs/fresh-partial-docs.png)\n\nThe code for such a page (excluding styling) might look like this:\n\n```tsx routes/docs/[id].tsx\nimport { define } from \"../../utils.ts\";\n\nexport default define.page(async (ctx) => {\n  const content = await loadContent(ctx.params.id);\n\n  return (\n    <div>\n      <aside>\n        <a href=\"/docs/page1\">Page 1</a>\n        <a href=\"/docs/page2\">Page 2</a>\n      </aside>\n      <Partial name=\"docs-content\">\n        {content}\n      </Partial>\n    </div>\n  );\n});\n```\n\nAn optimal route that only renders the content instead of the outer layout with\nthe sidebar might look like this respectively.\n\n```tsx routes/partials/docs/[id].tsx\nimport { define } from \"../utils.ts\";\nimport { Partial } from \"fresh/runtime\";\n\n// We only want to render the content, so disable\n// the `_app.tsx` template as well as any potentially\n// inherited layouts\nexport const config: RouteConfig = {\n  skipAppWrapper: true,\n  skipInheritedLayouts: true,\n};\n\nexport default define.page(async (ctx) => {\n  const content = await loadContent(ctx.params.id);\n\n  // Only render the new content\n  return (\n    <Partial name=\"docs-content\">\n      {content}\n    </Partial>\n  );\n});\n```\n\nBy adding the `f-partial` attribute we tell Fresh to fetch the content from our\nnewly added `/partials/docs/[id].tsx` route.\n\n```diff routes/docs/[id].tsx\n  <aside>\n-   <a href=\"/docs/page1\">Page 1</a>\n-   <a href=\"/docs/page2\">Page 2</a>\n+   <a href=\"/docs/page1\" f-partial=\"/partials/docs/page1\">Page 1</a>\n+   <a href=\"/docs/page2\" f-partial=\"/partials/docs/page2\">Page 2</a>\n  </aside>\n```\n\nWith this in place, Fresh will navigate to the new page when clicking any of the\ntwo links and _only_ load the content rendered by our optimized partial route.\n\n> Currently, `f-partial` is scoped to `<a>`, `<button>` and `<form>` elements.\n> This might be extended to more elements in the future.\n\n## Sending multiple Partials at the same time\n\nA neat aspect of partials in Fresh is that a response can return as many\npartials as desired. That way you can update multiple unrelated areas on your\npage in one single HTTP response. A scenario where this is useful are online\nshops for example.\n\n```tsx routes/partials/cart.tsx\nexport default function AddToCartPartial() {\n  return (\n    <>\n      <Partial name=\"cart-items\" mode=\"append\">\n        {/* Render the new cart item here */}\n      </Partial>\n      <Partial name=\"total-price\">\n        <p>Total: {totalPrice} €</p>\n      </Partial>\n    </>\n  );\n}\n```\n\nBoth partials will be applied to the current page.\n\n## Replacement mode\n\nBy default the whole content inside a partial will be replaced, but there are\nscenarios where you want to prepend or append new content instead. This can be\nachieved by adding the `mode` prop to a `Partial` component.\n\n- `replace` - Swap out the content of the existing partial (default)\n- `prepend` - Insert the new content before the existing content\n- `append` - Insert the new content after the existing content\n\nPersonally, we’ve found that the `append` mode is really useful when you have an\nUI which displays log messages or similar list-like data.\n\n```tsx routes/log.tsx\nexport default function LogView() {\n  const lines = getNewLogLines();\n\n  return (\n    <Partial name=\"logs-list\" mode=\"append\">\n      {lines.map((line) => {\n        return <li key={line}>{line}</li>;\n      })}\n    </Partial>\n  );\n}\n```\n\n> [info]: When picking the `prepend` or `append` mode, make sure to add keys to\n> the elements.\n\n## Bypassing or disabling Partials\n\nIf you want to exempt a particular element from triggering a partial request\nlike on a particular link, form or button, you can opt out of it by setting\n`f-client-nav={false}` on the element or one of the ancestor elements.\n\n```tsx routes/_app.tsx\n<body f-client-nav>\n  {/* This will cause a partial navigation */}\n  <a href=\"/docs/page1\">With partials</a>\n\n  {/* This WONT cause a partial navigation */}\n  <a href=\"/docs/page1\" f-client-nav={false}>No partials</a>\n\n  {/* This WONT cause a partial navigation on any elements below */}\n  <div f-client-nav={false}>\n    <div>\n      <a href=\"/docs/page1\">No partials</a>\n    </div>\n  </div>\n</body>;\n```\n\nWhenever an element is clicked Fresh checks if it has the `f-client-nav`\nattribute and if it is set to `true`. If the element itself doesn't have such an\nattribute, it will check if any of the ancestor elements has it. If an element\nwas found with a truthy `f-client-nav` attribute a partial request will be\ntriggered. If there is no such attribute or if it's set to `false`, no partial\nrequest will occur.\n"
  },
  {
    "path": "docs/latest/advanced/troubleshooting.md",
    "content": "---\ndescription: |\n  Troubleshooting Fresh applications.\n---\n\nThis site contains some tips to troubleshoot your app in case something doesn't\nwork as expected.\n\n## Update Deno\n\nThe Node compatibility layer in Deno is constantly receiving new improvements\nand bug fixes with every Deno version. Chances are that the issue you're running\ninto has already been fixed. To rule that out, install the latest Deno version.\n\n```sh\n# Install latest Deno version\ndeno upgrade\n```\n\n## Install or re-install dependencies\n\nWhen run for the first time, you might see Deno complaining about missing\npackages. Install them with:\n\n```shell\ndeno install --allow-scripts\n```\n\nIf you run into dependency trouble later and suspect that Deno might be caching\nan outdated package, you can force a clean reinstall by adding `-r` to the above\ncommand.\n\n## Update Fresh\n\nThe easiest way to resolve most issues is to ensure that you're on the\n[latest version](https://jsr.io/@fresh/core/versions) of Fresh. We continuously\nwork on Fresh and there is a good chance that the issue you're running into has\nalready been resolved in the latest version of Fresh.\n\n## Don't use esm.sh\n\nFresh 1.x heavily relied on [esm.sh](https://esm.sh/) to be able to use npm\npackages with Fresh. This continued a bit through the early alpha versions of\nFresh 2. With the move to [`vite`](https://vite.dev/) this is not necessary\nanymore and you should use the relevant npm package directly from npm.\n\n```diff deno.json\n  {\n    \"imports\": {\n-     \"cowsay\": \"https://esm.sh/cowsay\"\n+     \"cowsay\": \"npm:cowsay@^1.6.0\"\n    }\n  }\n```\n\n> [info]: Not using `esm.sh` solves many issues and footguns with duplicate\n> Preact versions in your app. If you're seeing strange JavaScript errors in the\n> browser in your app than this is likely the cause.\n\n## Attach a debugger\n\nTo attach a debugger to vite with Deno run this command:\n\n```sh Terminal\ndeno run -A --inspect npm:vite\n# or\ndeno run -A --inspect-brk npm:vite\n```\n\n## Debug vite resolution\n\nTo debug vite resolution issues run vite with the `--debug` flag. This will\nprint lots of debugging information to the terminal.\n\n## Debug vite transformations\n\nTo debug vite plugin transformations, use\n[`vite-plugin-inspect`](https://github.com/antfu-collective/vite-plugin-inspect).\nThis gives you a UI that shows all transformations of all plugins for every\nfile.\n\n## My deployment won't start\n\nIf your deployment doesn't boot, check the following things:\n\n1. Make sure that you ran `deno task build`.\n2. Make sure that your entry points to the generated `_fresh/server.js` file\n   instead of `main.ts`. The latter won't work with Fresh 2.\n\nError messages like `ISOLATE_INTERNAL_FAILURE` may indicate above issues, but\ncan also be caused by other problems in your deployment configuration.\n\n## VS Code does not find packages and/or types\n\nIf you see errors in VS Code like `Cannot find module 'fresh/runtime'` or see a\nlot of TypeScript errors, you likely have not installed the Deno extension. You\ncan easily find it inside VS Code's extension browser (identifier:\n`denoland.vscode-deno`) or get it from the\n[marketplace](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno).\n\nOnce installed and enabled, the currently installed Deno version should appear\nin the bottom status bar. If this does not happen automatically, you can enable\nthe Deno extension via the command palette (Cmd+Shift+P on macOS, Ctrl+Shift+P\non Windows/Linux) and run `Deno: Enable`.\n\nFor detailed instructions, see the official\n[Deno VS Code documentation](https://docs.deno.com/runtime/reference/vscode/).\n"
  },
  {
    "path": "docs/latest/advanced/vite.md",
    "content": "---\ndescription: |\n  Configure the fresh vite plugin.\n---\n\nThe Fresh vite plugin can be optionally configured in the following ways:\n\n```ts vite.config.ts\nimport { defineConfig, type Plugin } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [\n    fresh({\n      // Path to main server entry file. Default: main.ts\n      serverEntry: \"./path/to/main.ts\",\n      // Path to main client entry file. Default: client.ts\n      clientEntry: \"./path/to/client.ts\",\n      // Path to islands directory. Default: ./islands\n      islandsDir: \"./islands\",\n      // Path to routes directory. Default: ./routes\n      routeDir: \"./routes\",\n      // Optional regex to ignore folders when crawling the routes and\n      // island directory.\n      ignore: [/[\\\\/]+some-folder[\\\\/]+/],\n      // Additional specifiers to treat as island files. This is used\n      // for declaring islands from third party packages.\n      islandSpecifiers: [\"@example/my-remote-island\"],\n    }),\n  ],\n});\n```\n"
  },
  {
    "path": "docs/latest/concepts/app.md",
    "content": "---\ndescription: |\n  Add a global app wrapper to provide common meta tags or context for application routes.\n---\n\nThe `App` class is the heart of Fresh and routes incoming requests to the\ncorrect middlewares. This is where routes, middlewares, layouts and more are\ndefined.\n\n```tsx main.ts\nconst app = new App()\n  .use(staticFiles())\n  .get(\"/\", () => new Response(\"hello\"))\n  .get(\"/about\", (ctx) => ctx.render(<h1>About me</h1>));\n\n// Start server\napp.listen();\n```\n\nAll items are applied from top to bottom. This means that when you defined a\nmiddleware _after_ a `.get()` handler, it won't be included.\n\n```tsx main.tsx\nconst app = new App()\n  .use((ctx) => {\n    // Will be called for all middlewares\n    return ctx.next();\n  })\n  .get(\"/\", () => new Response(\"hello\"))\n  .use((ctx) => {\n    // Will only be called for `/about\n    return ctx.next();\n  })\n  .get(\"/about\", (ctx) => ctx.render(<h1>About me</h1>));\n```\n\n## `.use()`\n\nAdd one or more [middlewares](/docs/concepts/middleware). Middlewares are\nmatched left to right.\n\n```ts\n// Add a middleware at the root\napp.use(async (ctx) => {\n  console.log(\"my middleware\");\n  return await ctx.next();\n});\n```\n\nYou can also add multiple middlewares:\n\n```ts\napp.use(middleware1, middleware2, middleware3);\n```\n\nAdding middlewares at a specific path:\n\n```ts\napp.use(\"/foo/bar\", middleware);\n```\n\nMiddlewares can also be instantiated lazily:\n\n```ts\napp.use(\"/foo/bar\", async () => {\n  const mod = await import(\"./path/to/my/middleware.ts\");\n  return mod.default;\n});\n```\n\n## `.get()`\n\nRespond to a `GET` request with the specified middlewares.\n\n```ts\napp.get(\"/about\", async (ctx) => {\n  return new Response(`GET: ${ctx.url.pathname}`);\n});\n```\n\nRespond with multiple middlewares:\n\n```ts\napp.get(\"/about\", middleware1, middleware2, async (ctx) => {\n  return new Response(`GET: ${ctx.url.pathname}`);\n});\n```\n\nYou can also pass lazy middlewares:\n\n```ts\napp.get(\"/about\", async () => {\n  const mod = await import(\"./middleware-or-handler.ts\");\n  return mod.default;\n});\n```\n\n## `.post()`\n\nRespond to a `POST` request with the specified middlewares.\n\n```ts\napp.post(\"/api/user/:id\", async (ctx) => {\n  await somehowCreateUser(ctx.params.id);\n  return new Response(`User created`);\n});\n```\n\nRespond with multiple middlewares:\n\n```ts\napp.post(\"/api/user/:id\", middleware1, middleware2, async (ctx) => {\n  await somehowCreateUser(ctx.params.id);\n  return new Response(`User created`);\n});\n```\n\nYou can also pass lazy middlewares:\n\n```ts\napp.post(\"/api/user/:id\", async () => {\n  const mod = await import(\"./middleware-or-handler.ts\");\n  return mod.default;\n});\n```\n\n## `.put()`\n\nRespond to a `PUT` request with the specified middlewares.\n\n```ts\napp.put(\"/api/user/:id\", async (ctx) => {\n  await somehowSaveUser(ctx.params.id);\n  return new Response(`Updated user`);\n});\n```\n\nRespond with multiple middlewares:\n\n```ts\napp.put(\"/api/user/:id\", middleware1, middleware2, async (ctx) => {\n  await somehowSaveUser(ctx.params.id);\n  return new Response(`Updated user`);\n});\n```\n\nYou can also pass lazy middlewares:\n\n```ts\napp.put(\"/api/user/:id\", async () => {\n  const mod = await import(\"./middleware-or-handler.ts\");\n  return mod.default;\n});\n```\n\n## `.delete()`\n\nRespond to a `DELETE` request with the specified middlewares.\n\n```ts\napp.delete(\"/api/user/:id\", async (ctx) => {\n  await somehowDeleteUser(ctx.params.id);\n  return new Response(`User deleted`);\n});\n```\n\nRespond with multiple middlewares:\n\n```ts\napp.delete(\"/api/user/:id\", middleware1, middleware2, async (ctx) => {\n  await somehowDeleteUser(ctx.params.id);\n  return new Response(`User deleted`);\n});\n```\n\nYou can also pass lazy middlewares:\n\n```ts\napp.delete(\"/api/user/:id\", async () => {\n  const mod = await import(\"./middleware-or-handler.ts\");\n  return mod.default;\n});\n```\n\n## `.head()`\n\nRespond to a `HEAD` request with the specified middlewares.\n\n```ts\napp.head(\"/api/user/:id\", async (ctx) => {\n  return new Response(null, { status: 200 });\n});\n```\n\nRespond with multiple middlewares:\n\n```ts\napp.head(\"/api/user/:id\", middleware1, middleware2, async (ctx) => {\n  return new Response(null, { status: 200 });\n});\n```\n\nYou can also pass lazy middlewares:\n\n```ts\napp.head(\"/api/user/:id\", async () => {\n  const mod = await import(\"./middleware-or-handler.ts\");\n  return mod.default;\n});\n```\n\n## `.all()`\n\nRespond to a request for all HTTP verbs with the specified middlewares.\n\n```ts\napp.all(\"/api/foo\", async (ctx) => {\n  return new Response(\"hehe\");\n});\n```\n\nRespond with multiple middlewares:\n\n```ts\napp.all(\"/api/foo\", middleware1, middleware2, async (ctx) => {\n  return new Response(\"hehe\");\n});\n```\n\nYou can also pass lazy middlewares:\n\n```ts\napp.all(\"/api/foo\", async () => {\n  const mod = await import(\"./middleware-or-handler.ts\");\n  return mod.default;\n});\n```\n\n## `.fsRoute()`\n\nInjects all file-based routes, middlewares, layouts and error pages to the app\ninstance.\n\n```ts\napp.fsRoutes();\n```\n\nYou can optionally pass a path where they should be mounted.\n\n```ts\napp.fsRoutes(\"/foo/bar\");\n```\n\n> [info]: If possible, routes are lazily loaded. Routes that set a route config\n> and set `routeOverride` in particular, are never lazily loaded as Fresh would\n> need to load the file to get the route pattern.\n\n## `.route()`\n\nTODO\n\n## `.appWrapper()`\n\nSet the [App Wrapper](/docs/advanced/app-wrapper) component. This is where the\nouter HTML, typically up until the `<body>`-tag is rendered.\n\n## `.layout()`\n\nSet a [Layout](/docs/advanced/layouts) component at the specified path. The app\nwrapper component and prior layouts are inherited by default unless opted out.\n\n## `.onError()`\n\nSet an error route or middleware that will be rendered when it catches an error.\n\nSetting a middleware:\n\n```ts\n// top level error handler\napp.onError(\"*\", (ctx) => {\n  return new Response(String(ctx.error), { status: 500 });\n});\n```\n\nSetting a route:\n\nTODO\n\n## `.notFound()`\n\nCall this middleware or route whenever a HTTP 404 error is caught.\n\n```ts\napp.notFound(() => {\n  return new Response(\"Not found\", { status: 404 });\n});\n```\n\nTODO: Route\n\n## `.mountApp()`\n\nMount an entire other app at the specified path.\n\n```ts\nconst someRoutes = new App()\n  .get(\"/sitemap.xml\", (ctx) => {/* ... */})\n  .get(\"/robots.txt\", (ctx) => {/* ... */});\n\nexport const app = new App()\n  .use(staticFiles())\n  .mountApp(\"/\", someRoutes())\n  .fsRoutes();\n```\n\n## `.handler()`\n\nCreate a handler function out of your app. This is a function where you can pass\na [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) instance\nto and receive a\n[`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).\n\n```ts\nconst app = new App()\n  .get(\"/\", () => new Response(\"hello\"));\n\nconst handler = app.handler();\n\nconst response = await handler(new Request(\"http://localhost\"));\nconsole.log(await response.text()); // Logs: \"hello\"\n```\n\nThis functionality is often used during testing or to run Fresh inside other\nframeworks.\n\n## `.listen()`\n\nSpawns a server and listens for incoming connections.\n\n```ts\nconst app = new App()\n  .get(\"/\", () => new Response(\"hello\"));\n\napp.listen();\n```\n\nYou can pass an options object to customize which port to listen on and other\naspects.\n\n```ts\napp.listen({ port: 4000 });\n```\n"
  },
  {
    "path": "docs/latest/concepts/context.md",
    "content": "---\ndescription: Plugins can add new functionality to Fresh without requiring significant complexity.\n---\n\nThe `Context` instance is shared across all middlewares in Fresh. Use it to\nrespond with HTML, trigger redirects, access the incoming\n[`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) or read\nother metadata.\n\n## `.config`\n\nContains the resolved Fresh configuration.\n\n```ts\napp.get(\"/\", (ctx) => {\n  console.log(\"Config: \", ctx.config);\n  return new Response(\"hey\");\n});\n```\n\n## `.url`\n\nContains a [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)\ninstance of the requested url.\n\n```ts\napp.get(\"/\", (ctx) => {\n  console.log(\"path: \", ctx.url.pathname);\n\n  const hasParam = ctx.url.searchParams.has(\"q\");\n  return new Response(`Has q param: ${hasParam}`);\n});\n```\n\n## `.req`\n\nContains the incoming\n[`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) instance.\n\n```ts\napp.get(\"/\", (ctx) => {\n  console.log(\"Request: \", ctx.req);\n\n  if (ctx.req.headers.has(\"X-Foo\")) {\n    // do something\n  }\n\n  return new Response(\"hello\");\n});\n```\n\n## `.route`\n\nContains the matched route pattern as a `string`. Will be `null` if no pattern\nmatched.\n\n```ts\napp.get(\"/foo/:id\", (ctx) => {\n  console.log(ctx.route); // Logs: \"/foo/:id\n  // ...\n});\n```\n\n## `.params`\n\nContains the params of the matched route pattern.\n\n```ts\napp.get(\"/foo/:id\", (ctx) => {\n  console.log(\"id: \", ctx.params.id);\n\n  return new Response(`Accessed: /foo/${ctx.params.id}`);\n});\n```\n\n## `.state`\n\nPass data to the next middlewares with state. Every request has its own state\nobject.\n\n```ts\ninterface State {\n  text?: string;\n}\n\nconst app = new App<State>();\n\napp.use((ctx) => {\n  ctx.state.text = \"foo\";\n  return ctx.next();\n});\napp.use((ctx) => {\n  console.log(ctx.state.text); // Logs: \"foo\"\n  return ctx.next();\n});\n```\n\n## `.error`\n\nIf an error was thrown, this property will hold the caught value (default:\n`null`). This is typically used mainly on an error page.\n\n```ts\napp.onError((ctx) => {\n  const message = ctx.error instanceof Error\n    ? ctx.error.message\n    : String(ctx.error);\n\n  return new Response(message, { status: 500 });\n});\n```\n\n## `.redirect()`\n\nTrigger a redirect from a middleware:\n\n```ts\napp.get(\"/old-url\", (ctx) => {\n  return ctx.redirect(\"/new-url\");\n});\n```\n\nSet a custom status code (default is `302`):\n\n```ts\napp.get(\"/old-url\", (ctx) => {\n  return ctx.redirect(\"/new-url\", 307);\n});\n```\n\n## `.render()`\n\nRender JSX and create a HTML `Response`.\n\n```tsx\napp.get(\"/\", (ctx) => {\n  return ctx.render(<h1>hello world</h1>);\n});\n```\n\nSet custom response headers or other metadata:\n\n```tsx\napp.get(\"/teapot\", (ctx) => {\n  return ctx.render(\n    <h1>I'm a teapot</h1>,\n    {\n      status: 418,\n      headers: {\n        \"X-Foo\": \"abc\",\n      },\n    },\n  );\n});\n```\n"
  },
  {
    "path": "docs/latest/concepts/file-routing.md",
    "content": "---\ndescription: |\n  Routes are the basic building block of Fresh applications. They are used to define the behaviour the application when a given path is requested.\n---\n\nUse the `.fsRoutes()` helper on the [`App`](/docs/concepts/app) instance to\nspecify where file based routes should be inserted. It adds routes based on the\nstructure in the `routes/` folder in your project (or any other folder you have\nspecified when instantiating the [`fresh()` vite plugin](/docs/advanced/vite).\nin `vite.config.ts`). When you add a new file there, it will register a new\nroute automatically.\n\n```ts main.ts\nimport { App, staticFiles } from \"fresh\";\n\nconst app = new App({ basePath: \"/foo\" })\n  .use(staticFiles())\n  .fsRoutes(); // This inserts all file based routes here\n```\n\n> [info]: The `staticFiles()` middleware is required when using file based\n> routing. Otherwise the necessary JavaScript files for islands won't be served\n> to the browser.\n\nExample project structure:\n\n```txt-files Project structure\n<project root>\n├── deno.json\n├── main.ts\n├── vite.config.ts\n└── routes\n    ├── (marketing)  # Route group, used to group related routes\n    │   ├── _layout.tsx  # Apply layout to all routes in this directory\n    │   ├── about.tsx    # /about route\n    │   ├── career.tsx   # /career route\n    │   ├── (_components)  # related components\n    │   │   └── newsletter-cta.tsx\n    │   └── (_islands)  # Local island directory\n    │       └── interactive-stats.tsx # Fresh treats this as an island\n    └── shop\n        ├── (_components)\n        │   └── product-card.tsx\n        ├── (_islands)\n        │   └── cart.tsx # Fresh treats this as an island\n        └── index.tsx\n```\n\nFile names are mapped to route patterns as follows:\n\n- File extensions are ignored.\n- Literals in the file path are treated as string literals to match.\n- Files named `<path>/index.<ext>` behave identically to a file named\n  `<path>.<ext>`.\n- Path segments can be made dynamic by surrounding an identifier with `[` and\n  `]`.\n- Paths where the last path segment follows the structure `[...<ident>]` are\n  treated as having a wildcard suffix.\n\nHere is a table of file names, which route patterns they map to, and which paths\nthey might match:\n\n| File name                   | Route pattern          | Matching paths                          |\n| --------------------------- | ---------------------- | --------------------------------------- |\n| `index.ts`                  | `/`                    | `/`                                     |\n| `about.ts`                  | `/about`               | `/about`                                |\n| `blog/index.ts`             | `/blog`                | `/blog`                                 |\n| `blog/[slug].ts`            | `/blog/:slug`          | `/blog/foo`, `/blog/bar`                |\n| `blog/[slug]/comments.ts`   | `/blog/:slug/comments` | `/blog/foo/comments`                    |\n| `old/[...path].ts`          | `/old/:path*`          | `/old/foo`, `/old/bar/baz`              |\n| `docs/[[version]]/index.ts` | `/docs{/:version}?`    | `/docs`, `/docs/latest`, `/docs/canary` |\n\nAdvanced use-cases can require that a more complex pattern be used for matching.\nA custom [URL pattern][urlpattern] can be specified in the route configuration.\nThis pattern will be used instead of the file path based pattern:\n\n```ts routes/my-route.ts\nimport { RouteConfig } from \"fresh\";\n\nexport const config: RouteConfig = {\n  routeOverride: \"/x/:module@:version/:path*\",\n};\n\n// ...\n```\n\n## Route Groups\n\nWhen working with [layouts](/docs/advanced/layouts) or\n[middlewares](/docs/concepts/middleware), you'll sometimes come across a\nsituation where you want your routes to inherit from a layout other than what's\nsuggested by the URL segment.\n\nLet's illustrate that with an example:\n\n```txt Example page layout\n/about -> layout A\n/career -> layout A\n/archive -> layout B\n/contact -> layout B\n```\n\nWithout any way to group routes this is a problem because every route segment\ncan only have one `_layout` file.\n\n```txt-files Project structure\n└── <root>/routes\n    ├── _layout.tsx  # applies to all routes here :(\n    ├── about.tsx\n    ├── career.tsx\n    ├── archive.tsx\n    └── contact.tsx\n```\n\nWe can solve this problem with route groups. A route group is a folder which has\na name that is wrapped in parentheses. For example `(info)` would be considered\na route group and so would `(marketing)`. This enables us to group related\nroutes in a folder and use a different `_layout` file for each group.\n\n```txt-files Project structure\n└── <root>/routes\n    ├── (marketing)\n    │   ├── _layout.tsx  # only applies to about.tsx and career.tsx\n    │   ├── about.tsx\n    │   └── career.tsx\n    └── (info)\n        ├── _layout.tsx  # only applies to archive.tsx and contact.tsx\n        ├── archive.tsx\n        └── contact.tsx\n```\n\n> [warn]: Be careful about routes in different groups which match to the same\n> URL. Such scenarios will lead to ambiguity as to which route file should be\n> picked.\n>\n> ```txt-files Project structure\n> └── <root>/routes\n>     ├── (group-1)\n>     │   └── about.tsx  # Bad: Maps to same `/about` url\n>     └── (group-2)\n>         └── about.tsx  # Bad: Maps to same `/about` url\n> ```\n\n[urlpattern]: https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API\n"
  },
  {
    "path": "docs/latest/concepts/index.md",
    "content": "---\ndescription: |\n  This chapter goes over some fundamental concepts of Fresh.\n---\n\nThe way Fresh works is that it receives a\n[`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request), passes it\nthrough one or more middlewares until one of them responds. This can be an HTML\nresponse, a JSON response or any other response for that matter.\n\nIf the response was HTML and contained Islands (=interactive Preact components),\nFresh will boot them up in the browser and execute the relevant JavaScript.\n\nHere is an overview of the basic concepts in Fresh:\n\n- [**App**](/docs/concepts/app) - Holds all the information about your app, like\n  routes, etc\n- [**Middleware**](/docs/concepts/middleware) - Respond to a request and return\n  a `Response`. Used to set headers, or pass state to other middlewares. When a\n  middleware doesn't call the next one and returns a response, it's usually\n  called a \"handler\".\n- [**Context**](/docs/concepts/context) - Passed through every middleware. Use\n  this to share state, trigger redirects or render HTML.\n- [**Routes**](/docs/concepts/routing) - Responds to a particular URL and runs\n  as chain of middlewares if it matches\n- [**Islands**](/docs/concepts/islands) - Render interactive Preact components\n  on the client\n\nAdvanced concepts:\n\n- [**App wrapper**](/docs/advanced/app-wrapper) - Responsible for the outer HTML\n  structure, usually up to the `<body>`-tag\n- [**Layouts**](/docs/advanced/layouts) - Re-use a shared layout when calling\n  `ctx.render()` across routes\n- [**Partials**](/docs/advanced/partials) - Stream in server generated content\n  on the current page\n"
  },
  {
    "path": "docs/latest/concepts/islands.md",
    "content": "---\ndescription: |\n  Islands enable client side interactivity in Fresh. They are hydrated on the client in addition to being rendered on the server.\n---\n\nIslands enable client side interactivity in Fresh and they are rendered both on\nthe server and in the client.\n\nIslands are defined by creating a file in the `islands/` folder or a\n`(_islands)` folder somewhere in the `routes/` directory. The name of this file\nmust be a PascalCase or kebab-case name of the island.\n\n```tsx islands/my-island.tsx\nimport { useSignal } from \"@preact/signals\";\n\nexport default function MyIsland() {\n  const count = useSignal(0);\n\n  return (\n    <div>\n      <p>Count: {count}</p>\n      <button onClick={() => (count.value += 1)}>+</button>\n    </div>\n  );\n}\n```\n\nAn island can be used anywhere like a regular Preact component. Fresh will take\ncare of making it interactive on the client.\n\n```tsx main.tsx\nimport { App, staticFiles } from \"fresh\";\nimport MyIsland from \"./islands/my-island.tsx\";\n\nconst app = new App()\n  .use(staticFiles())\n  .get(\"/\", (ctx) => ctx.render(<MyIsland />));\n```\n\n## Passing props to islands\n\nPassing props to islands is supported, but only if the props are serializable.\nFresh can serialize the following types of values:\n\n- Primitive types `string`, `number`, `boolean`, `bigint`, `undefined`, and\n  `null`\n- `Infinity`, `-Infinity`, `-0`, and `NaN`\n- `Uint8Array`\n- `URL`\n- `Date`\n- `RegExp`\n- `JSX` Elements\n- Collections `Map` and `Set`\n- Plain objects with string keys and serializable values\n- Arrays containing serializable values\n- Preact Signals (if the inner value is serializable)\n\nCircular references are supported. If an object or signal is referenced multiple\ntimes, it is only serialized once and the references are restored upon\ndeserialization.\n\n> [warn]: Passing functions to an island is not supported.\n>\n> ```tsx routes/example.tsx\n> export default function () {\n>   // WRONG\n>   return <MyIsland onClick={() => console.log(\"hey\")} />;\n> }\n> ```\n\n### Passing JSX\n\nA powerful feature of Fresh is that you can pass server-rendered JSX to an\nisland via props.\n\n```tsx routes/index.tsx\nimport { staticFiles } from \"fresh\";\nimport MyIsland from \"../islands/my-island.tsx\";\n\nconst app = new App()\n  .use(staticFiles())\n  .get(\"/\", (ctx) => {\n    return ctx.render(\n      <MyIsland jsx={<h1>hello</h1>}>\n        <p>This text is rendered on the server</p>\n      </MyIsland>,\n    );\n  });\n```\n\n### Nesting islands\n\nIslands can be nested within other islands as well. In that scenario they act\nlike a normal Preact component, but still receive the serialized props if any\nwere present.\n\nIn essence, Fresh allows you to mix static and interactive parts in your app in\na way that's most optimal for your app. We'll keep sending only the JavaScript\nthat is needed for the islands to the browser.\n\n```tsx islands/other-island.tsx\nexport default (props: { foo: string }) => <>{props.foo}</>;\n```\n\n```tsx route/index.tsx\nimport MyIsland from \"../islands/my-island.tsx\";\nimport OtherIsland from \"../islands/other-island.tsx\";\n\n// Later...\n<div>\n  <MyIsland>\n    <OtherIsland foo=\"this prop will be serialized\" />\n  </MyIsland>\n  <p>Some more server rendered text</p>\n</div>;\n```\n\n## Rendering islands on client only\n\nWhen using client-only APIs, like `EventSource` or `navigator.getUserMedia`,\nthis component will not run on the server as it will produce an error. To fix\nthis use the `IS_BROWSER` flag as a guard:\n\n```tsx islands/my-island.tsx\nimport { IS_BROWSER } from \"fresh/runtime\";\n\nexport function MyIsland() {\n  // Return any prerenderable JSX here which makes sense for your island\n  if (!IS_BROWSER) return <div></div>;\n\n  // All the code which must run in the browser comes here!\n  // Like: EventSource, navigator.getUserMedia, etc.\n  return <div></div>;\n}\n```\n"
  },
  {
    "path": "docs/latest/concepts/layouts.md",
    "content": "---\ndescription: |\n  Add a layout to provide common meta tags, context for application sub routes, and common layout.\n---\n\nA layout is defined in a `_layout.tsx` file in any sub directory (at any level)\nunder the `routes/` folder. It must contain a default export that is a regular\nPreact component. Only one such layout is allowed per sub directory.\n\n```txt-files Project structure\n<project root>\n└── routes\n    ├── sub\n    │   ├── page.tsx\n    │   └── index.tsx\n    ├── other\n    │   ├── _layout.tsx  # will be applied on top of `routes/_layout.tsx`\n    │   └── page.tsx\n    ├── _layout.tsx  # will be applied to all routes\n    └── _app.tsx\n```\n\nThe component to be wrapped is received via props, in addition to a few other\nthings. This allows for the introduction of a global container functioning as a\ntemplate which can be conditioned based on state and params. Note that any state\nset by middleware is available via `props.state`.\n\n```tsx routes/sub/_layout.tsx\nimport { define } from \"../../utils.ts\";\n\nexport default define.layout({ Component, state }) => {\n  // do something with state here\n  return (\n    <div class=\"layout\">\n      <Component />\n    </div>\n  );\n})\n```\n\n## Async layouts\n\nIn case you need to fetch data asynchronously before rendering the layout, you\ncan use an async layout to do so.\n\n```tsx routes/sub/_layout.tsx\nimport { define } from \"../../utils.ts\";\n\nexport default define.layout(async (ctx) => {\n  // do something with state here\n  const data = await loadData();\n\n  return (\n    <div class=\"layout\">\n      <p>{data.greeting}</p>\n      <ctx.Component />\n    </div>\n  );\n});\n```\n\n## Opting out of layout inheritance\n\nSometimes you want to opt out of the layout inheritance mechanism for a\nparticular route. This can be done via route configuration. Picture a directory\nstructure like this:\n\n```txt-files Project structure\n└── <root>/routes\n    ├── sub\n    │   ├── _layout_.tsx\n    │   ├── special.tsx  # should not inherit layouts\n    │   └── index.tsx\n    └── _layout.tsx\n```\n\nTo make `routes/sub/special.tsx` opt out of rendering layouts we can set\n`skipInheritedLayouts: true`.\n\n```tsx routes/sub/special.tsx\nimport { type RouteConfig } from \"fresh\";\nimport { define } from \"../../utils.ts\";\n\nexport const config: RouteConfig = {\n  skipInheritedLayouts: true, // Skip already inherited layouts\n};\n\nexport default define.layout(() => {\n  return <p>Hello world</p>;\n});\n```\n\nYou can skip already inherited layouts inside a layout file:\n\n```tsx routes/special/_layout.tsx\nimport { type LayoutConfig } from \"fresh\";\n\nexport const config: LayoutConfig = {\n  skipInheritedLayouts: true, // Skip already inherited layouts\n};\n\nexport default function MyPage() {\n  return <p>Hello world</p>;\n}\n```\n"
  },
  {
    "path": "docs/latest/concepts/middleware.md",
    "content": "---\ndescription: |\n  Add middleware routes to intercept requests or responses for analytics purposes, access control, or anything else.\n---\n\nA middleware is a function that receives a [`Context`](/docs/concepts/context)\nobject with the\n[`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) and\nreturns a\n[`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response). They\nare typically used to set HTTP Headers, measure response times or fetch data and\npass it to another middleware.\n\n```tsx main.ts\nconst app = new App<{ greeting: string }>()\n  .use((ctx) => {\n    // Middleware to pass data\n    ctx.state.greeting = \"Hello world\";\n    return ctx.next();\n  })\n  .use(async (ctx) => {\n    // Middleware to add a HTTP header\n    const res = await ctx.next();\n    res.headers.set(\"server\", \"fresh server\");\n    return res;\n  })\n  // A handler is a form of middleware that responds. Here we\n  // render HTML and return it.\n  .get(\"/\", (ctx) => {\n    return ctx.render(<h1>{ctx.state.greeting}</h1>);\n  });\n```\n\nMiddlewares can be chained and combined in whatever way you desire. They are an\nexcellent way to make http-related logic reusable on the server.\n\n## Middleware helper\n\nUse the `define.middleware()` helper to get typings out of the box:\n\n```ts middleware/my-middleware.ts\nimport { define } from \"../utils.ts\";\n\nconst middleware = define.middleware(async (ctx) => {\n  console.log(\"my middleware\");\n  return await ctx.next();\n});\n```\n\n## Included middlewares\n\nFresh ships with the following middlewares built-in:\n\n- [cors()](/docs/plugins/cors) - Set CORS HTTP headers\n- [csrf()](/docs/plugins/csrf) - Set CSRF HTTP headers\n- [trailingSlash()](/docs/plugins/trailing-slashes) - Enforce trailing slashes\n\n## Filesystem-based middlewares\n\nWith file system based routing you can define a middleware in a `_middleware.ts`\nfile inside the `routes/` folder or any of it's subfolders.\n\n```ts routes/_middleware.ts\nimport { define } from \"../utils.ts\";\n\nexport default define.middleware(async (ctx) => {\n  console.log(\"my middleware\");\n  return await ctx.next();\n});\n```\n\nYou can also export an array of middlewares:\n\n```ts routes/_middleware.ts\nimport { define } from \"../utils.ts\";\n\nconst middleware1 = define.middleware(async (ctx) => {\n  console.log(\"A\");\n  return await ctx.next();\n});\n\nconst middleware2 = define.middleware(async (ctx) => {\n  console.log(\"B\");\n  return await ctx.next();\n});\n\nexport default [middleware1, middleware2];\n```\n"
  },
  {
    "path": "docs/latest/concepts/routing.md",
    "content": "---\ndescription: |\n  File based routing is the simplest way to do routing in Fresh apps. Additionally custom patterns can be configured per route.\n---\n\nRouting defines which middlewares and routes should respond to a particular\nrequest.\n\n```ts main.ts\nconst app = new App()\n  .get(\"/\", () => new Response(\"hello\")) // Responds to: GET /\n  .get(\"/other\", () => new Response(\"other\")) // Responds to: GET /other\n  .post(\"/upload\", () => new Response(\"upload\")) // Responds to: POST /upload\n  .get(\"/books/:id\", (ctx) => {\n    // Responds to: GET /books/my-book, /books/cool-book, etc\n    const id = ctx.params.id;\n    return new Response(`Book id: ${id}`);\n  })\n  .get(\"/blog/:post/comments\", () => {\n    // Responds to: GET /blog/my-post/comments, /blog/hello/comments, etc\n    const post = ctx.params.post;\n    return new Response(`Blog post comments for post: ${post}`);\n  })\n  .get(\"/foo/*\", (ctx) => {\n    // Responds to: GET /foo/bar, /foo/bar/baz, etc\n    return new Response(\"foo\");\n  });\n```\n\nFresh supports the full\n[`URLPattern`](https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API)\nsyntax for setting pathnames.\n"
  },
  {
    "path": "docs/latest/concepts/static-files.md",
    "content": "---\ndescription: |\n  Fresh has built-in support for serving static files. This is useful for serving images, CSS, and other static assets.\n---\n\nStatic assets placed in the `static/` directory are served at the root of the\nwebserver via the `staticFiles()` middleware. They are streamed directly from\ndisk for optimal performance with\n[`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag)\nheaders.\n\n```ts main.ts\nimport { staticFiles } from \"fresh\";\n\nconst app = new App()\n  .use(staticFiles());\n```\n\n## Caching headers\n\nBy default, Fresh adds caching headers for the `src` and `srcset` attributes on\n`<img>` and `<source>` tags.\n\n```tsx main.tsx\n// Caching headers will be automatically added\napp.get(\"/user\", (ctx) => ctx.render(<img src=\"/user.png\" />));\n```\n\nYou can always opt out of this behaviour per tag, by adding the\n`data-fresh-disable-lock` attribute.\n\n```tsx main.tsx\n// Opt-out of automatic caching headers\napp.get(\n  \"/user\",\n  (ctx) => ctx.render(<img src=\"/user.png\" data-fresh-disable-lock />),\n);\n```\n\n## Adding caching headers manually\n\nUse the `asset()` function to add caching headers manually. It will be served\nwith a cache lifetime of one year.\n\n```tsx routes/about.tsx\nimport { asset } from \"fresh/runtime\";\n\nexport default function About() {\n  // Adding caching headers manually\n  return <a href={asset(\"/brochure.pdf\")}>View brochure</a>;\n}\n```\n"
  },
  {
    "path": "docs/latest/deployment/cloudflare-workers.md",
    "content": "---\ndescription: \"Deploy Fresh on Cloudflare Workers\"\n---\n\nDeploy Fresh to Cloudflare Workers by following these instructions:\n\n1. Run `deno install --allow-scripts npm:@cloudflare/vite-plugin npm:wrangler`\n2. Add the cloudflare plugin in your vite configuration file:\n\n```diff vite.config.ts\n  import { defineConfig } from \"vite\";\n  import { fresh } from \"@fresh/plugin-vite\";\n+ import { cloudflare } from \"@cloudflare/vite-plugin\";\n\n  export default defineConfig({\n    plugins: [\n      fresh(),\n+     cloudflare(),\n    ],\n  });\n```\n\n3. Create a `server.js` file that serves as the cloudflare worker entry file:\n\n```js\nimport server from \"./_fresh/server.js\";\n\nexport default {\n  fetch: server.fetch,\n};\n```\n\n4. Follow further instructions provided by the cloudflare vite plugin.\n\nCheck out the\n[Cloudflare Documentation](https://developers.cloudflare.com/workers/vite-plugin/)\nfor further information.\n\n> [info]: Make sure that you set the the correct entrypoint in your\n> `wrangler.jsonc` file. It should point to `\"main\": \"./server.js\"`\n"
  },
  {
    "path": "docs/latest/deployment/deno-compile.md",
    "content": "---\ndescription: \"Generate a self contained executable with deno compile.\"\n---\n\nYou can create a self-contained executable out of your app with the\n[`deno compile` command](https://docs.deno.com/runtime/reference/cli/compile/).\nIt will include all assets and dependencies. This executable can run on any\nplatform without requiring Deno to be installed.\n\n```sh\n# Build your app first\n$ deno task build\n# Generate self-contained executable\ndeno compile --output my-app --include _fresh -A _fresh/compiled-entry.js\n```\n\nThe compiled entry supports two environment variables out of the box:\n\n- `PORT` to set the port number (`PORT=4000 my-app`)\n- `HOSTNAME` to set the host name number (`HOSTNAME=0.0.0.0 my-app`)\n"
  },
  {
    "path": "docs/latest/deployment/deno-deploy.md",
    "content": "---\ndescription: \"Deploy Fresh on Deno Deploy\"\n---\n\nThe recommended way to deploy Fresh is by using\n[Deno Deploy](https://deno.com/deploy). It will automatically create branch\npreviews for pull requests, collect request and HTTP metrics, as well has\ncollect traces for you out of the box.\n\n1. Log in to [Deno Deploy](https://deno.com/deploy)\n1. Create a new app\n1. Link your GitHub repository\n1. Pick the \"Fresh\" preset if it's not already detected automatically\n\nEvery time you merge into the `main` branch a new production deployment will be\ncreated.\n"
  },
  {
    "path": "docs/latest/deployment/docker.md",
    "content": "---\ndescription: \"Deploy Fresh with Docker\"\n---\n\nYou can deploy Fresh to any platform that can run Docker containers. Docker is a\ntool to containerize projects and portably run them on any supported platform.\n\nWhen packaging your Fresh app for Docker, it is important that you set the\n`DENO_DEPLOYMENT_ID` environment variable in your container. This variable needs\nto be set to an opaque string ID that represents the version of your application\nthat is currently being run. This could be a Git commit hash, or a hash of all\nfiles in your project. It is critical for the function of Fresh that this ID\nchanges when _any_ file in your project changes - if it doesn't, incorrect\ncaching **will** cause your project to not function correctly.\n\nHere is an example `Dockerfile` for a Fresh project:\n\n```dockerfile Dockerfile\nFROM denoland/deno:latest\n\nARG GIT_REVISION\nENV DENO_DEPLOYMENT_ID=${GIT_REVISION}\n\nWORKDIR /app\n\nCOPY . .\nRUN deno task build\nRUN deno cache _fresh/server.js\n\nEXPOSE 8000\n\nCMD [\"serve\", \"-A\", \"_fresh/server.js\"]\n```\n\nTo build your Docker image inside of a Git repository:\n\n```sh Terminal\n$ docker build --build-arg GIT_REVISION=$(git rev-parse HEAD) -t my-fresh-app .\n```\n\nThen run your Docker container:\n\n```sh Terminal\n$ docker run -t -i -p 80:8000 my-fresh-app\n```\n\nTo deploy to a cloud provider, push it to a container registry and follow their\ndocumentation.\n\n- [Amazon Web Services](https://docs.aws.amazon.com/AmazonECS/latest/userguide/create-container-image.html#create-container-image-push-ecr)\n- [Google Cloud](https://cloud.google.com/container-registry/docs/pushing-and-pulling)\n"
  },
  {
    "path": "docs/latest/deployment/index.md",
    "content": "---\ndescription: \"Create a production build of your app\"\n---\n\nWhen shipping an app to production, we can run a build step that optimizes\nassets for consumption in the browser. This step can be invoked by running:\n\n```sh Terminal\ndeno task build\n# or\ndeno run -A dev.ts build\n```\n\nOnce completed, it will have created a `_fresh` folder in the project directory\nwhich contains the optimized assets.\n\n> [info]: The `_fresh` folder should not be committed to git. Exclude it via\n> `.gitignore`.\n>\n> ```gitignore .gitignore\n> # Ignore fresh build directory\n> _fresh/\n> ```\n\n## Running a production build\n\nTo run Fresh in production mode, run the `start` task:\n\n```sh Terminal\ndeno task start\n# or\ndeno serve -A _fresh/server.js\n```\n\nFresh will automatically pick up the optimized assets in the `_fresh` directory.\n"
  },
  {
    "path": "docs/latest/examples/active-links.md",
    "content": "---\ndescription: |\n  Style active links with ease in Fresh\n---\n\nFresh automatically enhances the accessibility of `<a>` elements by adding the\naria-current attribute when rendering links that match the current URL. This\nattribute is recognized by assistive technologies and clearly indicates the\ncurrent page within a set of pages.\n\n- `aria-current=\"page\"` - Added to links with an exact path match, enhancing\n  accessibility by indicating the current page to assistive technologies.\n\nAs we aim to improve accessibility, we encourage the use of aria-current for\nstyling current links where applicable.\n\n## Styling with CSS\n\nThe aria-current attribute is easily styled with CSS using attribute selectors,\nproviding a native way to visually differentiate the active link.\n\n```css static/styles.css\n/* Give links pointing to the current page a green color */\na[aria-current=\"page\"] {\n  color: green;\n}\n\n/* Color all ancestor links of the current page */\na[aria-current=\"true\"] {\n  color: peachpuff;\n}\n```\n\n## Tailwindcss\n\nIn Tailwindcss or similar CSS frameworks, you can apply styles to elements with\nthe `aria-current` attribute using bracket notation in your class definitions.\nFor Tailwindcss, use the syntax:\n\n```tsx components/Menu.tsx\nfunction Menu() {\n  return (\n    <a href=\"/foo\" class=\"aria-[current]:text-green-600\">\n      Link to some page\n    </a>\n  );\n}\n```\n"
  },
  {
    "path": "docs/latest/examples/daisyui.md",
    "content": "---\ntitle: Install daisyUI for Deno Fresh\ndesc: How to install Tailwind CSS and daisyUI in a Deno Fresh project\n---\n\n[daisyUI](https://daisyui.com/) is a component library for\n[Tailwind CSS](https://tailwindcss.com/) that provides semantic class names for\ncommon UI components like buttons, cards, and modals. It makes building\nbeautiful interfaces faster while maintaining full Tailwind CSS compatibility.\n\n## Installation\n\nTo get started with daisyUI, make sure you have Tailwind CSS enabled in your\nFresh project, then install daisyUI and update your configuration.\n\n1. Run `deno i -D npm:daisyui@latest` to install daisyUI\n2. Add daisyUI configuration in `./assets/styles.css`:\n\n   ```diff assets/styles.css\n     @import \"tailwindcss\";\n   + @plugin \"daisyui\";\n   ```\n\nNow you're ready to use daisyUI.\n\n## Using daisyUI Components\n\nCreate a button component in the `components` directory, using daisyUI's style\nclasses for reference.\n\n```tsx components/Button.tsx\nimport type { ComponentChildren } from \"preact\";\n\nexport interface ButtonProps {\n  id?: string;\n  onClick?: () => void;\n  children?: ComponentChildren;\n  disabled?: boolean;\n}\n\nexport function Button(props: ButtonProps) {\n  return (\n    <button\n      className=\"btn btn-dash btn-primary\"\n      {...props}\n    />\n  );\n}\n```\n\n### Display Effect\n\n![DaisyUI Showcase](/docs/fresh-daisyui-showcase.jpg)\n\n### daisyUI Class Name Reference\n\nFor more components and usage, please refer to the\n[daisyUI official documentation](https://daisyui.com/)\n"
  },
  {
    "path": "docs/latest/examples/markdown.md",
    "content": "---\ndescription: |\n  How to render markdown on your Fresh site.\n---\n\n[Markdown](https://www.markdownguide.org/basic-syntax/) is a common text-based\nfile format that is often used for writing documentation, blogs and more. In\nthis example we are going convert markdown content to HTML and send it to the\nbrowser.\n\nFirst, let's install the [`@deno/gfm`](https://jsr.io/@deno/gfm) package that\ncan transform markdown to html.\n\n1. Run `deno install jsr:@deno/gfm`\n2. Create a markdown file like `content.md`:\n\n```md path/to/content.md\n## some heading\n\nand some interesting text here\n\n> oh look a blockquote\n```\n\n4. Add a route that renders that file\n\n```tsx main.ts\nimport { CSS, render as renderMarkdown } from \"@deno/gfm\";\n\nconst CONTENT = `## some heading\n\nand some interesting text here\n\n> oh look a blockquote\n`;\n\nconst app = new App();\n\napp.get(\"/\", async (ctx) => {\n  const content = await Deno.readTextFile(\"path/to/content.md\");\n  const html = renderMarkdown(content);\n\n  return await ctx.render(\n    <div>\n      <h1>Here comes a markdown post:</h1>\n      <style dangerouslySetInnerHTML={{ __html: CSS }} />\n      <div dangerouslySetInnerHTML={{ __html: html }} />\n    </div>,\n  );\n});\n```\n\n## Other libraries\n\nThere are several other popular libraries besides `@deno/gfm` that can be used\nto render markdown. The most common ones are:\n\n- [marked](https://marked.js.org/)\n- [remark](https://remark.js.org/)\n"
  },
  {
    "path": "docs/latest/examples/migration-guide.md",
    "content": "---\ndescription: |\n  Migration guide for Fresh 2.x\n---\n\nWe tried to keep breaking changes in Fresh 2 as minimal as possible, but some\nchanges need to be updated manually. Fresh 2 comes with many quality of life\n[improvements](https://deno.com/blog/an-update-on-fresh) that make it easier to\nextend and adapt Fresh. We've created this upgrade guide as part of upgrading\nour own apps here at Deno.\n\nUse this guide to migrate a Fresh 1.x app to Fresh 2.\n\n## Applying automatic updates\n\nMost changes can be applied automatically with the update script. Start the\nupdate by running it in your project directory:\n\n```sh Terminal\ndeno run -Ar jsr:@fresh/update\n```\n\nThis will apply most API changes made in Fresh 2 automatically update like\nchanging `$fresh/server.ts` imports to `fresh`.\n\n## Getting `main.ts` and `dev.ts` ready\n\nConfiguring Fresh doesn't require a dedicated config file anymore. You can\ndelete the `fresh.config.ts` file. The `fresh.gen.ts` manifest file isn't needed\nanymore either.\n\n```diff Project structure\n  <project root>\n  ├── routes/\n- ├── dev.ts\n- ├── fresh.gen.ts\n- ├── fresh.config.ts\n  └── main.ts\n```\n\nFresh 2 takes great care in ensuring that code that's only needed during\ndevelopment is separate from production code. This split makes deployments much\nsmaller, quicker to upload and allows them to boot up much quicker in\nproduction.\n\n### Replacing `dev.ts` with `vite.config.ts`\n\nDelete `dev.ts` and create a `vite.config.ts` file instead. Pass your custom\nFresh configuration to the [fresh vite plugin](/docs/advanced/vite).\n\n```ts vite.config.ts\nimport { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\nimport tailwindcss from \"@tailwindcss/vite\";\n\nexport default defineConfig({\n  plugins: [\n    fresh(),\n    tailwindcss(),\n  ],\n});\n```\n\n### Add a `client.ts` file\n\nThe `client.ts` file is the main entry file for client-side code. Since vite\nrequires everything to be part of the internal module graph to get hot module\nreloading to work, this is also the place where you import CSS assets.\n\n```diff Project structure\n  <project root>\n  ├── routes/\n+ ├── client.ts\n  ├── vite.config.ts\n  └── main.ts\n```\n\n```ts client.ts\n// Import CSS files here for hot module reloading to work.\nimport \"./assets/styles.css\";\n```\n\n### Updating `main.ts`\n\nSimilarly, configuration related to running Fresh in production can be passed to\n`new App()`:\n\n```ts main.ts\nimport { App, staticFiles } from \"fresh\";\n\nexport const app = new App()\n  // Add static file serving middleware\n  .use(staticFiles())\n  // Enable file-system based routing\n  .fsRoutes();\n```\n\n## Merging error pages\n\nBoth the `_500.tsx` and `_404.tsx` template have been unified into a single\n`_error.tsx` template.\n\n```diff Project structure\n  └── <root>/routes/\n-     ├── _404.tsx\n-     ├── _500.tsx\n+     ├── _error.tsx\n      └── ...\n```\n\nInside the `_error.tsx` template you can show different content based on errors\nor status codes with the following code:\n\n```tsx routes/_error.tsx\nexport default function ErrorPage(props: PageProps) {\n  const error = props.error; // Contains the thrown Error or HTTPError\n  if (error instanceof HttpError) {\n    const status = error.status; // HTTP status code\n\n    // Render a 404 not found page\n    if (status === 404) {\n      return <h1>404 - Page not found</h1>;\n    }\n  }\n\n  return <h1>Oh no...</h1>;\n}\n```\n\n## Updating tasks\n\nThe server entrypoint is now generated by Fresh for more optimal startup times.\nThis means you need to update your task when launching Fresh in production mode.\n\nTo launch Fresh in production mode:\n\n```diff\n- deno run -A main.ts\n+ deno serve -A _fresh/server.js\n```\n\nYou'll likely have that command inside your `deno.json` as a task. Update it\naccordingly.\n\n```diff deno.json\n  {\n    \"tasks\":\n-     \"dev\": \"deno run -A dev.ts\",\n-     \"build\": \"deno run -A dev.ts build\",\n-     \"preview\": \"deno run -A main.ts\"\n+     \"dev\": \"vite\",\n+     \"build\": \"vite build\",\n+     \"preview\": \"deno serve -A _fresh/server.js\"\n   }\n  }\n```\n\n## Update imports\n\nAdd `jsr:@fresh/plugin-vite` and `npm:vite` to `imports` in your `deno.json`\nfile, either manually or with this command:\n\n```shell\ndeno install npm:vite jsr:@fresh/plugin-vite\n```\n\nThis should be enough in most cases. If you still encounter problems, you may\nalso need to update `compilerOptions` and other parts. For a complete,\nup‑to‑date example, generate a new app (“Migrate by example” below) and compare.\n\n> Note: With Vite, Fresh’s Tailwind plugin (`jsr:@fresh/plugin-tailwindcss*`) is\n> not needed; use the Vite integration instead (e.g. `npm:@tailwindcss/vite`).\n\n## Update deployment settings\n\nFresh 2 requires assets to be built during deployment instead of building them\non demand. Run the `deno task build` or `deno run build` command as part of your\ndeployment process.\n\nIf you have already set up Fresh's 1.x \"Ahead-of-Time Builds\", then no changes\nare necessary.\n\n## Trailing slash handling\n\nThe handling trailing slashes has been extracted to an optional middleware that\nyou can add if needed. This middleware can be used to ensure that URLs always\nhave a trailing slash at the end or that they will never have one.\n\n```diff main.ts\n-  import { App, staticFiles } from \"fresh\";\n+  import { App, staticFiles, trailingSlashes } from \"fresh\";\n\n  export const app = new App({ root: import.meta.url })\n    .use(staticFiles())\n+   .use(trailingSlashes(\"never\"));\n```\n\n## Automatic updates\n\n> [info]: The changes listed here are applied automatically when running the\n> [`@fresh/update`](https://jsr.io/@fresh/update) script and you shouldn't need\n> to have to do these yourself.\n\n### Unified middleware signatures\n\nMiddleware, handler and route component signatures have been unified to all look\nthe same. Instead of receiving two arguments, they receive one. The `Request`\nobject is stored on the context object as `ctx.req`.\n\n```diff middleware.ts\n- const middleware = (req, ctx) => new Response(\"ok\");\n+ const middleware = (ctx) => new Response(\"ok\");\n```\n\nSame is true for handlers:\n\n```diff route/page.tsx\n  export const handler = {\n-   GET(req, ctx) {\n+   GET(ctx) {\n      return new Response(\"ok\");\n    },\n  };\n```\n\n...and async route components:\n\n```diff routes/my-page.tsx\n-  export default async function MyPage(req: Request, ctx: RouteContext) {\n+  export default async function MyPage(props: PageProps) {\n    const value = await loadFooValue();\n    return <p>foo is: {value}</p>;\n  }\n```\n\nAll the various context interfaces have been consolidated and simplified:\n\n| Fresh 1.x                                     | Fresh 2.x                           |\n| --------------------------------------------- | ----------------------------------- |\n| `AppContext`, `LayoutContext`, `RouteContext` | [`Context`](/docs/concepts/context) |\n\n### Context methods\n\nThe `ctx.renderNotFound()` method has been removed in favor of throwing an\n`HttpError` instance. This allows all middlewares to optionally participate in\nerror handling. Other properties have been moved or renamed to make it easier to\nre-use existing objects internally as a minor performance optimization.\n\n| Fresh 1.x              | Fresh 2.x                  |\n| ---------------------- | -------------------------- |\n| `ctx.renderNotFound()` | `throw new HttpError(404)` |\n| `ctx.basePath`         | `ctx.config.basePath`      |\n| `ctx.remoteAddr`       | `ctx.info.remoteAddr`      |\n\n### `ctx.render()`\n\nThe _meaning_ of `ctx.render()` has changed. In Fresh 1.x this was used to pass\ndata from a handler to a component. In Fresh 2.x this function has been\ngeneralized to render JSX, so it's no longer needed.\n\nFresh 1.x:\n\n```ts\nexport const handler = {\n  async GET(req, ctx) {\n    const data = await Query();\n    await ctx.render({ value: data });\n  },\n};\n\nexport default function Page({ data }) {\n  // ...\n}\n```\n\nFresh 2 (removed):\n\n```ts\nexport const handler = {\n  async GET(ctx) {\n    const data = await Query();\n    return { data: { value: data } };\n  },\n};\n\nexport default function Page({ data }) {\n  // ...\n}\n```\n\nTo render JSX in general, use the `ctx.render()` function:\n\n```tsx\nconst app = new App()\n  .get(\"/\", () => ctx.render(<h1>hello</h1>));\n```\n\n## `createHandler`\n\nThe `createHandler` function was often used to launch Fresh for tests. This can\nbe now done via vite's `createBuilder` function. See the\n[testing page](/docs/testing) for more information.\n\n## `deno compile`\n\nIf you're using `deno compile` to generate a binary out of the Fresh app be sure\nto [update the command](/docs/deployment/deno-compile) to generate the binary.\n\n## Getting help\n\n### 1. Migrate by example\n\nIf you run into problems with upgrading your app, first, try starting a new\nFresh 2 project and looking at the new structure.\n\neg. `mkdir fresh2-demo && cd fresh2-demo && deno run -Ar jsr:@fresh/init`\n\n### 2. Document\n\nIf you still can't solve the migration problems. Please reach out to us by\ncreating an issue here https://github.com/denoland/fresh/issues/new . That way\nwe can improve this migration guide for everyone.\n"
  },
  {
    "path": "docs/latest/examples/rendering-raw-html.md",
    "content": "---\ndescription: |\n  How to render raw HTML in Fresh.\n---\n\nText content in Fresh is always escaped, whether serverside rendered or rendered\nin islands. While this generally desired, it can create issues in certain\nsituations.\n\nTo address this you can render raw HTML via Preact's `dangerouslySetInnerHTML`\nprop:\n\n```tsx routes/dynamic-html.tsx\n<div dangerouslySetInnerHTML={{ __html: \"<h1>This is raw HTML</h1>\" }} />;\n```\n\nThis will output:\n\n```html Response body\n<div>\n  <h1>This is raw HTML</h1>\n</div>\n```\n\nA common use case for rendering raw HTML is syntax highlighting code blocks or\nrendering markdown.\n\n> [warn]: Setting arbitrary HTML can be dangerous, hence the\n> `dangerouslySetInnerHTML` naming. Make sure you trust the source. Rendering\n> user-supplied HTML to the DOM makes your site vulnerable to cross-site\n> scripting. The markup must first be sanitized, or better yet, something you\n> trust.\n"
  },
  {
    "path": "docs/latest/examples/sharing-state-between-islands.md",
    "content": "---\ndescription: |\n  When you need to have state shared between islands, this page provides a few recipes.\n---\n\nAll of this content is lifted from this great\n[example](https://fresh-with-signals.deno.dev/) by Luca. The source can be found\n[here](https://github.com/lucacasonato/fresh-with-signals).\n\n## Multiple Sibling Islands with Independent State\n\nImagine we have `Counter.tsx` like this:\n\n```tsx islands/Counter.tsx\nimport { useSignal } from \"@preact/signals\";\nimport { Button } from \"../components/Button.tsx\";\n\ninterface CounterProps {\n  start: number;\n}\n\n// This island is used to display a counter and increment/decrement it. The\n// state for the counter is stored locally in this island.\nexport default function Counter(props: CounterProps) {\n  const count = useSignal(props.start);\n  return (\n    <div class=\"flex gap-2 items-center w-full\">\n      <p class=\"flex-grow-1 font-bold text-xl\">{count}</p>\n      <Button onClick={() => count.value--}>-1</Button>\n      <Button onClick={() => count.value++}>+1</Button>\n    </div>\n  );\n}\n```\n\nNote how `useSignal` is within the `Counter` component. Then if we instantiate\nsome counters like this...\n\n```tsx routes/index.tsx\n<Counter start={3} />\n<Counter start={4} />\n```\n\nthey'll keep track of their own independent state. Not much sharing going on\nhere, yet.\n\n## Multiple Sibling Islands with Shared State\n\nBut we can switch things up by looking at a `SynchronizedSlider.tsx` like this:\n\n```tsx islands/SynchronizedSlider.tsx\nimport { Signal } from \"@preact/signals\";\n\ninterface SliderProps {\n  slider: Signal<number>;\n}\n\n// This island displays a slider with a value equal to the `slider` signal's\n// value. When the slider is moved, the `slider` signal is updated.\nexport default function SynchronizedSlider(props: SliderProps) {\n  return (\n    <input\n      class=\"w-full\"\n      type=\"range\"\n      min={1}\n      max={100}\n      value={props.slider.value}\n      onInput={(e) => (props.slider.value = Number(e.currentTarget.value))}\n    />\n  );\n}\n```\n\nNow if we were to do the following...\n\n```tsx routes/index.tsx\nexport default function Home() {\n  const sliderSignal = useSignal(50);\n  return (\n    <div>\n      <SynchronizedSlider slider={sliderSignal} />\n      <SynchronizedSlider slider={sliderSignal} />\n      <SynchronizedSlider slider={sliderSignal} />\n    </div>\n  );\n}\n```\n\nthey would all use the same value.\n\n## Independent Islands\n\nWe can also create a `signal` in a utility file and export it for consumption\nacross multiple places.\n\n```ts utils/cart.ts\nimport { signal } from \"@preact/signals\";\n\nexport const cart = signal<string[]>([]);\n```\n\n```tsx islands/AddToCart.tsx\nimport { Button } from \"../components/Button.tsx\";\nimport { cart } from \"../utils/cart.ts\";\n\ninterface AddToCartProps {\n  product: string;\n}\n\n// This island is used to add a product to the cart state.\nexport default function AddToCart(props: AddToCartProps) {\n  return (\n    <Button\n      onClick={() => (cart.value = [...cart.value, props.product])}\n      class=\"w-full\"\n    >\n      Add{cart.value.includes(props.product) ? \" another\" : \"\"} \"{props.product}\n      \" to cart\n    </Button>\n  );\n}\n```\n\n```tsx islands/Cart.tsx\nimport { Button } from \"../components/Button.tsx\";\nimport { cart } from \"../utils/cart.ts\";\nimport * as icons from \"../components/Icons.tsx\";\n\n// This island is used to display the cart contents and remove items from it.\nexport default function Cart() {\n  return (\n    <h1 class=\"text-xl flex items-center justify-center\">\n      Cart\n    </h1>\n\n    <ul class=\"w-full bg-gray-50 mt-2 p-2 rounded-sm min-h-[6.5rem]\">\n      {cart.value.length === 0 && (\n        <li class=\"text-center my-4\">\n          <div class=\"text-gray-400\">\n            <icons.Cart class=\"w-8 h-8 inline-block\" />\n            <div>\n              Your cart is empty.\n            </div>\n          </div>\n        </li>\n      )}\n      {cart.value.map((product, index) => (\n        <CartItem product={product} index={index} />\n      ))}\n    </ul>\n  );\n}\n\ninterface CartItemProps {\n  product: string;\n  index: number;\n}\n\nfunction CartItem(props: CartItemProps) {\n  const remove = () => {\n    const newCart = [...cart.value];\n    newCart.splice(props.index, 1);\n    cart.value = newCart;\n  };\n\n  return (\n    <li class=\"flex items-center justify-between gap-1\">\n      <icons.Lemon class=\"text-gray-500\" />\n      <div class=\"flex-1\">\n        {props.product}\n      </div>\n      <Button onClick={remove} aria-label=\"Remove\" class=\"border-none\">\n        <icons.X class=\"inline-block w-4 h-4\" />\n      </Button>\n    </li>\n  );\n}\n```\n\nNow we can add the islands to our site by doing the following:\n\n```tsx routes/cart.tsx\n<AddToCart product=\"Lemon\" />\n<AddToCart product=\"Lime\" />\n<Cart />\n```\n\nWhat happens as a result? The `cart` signal is shared across the two `AddToCart`\nislands _and_ the `Cart` island.\n"
  },
  {
    "path": "docs/latest/getting-started/index.md",
    "content": "---\ndescription: |\n  In this chapter of the Fresh documentation, you'll be introduced to the\n  framework. Create a new project, run it locally, edit and create pages, fetch\n  data, handle user interactions, and deploy it.\n---\n\nLet's set up your first Fresh project. To create a new project, run this\ncommand:\n\n```sh Terminal\ndeno run -Ar jsr:@fresh/init\n```\n\nThis will spawn a short wizard that guides you through the setup, like the\nproject name, if you want to use tailwindcss and if you're using vscode. Your\nproject folder should look like this:\n\n```txt-files Project structure\n<project root>\n├── components/         # Store other components here. Can be named differently\n│   └── Button.tsx\n├── islands/            # Components that need JS to run client-side\n│   └── Counter.tsx\n├── routes/             # File system based routes\n│   ├── api/\n│   │   └── [name].tsx  # API route for /api/:name\n│   ├── _app.tsx        # Renders the outer <html> content structure\n│   └── index.tsx       # Renders /\n├── static/             # Contains static assets like css, logos, etc\n│   └── ...\n│\n├── client.ts       # Client entry file that's loaded on every page.\n├── main.ts         # The server entry file of your app\n├── deno.json       # Contains dependencies, tasks, etc\n└── vite.config.ts  # Vite configuration file\n```\n\n## Path aliases\n\nYour new project comes with a `@/` path alias pre-configured in `deno.json`.\nThis allows you to use absolute imports from your project root instead of\nrelative paths:\n\n```tsx routes/about.tsx\n// With @/ alias\nimport { define } from \"@/utils.ts\";\nimport { Button } from \"@/components/Button.tsx\";\n\n// Without alias (relative paths)\nimport { define } from \"../utils.ts\";\nimport { Button } from \"../components/Button.tsx\";\n```\n\nThe `@/` alias is configured in your `deno.json` imports section:\n\n```json deno.json\n{\n  \"imports\": {\n    \"@/\": \"./\"\n    // ... other imports\n  }\n}\n```\n\nThis makes imports cleaner and easier to refactor, especially as your project\ngrows.\n\nRun the `dev` task to launch your app in development mode:\n\n```sh Terminal\ndeno task dev\n```\n\nGo to the URL printed in the terminal to view your app.\n\n![Screenshot of the newly initialized Fresh app showing a counter](/docs/getting-started-1-init.jpg)\n\n> [info]: If you encounter any problems during setup or development, check the\n> [troubleshooting guide](/docs/latest/advanced/troubleshooting) for common\n> issues and solutions.\n\n## Creating our first route\n\nLet's create a new about page at `/about`. We can do that by adding a new file\nat `routes/about.tsx`.\n\n```tsx routes/about.tsx\nimport { define } from \"@/utils.ts\";\n\nexport default define.page(() => {\n  return (\n    <main>\n      <h1>About</h1>\n      <p>This is the about page.</p>\n    </main>\n  );\n});\n```\n\nIf we navigate to `/about` in the browser we'll see our newly created page.\n\n![Screenshot of the /about route](/docs/getting-started-2-about.png)\n\n## Create an island\n\nWe're going to create a countdown component that requires JavaScript to function\nin the browser.\n\nCreate a new file at `islands/Countdown.tsx`\n\n```tsx islands/Countdown.tsx\nimport { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport function Countdown(props: { target: string }) {\n  const count = useSignal(10);\n\n  useEffect(() => {\n    const timer = setInterval(() => {\n      if (count.value <= 0) {\n        clearInterval(timer);\n      }\n\n      count.value -= 1;\n    }, 1000);\n\n    return () => clearInterval(timer);\n  }, []);\n\n  if (count.value <= 0) {\n    return <p>Countdown: 🎉</p>;\n  }\n\n  return <p>Countdown: {count}</p>;\n}\n```\n\nLet's add the countdown to our about page:\n\n```tsx routes/about.tsx\nimport { define } from \"@/utils.ts\";\nimport { Countdown } from \"@/islands/Countdown.tsx\";\n\nexport default define.page(() => {\n  return (\n    <main>\n      <h1>About</h1>\n      <p>This is the about page.</p>\n      <Countdown />\n    </main>\n  );\n});\n```\n\nNow, we can see our countdown in action:\n\n![Screenshot of the countdown component](/docs/getting-started-3-cotuntdown.png)\n"
  },
  {
    "path": "docs/latest/introduction/index.md",
    "content": "---\ndescription: |\n  Fresh is a full stack modern web framework for JavaScript and TypeScript\n  developers, designed to build high-quality, performant,\n  and personalized web applications.\n---\n\nFresh is a small, fast and extensible full stack web framework built on Web\nStandards. It's designed for building high-quality, performant, and personalized\nweb applications.\n\n```tsx main.tsx\nimport { App } from \"fresh\";\n\nconst app = new App()\n  .get(\"/\", () => new Response(\"hello world\"))\n  .get(\"/jsx\", (ctx) => ctx.render(<h1>render JSX!</h1>));\n\napp.listen();\n```\n\n## Quick Start\n\nCreate a new Fresh app by running:\n\n```sh Terminal\ndeno run -Ar jsr:@fresh/init\n```\n\n## Features\n\nThe core idea powering Fresh is to render server generated HTML pages and only\nship JavaScript for areas in the page that need to be interactive. This is often\nreferred to as the\n[Island Architecture](https://jasonformat.com/islands-architecture).\n\n- **Fast** 🚀 - Rendering is super fast thanks to [Preact][preact] and Deno's\n  [`precompile` transform](https://docs.deno.com/runtime/reference/jsx/#jsx-precompile-transform)\n- **Lightweight** 🏎️ - Only ship the JavaScript you need\n- **Extensible** 🧩 - Nearly every aspect can be customized\n- **Powerful & small API** 🤗 - Familiar APIs make you productive quickly\n- **Built-in OpenTelemetry** 📈 - Built-in support for OpenTelemetry\n\n## When to use Fresh\n\nFresh is ideal for sites and apps that are primarily server rendered, like a\nhome page, an e-commerce shop or something like GitHub or Bluesky.\n\n- Web APIs\n- E-Commerce shops\n- Portfolio sites\n- Landing pages & Documentation\n- CRUD apps\n\nThat said, if you want to build a Single-Page-App (=SPA), then Fresh is not the\nright framework.\n\n## Who is using Fresh?\n\nFresh powers [deno.com](https://deno.com) and [Deno Deploy][deno-deploy] among\nmany other projects at Deno. It's also used by [deco.cx](https://deco.cx/) for\ne-commerce projects.\n\n## Where to host Fresh apps?\n\nFresh is most often deployed on [Deno Deploy][deno-deploy] where it can be\ndeployed with 1-click and has out of the box integrations with metrics among\nother things.\n\nFresh projects can be deployed manually to any platform with [Deno][deno] like\nvia docker containers too.\n\n[preact]: https://preactjs.com\n[deno]: https://deno.com\n[deno-deploy]: https://deno.com/deploy\n"
  },
  {
    "path": "docs/latest/plugins/cors.md",
    "content": "---\ndescription: \"Set CORS HTTP headers with the cors middleware\"\n---\n\nThe `cors()` middleware can be used to add\n[Cross-Origin-Resource-Sharing headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS)\nto HTTP requests. These allow the server to indicate which origins (domains,\nscheme or port) other than its own is permitted to load resources from.\n\n```ts main.ts\nimport { cors } from \"fresh\";\n\nconst app = new App()\n  .use(cors({\n    origin: \"http://example.com\",\n    allowHeaders: [\"X-Custom-Header\", \"Upgrade-Insecure-Requests\"],\n    allowMethods: [\"POST\", \"GET\", \"OPTIONS\"],\n    exposeHeaders: [\"Content-Length\", \"X-Kuma-Revision\"],\n    maxAge: 600,\n    credentials: true,\n  }))\n  .get(\"/\", () => new Response(\"hello\"));\n```\n\n## Options\n\nSee the [API docs](https://jsr.io/@fresh/core/doc/~/cors) for a list of all\nsupported options\n"
  },
  {
    "path": "docs/latest/plugins/csp.md",
    "content": "---\ndescription: \"Set Content-Security-Policy (CSP) HTTP headers with the csp middleware\"\n---\n\nThe `csp()` middleware can be used to add\n[Content-Security-Policy headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP)\nto HTTP requests. These restrict which resources a document is allowed to load.\n\n```ts main.ts\nimport { csp } from \"fresh\";\n\nconst app = new App()\n  .use(csp({\n    // If true, sets Content-Security-Policy-Report-Only header instead\n    // of Content-Security-Policy\n    reportOnly: true,\n    // If set, adds Reporting-Endpoints, report-to, and report-uri\n    // directive.\n    reportTo: \"/api/csp-reports\",\n    // Additional CSP directives to add or override the defaults\n    csp: [\n      \"script-src 'self' 'unsafe-inline' 'https://example.com'\",\n    ],\n  }))\n  .get(\"/\", () => new Response(\"hello\"));\n```\n\n## Options\n\nSee the [API docs](https://jsr.io/@fresh/core/doc/~/csp) for a list of all\nsupported options.\n"
  },
  {
    "path": "docs/latest/plugins/csrf.md",
    "content": "---\ndescription: \"Prevent Cross-Site Request Forgery with this middleware\"\n---\n\nThe `csrf()` middleware can be used to add safeguard against\n[Cross-Site Request Forgery vulnerabilities](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF).\nIt checks if the user is allowed to load the requested URL based on the values\nin the\n[`Sec-Fetch-Site`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site)\nheader and\n[`Origin`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Origin)\nheader. to HTTP requests. These allow the server to indicate which origins\n(domains, scheme or port) other than its own is permitted to load resources\nfrom.\n\n```ts main.ts\nimport { app, csrf } from \"fresh\";\n\nconst app = new App();\n\napp.use(csrf());\n\n// Specify a single origin\napp.use(csrf({ origin: \"https://example.com\" }));\n\n// Specify multiple origins\napp.use(\n  csrf({ origin: [\"https://example.com\", \"https://trusted.example.com\"] }),\n);\n\n// Use a function\napp.use(\n  csrf({\n    origin: (origin) => /^https:\\/\\/(foo|bar)\\.example\\.com$/.test(origin),\n  }),\n);\n```\n"
  },
  {
    "path": "docs/latest/plugins/index.md",
    "content": "---\ndescription: \"Extend fresh with plugins\"\n---\n\nFresh itself can be extended through the methods available on the\n[`App`](/docs/concepts/app) class or on the `Builder` class. Most of the\nfeatures in Fresh itself are built using these APIs.\n\n## Custom middlewares\n\nIf you need to modify requests, add HTTP headers or pass additional data to\nother middlewares via `ctx.state`, then going with a middleware is the way to\ngo.\n\n```ts middleware/fresh.ts\nconst addXFreshHeader = define.middleware(async (ctx) => {\n  const res = await ctx.next();\n  res.headers.set(\"X-Fresh\", \"served by Fresh\");\n  return res;\n});\n```\n\nLearn more about [middlewares](/docs/concepts/middleware).\n\nTODO: Show more ways\n"
  },
  {
    "path": "docs/latest/plugins/trailing-slashes.md",
    "content": "---\ndescription: \"Ensure URLs always end or never end with trailing slashes\"\n---\n\nThe `trailingSlashes()` middleware can be used to ensure URL pathnames always\nend with a slash character or will never end with one. It redirects the user's\nrequest respectively.\n\n```ts main.ts\nimport { trailingSlashes } from \"fresh\";\n\nconst app = new App()\n  .use(trailingSlashes(\"never\"))\n  .get(\"/\", () => new Response(\"hello\"));\n```\n\nAlways append a trailing slash:\n\n```ts main.ts\nimport { trailingSlashes } from \"fresh\";\n\nconst app = new App()\n  .use(trailingSlashes(\"always\"))\n  .get(\"/\", () => new Response(\"hello\"));\n```\n"
  },
  {
    "path": "docs/latest/testing/index.md",
    "content": "---\ndescription: |\n  Learn how to test Fresh applications using Deno's built-in test runner.\n---\n\nTo ensure that your application works as expected we can write tests. Any aspect\nof Fresh can be tested as a whole together or in isolation. We use Deno's\nbuilt-in [test runner](https://docs.deno.com/runtime/fundamentals/testing/) to\nwrite tests.\n\n## Testing middlewares\n\nTo test [middlewares](/docs/concepts/middleware) we're going to create a dummy\napp and return the relevant info we want to check in a custom `/` handler. This\ntest assumes the `State` object in `utils.ts` has `text` property.\n\n```ts tests/middleware.test.ts\nimport { expect } from \"@std/expect\";\nimport { App } from \"fresh\";\nimport { define, type State } from \"../utils.ts\";\n\nconst middleware = define.middleware((ctx) => {\n  ctx.state.text = \"middleware text\";\n  return ctx.next();\n});\n\nDeno.test(\"My middleware - sets ctx.state.text\", async () => {\n  const handler = new App<State>()\n    .use(middleware)\n    .get(\"/\", (ctx) => {\n      return new Response(ctx.state.text || \"\");\n    })\n    .handler();\n\n  const res = await handler(new Request(\"http://localhost\"));\n  const text = await res.text();\n\n  expect(text).toEqual(\"middleware text\");\n});\n```\n\nYou can extend this pattern for other middlewares. When you have a middleware\nthat adds a header to the returned response, you can assert against that too.\n\n## Testing app wrapper or layouts\n\nBoth the [app wrapper](/docs/advanced/app-wrapper) component and\n[layouts](/docs/advanced/layouts) can be tested in the same way.\n\n```tsx tests/appWrapper.test.tsx\nimport { expect } from \"@std/expect\";\nimport { App } from \"fresh\";\nimport { define, type State } from \"../utils.ts\";\n\nconst AppWrapper = define.layout(function AppWrapper({ Component }) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charset=\"utf-8\" />\n        <title>My App</title>\n      </head>\n      <body>\n        <Component />\n      </body>\n    </html>\n  );\n});\n\nDeno.test(\"App Wrapper - renders title and content\", async () => {\n  const handler = new App<State>()\n    .appWrapper(AppWrapper)\n    .get(\"/\", (ctx) => ctx.render(<h1>hello</h1>))\n    .handler();\n\n  const res = await handler(new Request(\"http://localhost\"));\n  const text = await res.text();\n\n  expect(text).toContain(\"My App\");\n  expect(text).toContain(\"hello\");\n});\n```\n\nSame can be done for layouts.\n\n```tsx tests/layout.test.tsx\nimport { expect } from \"@std/expect\";\nimport { App } from \"fresh\";\nimport { define, type State } from \"../utils.ts\";\n\nconst MyLayout = define.layout(function MyLayout({ Component }) {\n  return (\n    <div>\n      <h1>My Layout</h1>\n      <Component />\n    </div>\n  );\n});\n\nDeno.test(\"MyLayout - renders heading and content\", async () => {\n  const handler = new App<State>()\n    .appWrapper(MyLayout)\n    .get(\"/\", (ctx) => ctx.render(<h1>hello</h1>))\n    .handler();\n\n  const res = await handler(new Request(\"http://localhost\"));\n  const text = await res.text();\n\n  expect(text).toContain(\"My Layout\");\n  expect(text).toContain(\"hello\");\n});\n```\n\n## Testing routes and handlers\n\nFor testing your route handlers and business logic, you can use the same\n[`App`](/docs/concepts/app) pattern shown above. Fresh makes it easy to test\nindividual routes without needing a full build process, as long as they export a\nhandler:\n\n```ts tests/routes.test.ts\nimport { expect } from \"@std/expect\";\nimport { App } from \"fresh\";\nimport { type State } from \"../utils.ts\";\n\n// Import actual route handlers\nimport { handler as apiHandler } from \"../routes/api/[name].tsx\";\n\nDeno.test(\"API route returns name\", async () => {\n  const app = new App<State>()\n    .get(\"/api/:name\", apiHandler.GET)\n    .handler();\n\n  const response = await app(new Request(\"http://localhost/api/joe\"));\n  const text = await response.text();\n\n  expect(text).toEqual(\"Hello, Joe!\");\n});\n```\n\n## Testing islands\n\nTesting islands requires different approaches for server-side and client-side\nbehavior:\n\n### Server-side rendering of islands\n\nYou can test that your islands render correctly on the server using the same\n[`App`](/docs/concepts/app) pattern. Note: this requires a `.tsx` file extension\nto use JSX:\n\n```tsx tests/island-ssr.test.tsx\nimport { expect } from \"@std/expect\";\nimport { App } from \"fresh\";\nimport { useSignal } from \"@preact/signals\";\nimport { type State } from \"../utils.ts\";\nimport Counter from \"../islands/Counter.tsx\";\n\nfunction CounterPage() {\n  const count = useSignal(3);\n  return (\n    <div class=\"p-8\">\n      <h1>Counter Test Page</h1>\n      <Counter count={count} />\n    </div>\n  );\n}\n\nDeno.test(\"Counter page renders island\", async () => {\n  const app = new App<State>()\n    .get(\"/counter\", (ctx) => {\n      return ctx.render(<CounterPage />);\n    })\n    .handler();\n\n  const response = await app(new Request(\"http://localhost/counter\"));\n  const html = await response.text();\n\n  // Verify the island's initial HTML is present\n  expect(html).toContain('class=\"flex gap-8 py-6\"');\n  expect(html).toContain(\"Counter Test Page\");\n  expect(html).toContain(\"3\");\n});\n```\n\n### Client-side island interactivity\n\nFor testing client-side island behavior (clicks, state changes, etc.), you need\na full build and browser environment. You can use the approach similar to\nFresh's own tests:\n\n```tsx tests/island-client.test.tsx\nimport { expect } from \"@std/expect\";\nimport { buildFreshApp, startTestServer } from \"./test-utils.ts\";\n\nconst app = await buildFreshApp();\n\nDeno.test(\"Counter island renders correctly\", async () => {\n  const { server, address } = startTestServer(app);\n\n  try {\n    // Basic smoke test: verify the island HTML is served\n    const response = await fetch(`${address}/`);\n    const html = await response.text();\n\n    expect(html).toContain('class=\"flex gap-8 py-6\"');\n    expect(html).toContain(\"3\");\n  } finally {\n    await server.shutdown();\n  }\n});\n```\n\n```tsx tests/test-utils.ts\nimport { createBuilder, type InlineConfig } from \"vite\";\nimport * as path from \"@std/path\";\n\n// Default Fresh build configuration\nexport const FRESH_BUILD_CONFIG: InlineConfig = {\n  logLevel: \"error\",\n  root: \"./\",\n  build: { emptyOutDir: true },\n  environments: {\n    ssr: { build: { outDir: path.join(\"_fresh\", \"server\") } },\n    client: { build: { outDir: path.join(\"_fresh\", \"client\") } },\n  },\n};\n\n// Helper function to create and build the Fresh app\nexport async function buildFreshApp(config: InlineConfig = FRESH_BUILD_CONFIG) {\n  const builder = await createBuilder(config);\n  await builder.buildApp();\n  return await import(\"../_fresh/server.js\");\n}\n\n// Helper function to start a test server\nexport function startTestServer(app: {\n  default: {\n    fetch: (req: Request) => Promise<Response>;\n  };\n}) {\n  const server = Deno.serve({\n    port: 0,\n    handler: app.default.fetch,\n  });\n\n  const { port } = server.addr as Deno.NetAddr;\n  const address = `http://localhost:${port}`;\n\n  return { server, address };\n}\n```\n\n**Note:** For most applications, testing the server-side rendering is\nsufficient. Only test client-side interactivity if you have complex island logic\nthat needs verification.\n"
  },
  {
    "path": "docs/toc.ts",
    "content": "import FRESH_VERSIONS_1x from \"../versions.json\" with { type: \"json\" };\nimport LATEST_VERSION_2 from \"../packages/fresh/deno.json\" with {\n  type: \"json\",\n};\n\ntype RawTableOfContents = Record<\n  string,\n  {\n    label: string;\n    content: Record<string, RawTableOfContentsEntry>;\n  }\n>;\n\ninterface RawTableOfContentsEntry {\n  title: string;\n  link?: string;\n  pages?: [string, string, string?][];\n}\n\nconst toc: RawTableOfContents = {\n  latest: {\n    label: LATEST_VERSION_2.version,\n    content: {\n      introduction: {\n        title: \"Introduction\",\n        link: \"latest\",\n      },\n      \"getting-started\": {\n        title: \"Getting Started\",\n        link: \"latest\",\n      },\n      concepts: {\n        title: \"Concepts\",\n        link: \"latest\",\n        pages: [\n          [\"app\", \"App\", \"link:latest\"],\n          [\"middleware\", \"Middlewares\", \"link:latest\"],\n          [\"context\", \"Context\", \"link:latest\"],\n\n          [\"routing\", \"Routing\", \"link:latest\"],\n\n          [\"islands\", \"Islands\", \"link:latest\"],\n          [\"static-files\", \"Static files\", \"link:latest\"],\n\n          [\"file-routing\", \"File routing\", \"link:latest\"],\n        ],\n      },\n      advanced: {\n        title: \"Advanced\",\n        link: \"latest\",\n        pages: [\n          [\"app-wrapper\", \"App wrapper\", \"link:latest\"],\n          [\"layouts\", \"Layouts\", \"link:latest\"],\n          [\"error-handling\", \"Error handling\", \"link:latest\"],\n          [\"partials\", \"Partials\", \"link:latest\"],\n          [\"forms\", \"Forms\", \"link:latest\"],\n          [\"define\", \"Define Helpers\", \"link:latest\"],\n          [\"environment-variables\", \"Environment Variables\", \"link:latest\"],\n          [\"head\", \"Modifying <head>\", \"link:latest\"],\n          [\"vite\", \"Vite Plugin Options\", \"link:latest\"],\n          [\"troubleshooting\", \"Troubleshooting\", \"link:latest\"],\n          [\"builder\", \"Builder (Legacy)\", \"link:latest\"],\n        ],\n      },\n      deployment: {\n        title: \"Deployment\",\n        link: \"latest\",\n        pages: [\n          [\"deno-deploy\", \"Deno Deploy\", \"link:latest\"],\n          [\"deno-compile\", \"deno compile\", \"link:latest\"],\n          [\"docker\", \"Docker\", \"link:latest\"],\n          [\"cloudflare-workers\", \"Cloudflare Workers\", \"link:latest\"],\n        ],\n      },\n      testing: {\n        title: \"Testing\",\n        link: \"latest\",\n      },\n      plugins: {\n        title: \"Plugins\",\n        link: \"latest\",\n        pages: [\n          [\"cors\", \"cors\", \"link:latest\"],\n          [\"csrf\", \"csrf\", \"link:latest\"],\n          [\"csp\", \"csp\", \"link:latest\"],\n          [\"trailing-slashes\", \"trailingSlashes\", \"link:latest\"],\n        ],\n      },\n      examples: {\n        title: \"Examples\",\n        link: \"latest\",\n        pages: [\n          [\"migration-guide\", \"Migration Guide\", \"link:latest\"],\n          [\"daisyui\", \"daisyUI\", \"link:latest\"],\n          [\"markdown\", \"Rendering Markdown\", \"link:latest\"],\n          [\"rendering-raw-html\", \"Rendering raw HTML\", \"link:latest\"],\n          [\n            \"sharing-state-between-islands\",\n            \"Sharing state between islands\",\n            \"link:latest\",\n          ],\n          [\"active-links\", \"Active links\", \"link:latest\"],\n        ],\n      },\n    },\n  },\n  \"1.x\": {\n    label: FRESH_VERSIONS_1x[0],\n    content: {\n      introduction: {\n        title: \"Introduction\",\n      },\n      \"getting-started\": {\n        title: \"Getting Started\",\n        pages: [\n          [\"create-a-project\", \"Create a project\"],\n          [\"running-locally\", \"Running locally\"],\n          [\"create-a-route\", \"Create a route\"],\n          [\"dynamic-routes\", \"Dynamic routes\"],\n          [\"custom-handlers\", \"Custom handlers\"],\n          [\"form-submissions\", \"Form submissions\"],\n          [\"adding-interactivity\", \"Adding interactivity\"],\n          [\"deploy-to-production\", \"Deploy to production\"],\n        ],\n      },\n      concepts: {\n        title: \"Concepts\",\n        pages: [\n          [\"architecture\", \"Architecture\"],\n          [\"server-components\", \"Server Components\"],\n          [\"routing\", \"Routing\"],\n          [\"routes\", \"Routes\"],\n          [\"app-wrapper\", \"App wrapper\"],\n          [\"layouts\", \"Layouts\"],\n          [\"forms\", \"Forms\"],\n          [\"islands\", \"Interactive islands\"],\n          [\"static-files\", \"Static files\"],\n          [\"middleware\", \"Middlewares\"],\n          [\"error-pages\", \"Error pages\"],\n          [\"partials\", \"Partials\"],\n          [\"data-fetching\", \"Data fetching\"],\n          [\"ahead-of-time-builds\", \"Ahead-of-time Builds\"],\n          [\"deployment\", \"Deployment\"],\n          [\"plugins\", \"Plugins\"],\n          [\"updating\", \"Updating Fresh\"],\n          [\"server-configuration\", \"Server configuration\"],\n        ],\n      },\n      integrations: {\n        title: \"Integrations\",\n      },\n      examples: {\n        title: \"Examples\",\n        pages: [\n          [\"migrating-to-tailwind\", \"Migrating to Tailwind\"],\n          [\"modifying-the-head\", \"Modifying the <head>\"],\n          [\"writing-tests\", \"Writing tests\"],\n          [\"changing-the-src-dir\", \"Changing the source directory\"],\n          [\"using-twind-v1\", \"Using Twind v1\"],\n          [\"init-the-server\", \"Initializing the server\"],\n          [\"using-fresh-canary-version\", \"Using Fresh canary version\"],\n          [\"dealing-with-cors\", \"Dealing with CORS\"],\n          [\"creating-a-crud-api\", \"Creating a CRUD API\"],\n          [\"handling-complex-routes\", \"Handling complex routes\"],\n          [\"rendering-markdown\", \"Rendering markdown\"],\n          [\"rendering-raw-html\", \"Rendering raw HTML\"],\n          [\"sharing-state-between-islands\", \"Sharing state between islands\"],\n          [\"using-csp\", \"Using CSP\"],\n          [\"active-links\", \"Styling active links\"],\n          [\n            \"client-side-components-and-libraries\",\n            \"Client only side components\",\n          ],\n        ],\n      },\n    },\n  },\n};\n\nexport default toc;\n"
  },
  {
    "path": "packages/build-id/README.md",
    "content": "# Fresh build id\n\nDon't use this package directly. It's considered internal for Fresh.\n"
  },
  {
    "path": "packages/build-id/deno.json",
    "content": "{\n  \"name\": \"@fresh/build-id\",\n  \"version\": \"1.0.1\",\n  \"license\": \"MIT\",\n  \"exports\": {\n    \".\": \"./mod.ts\"\n  },\n  \"imports\": {\n    \"@std/encoding\": \"jsr:@std/encoding@^1.0.10\"\n  }\n}\n"
  },
  {
    "path": "packages/build-id/mod.ts",
    "content": "/**\n * Don't use this package directly. It's considered internal for Fresh.\n *\n * @example\n * ```ts\n * // `BUILD_ID` is based on the `DENO_DEPLOYMENT_ID` environment variable.\n * ```\n *\n * @see https://fresh.deno.dev/docs/deployment/docker\n *\n * @module\n * @private\n */\n\nimport { encodeHex } from \"@std/encoding/hex\";\n\nexport const DENO_DEPLOYMENT_ID: string | undefined = Deno.env.get(\n  \"DENO_DEPLOYMENT_ID\",\n);\nconst deploymentId = DENO_DEPLOYMENT_ID ||\n  // For CI\n  Deno.env.get(\"GITHUB_SHA\") ||\n  Deno.env.get(\"CI_COMMIT_SHA\") ||\n  crypto.randomUUID();\nconst buildIdHash = await crypto.subtle.digest(\n  \"SHA-1\",\n  new TextEncoder().encode(deploymentId),\n);\n\nexport let BUILD_ID: string = encodeHex(buildIdHash);\n\nexport function setBuildId(buildId: string): void {\n  BUILD_ID = buildId;\n}\n"
  },
  {
    "path": "packages/examples/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-2024 the Deno authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "packages/examples/README.md",
    "content": "# Examples for Fresh\n\nThis package contains examples for using Fresh with [JSR](https://jsr.io/).\n\nLearn more about the Fresh framework here:\n[https://fresh.deno.dev/](https://fresh.deno.dev/)\n\n## Usage: Island example\n\n```tsx\nimport { App } from \"fresh\";\n// Import the island function\nimport { DemoIsland } from \"jsr:@fresh/examples/island\";\n\nexport const app = new App({ root: import.meta.url })\n  .use(staticFiles());\n\n// Use the island somewhere in your components\napp.get(\"/\", (ctx) => ctx.render(<DemoIsland />));\n\nawait app.listen();\n```\n\n## Usage: App1 or App2 example\n\n```tsx\nimport { App } from \"fresh\";\n// Import the example apps\nimport { app1 } from \"jsr:@fresh/examples/app1\";\nimport { app2 } from \"jsr:@fresh/examples/app2\";\n\nexport const app = new App({ root: import.meta.url })\n  .use(staticFiles());\n\n// Merge apps from JSR into this one\napp.mountApp(\"/app1\", app1);\napp.mountApp(\"/app2\", app1);\n\nawait app.listen();\n```\n\n## License\n\nMIT, see the [LICENSE](./LICENSE) file.\n"
  },
  {
    "path": "packages/examples/deno.json",
    "content": "{\n  \"name\": \"@fresh/examples\",\n  \"version\": \"1.0.1\",\n  \"license\": \"MIT\",\n  \"imports\": {\n    \"fresh\": \"jsr:@fresh/core@^2.0.0\",\n    \"preact\": \"npm:preact@^10.27.0\",\n    \"@preact/signals\": \"npm:@preact/signals@^2.2.1\"\n  },\n  \"exports\": {\n    \"./island\": \"./src/island.tsx\",\n    \"./app1\": \"./src/app1.tsx\",\n    \"./app2\": \"./src/app2.tsx\"\n  }\n}\n"
  },
  {
    "path": "packages/examples/src/app1.tsx",
    "content": "/**\n * Module containing a simple example Fresh App\n *\n * @module\n */\n\nimport { App } from \"fresh\";\nimport { Doc } from \"./shared.tsx\";\n\n/** App that renders a sample HTML document */\nexport const app1: App<unknown> = new App()\n  .get(\"/\", (ctx) =>\n    ctx.render(\n      <Doc>\n        <h1>App1</h1>\n        <p>This app is loaded from JSR</p>\n      </Doc>,\n    ));\n"
  },
  {
    "path": "packages/examples/src/app2.tsx",
    "content": "/**\n * Module containing a simple example Fresh App\n *\n * @module\n */\n\nimport { App } from \"fresh\";\nimport { Doc } from \"./shared.tsx\";\n\n/** App that renders a sample HTML document */\nexport const app2: App<unknown> = new App()\n  .get(\"/\", (ctx) =>\n    ctx.render(\n      <Doc>\n        <h1>App2</h1>\n        <p>This app is loaded from JSR</p>\n      </Doc>,\n    ));\n"
  },
  {
    "path": "packages/examples/src/island.tsx",
    "content": "/**\n * Example of external Fresh Island component.\n *\n * @example\n * ```tsx\n * import { App } from \"fresh\";\n * import { DemoIsland } from \"jsr:@fresh/examples/island\";\n *\n * const app = new App();\n *\n * // Use the island somewhere in your components\n * app.get(\"/\", (ctx) => ctx.render(<DemoIsland />));\n * ```\n *\n * @module\n */\n\nimport { useSignal } from \"@preact/signals\";\nimport type { JSX } from \"preact\";\n\n/** A simple counter demo island component using Preact signals */\nexport function DemoIsland(): JSX.Element {\n  const count = useSignal(0);\n\n  return (\n    <div style=\"display: flex; gap: 1rem; padding: 2rem;\">\n      <button type=\"button\" onClick={() => (count.value -= 1)}>-1</button>\n      <p style=\"font-variant-numeric: tabular-nums;\">{count}</p>\n      <button type=\"button\" onClick={() => (count.value += 1)}>+1</button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/examples/src/shared.tsx",
    "content": "import type { ComponentChildren } from \"preact\";\n\nexport function Doc(props: { children?: ComponentChildren }) {\n  return (\n    <html>\n      <head>\n        <meta charset=\"utf-8\" />\n        <title>Fresh Examples</title>\n      </head>\n      <body>{props.children}</body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/README.md",
    "content": "# Fresh\n\nFresh is a small, fast and extensible full stack web framework built on Web\nStandards. It’s designed for building high-quality, performant, and personalized\nweb applications.\n\n[Learn more about Fresh](https://fresh.deno.dev/)\n\n## Usage\n\nGenerate a new Fresh project with `@fresh/init`:\n\n```sh\ndeno run -Ar jsr:@fresh/init\n```\n\nAdd middleware, routes, & endpoints as needed via the `routes/` folder or\ndirectly on the `App` instance.\n\n```tsx\nimport { App } from \"fresh\";\n\nconst app = new App()\n  .get(\"/\", () => new Response(\"hello world\"))\n  .get(\"/jsx\", (ctx) => ctx.render(<h1>render JSX!</h1>));\n\napp.listen();\n```\n\nFor more information on getting started with Fresh, head on over to the\n[documentation](https://fresh.deno.dev/docs/introduction).\n"
  },
  {
    "path": "packages/fresh/deno.json",
    "content": "{\n  \"name\": \"@fresh/core\",\n  \"version\": \"2.2.1\",\n  \"license\": \"MIT\",\n  \"exports\": {\n    \".\": \"./src/mod.ts\",\n    \"./runtime\": \"./src/runtime/shared.ts\",\n    \"./runtime-client\": \"./src/runtime/client/mod.ts\",\n    \"./dev\": \"./src/dev/mod.ts\",\n    \"./compat\": \"./src/compat.ts\",\n    \"./internal\": \"./src/internals.ts\",\n    \"./internal-dev\": \"./src/internals_dev.ts\"\n  },\n  \"imports\": {\n    \"@deno/esbuild-plugin\": \"jsr:@deno/esbuild-plugin@^1.2.0\",\n    \"@opentelemetry/api\": \"npm:@opentelemetry/api@^1.9.0\",\n    \"@std/encoding\": \"jsr:@std/encoding@^1.0.10\",\n    \"@std/fmt\": \"jsr:@std/fmt@^1.0.8\",\n    \"@std/fs\": \"jsr:@std/fs@^1.0.19\",\n    \"@std/html\": \"jsr:@std/html@^1.0.5\",\n    \"@std/http\": \"jsr:@std/http@^1.0.21\",\n    \"@std/jsonc\": \"jsr:@std/jsonc@^1.0.2\",\n    \"@std/media-types\": \"jsr:@std/media-types@^1.1.0\",\n    \"@std/path\": \"jsr:@std/path@^1.1.2\",\n    \"@std/semver\": \"jsr:@std/semver@^1.0.6\",\n    \"@std/uuid\": \"jsr:@std/uuid@^1.0.9\",\n    \"@types/node\": \"npm:@types/node@^24.2.1\",\n    \"esbuild-wasm\": \"npm:esbuild-wasm@^0.25.11\",\n    \"preact\": \"npm:preact@^10.28.3\",\n    \"preact-render-to-string\": \"npm:preact-render-to-string@^6.6.3\"\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/app.ts",
    "content": "import { trace } from \"@opentelemetry/api\";\n\nimport { DENO_DEPLOYMENT_ID } from \"@fresh/build-id\";\nimport * as colors from \"@std/fmt/colors\";\nimport {\n  type MaybeLazyMiddleware,\n  type Middleware,\n  runMiddlewares,\n} from \"./middlewares/mod.ts\";\nimport { Context } from \"./context.ts\";\nimport { mergePath, type Method, UrlPatternRouter } from \"./router.ts\";\nimport type { FreshConfig, ResolvedFreshConfig } from \"./config.ts\";\nimport type { BuildCache } from \"./build_cache.ts\";\nimport { HttpError } from \"./error.ts\";\nimport type { LayoutConfig, MaybeLazy, Route, RouteConfig } from \"./types.ts\";\nimport type { RouteComponent } from \"./segments.ts\";\nimport {\n  applyCommands,\n  type Command,\n  CommandType,\n  DEFAULT_NOT_ALLOWED_METHOD,\n  DEFAULT_NOT_FOUND,\n  newAppCmd,\n  newErrorCmd,\n  newHandlerCmd,\n  newLayoutCmd,\n  newMiddlewareCmd,\n  newNotFoundCmd,\n  newRouteCmd,\n} from \"./commands.ts\";\nimport { MockBuildCache } from \"./test_utils.ts\";\n\n// TODO: Completed type clashes in older Deno versions\n// deno-lint-ignore no-explicit-any\nexport const DEFAULT_CONN_INFO: any = {\n  localAddr: { transport: \"tcp\", hostname: \"localhost\", port: 8080 },\n  remoteAddr: { transport: \"tcp\", hostname: \"localhost\", port: 1234 },\n};\n\nconst defaultOptionsHandler = (methods: string[]): () => Promise<Response> => {\n  return () =>\n    Promise.resolve(\n      new Response(null, {\n        status: 204,\n        headers: { Allow: methods.join(\", \") },\n      }),\n    );\n};\n// deno-lint-ignore require-await\nconst DEFAULT_ERROR_HANDLER = async <State>(ctx: Context<State>) => {\n  const { error } = ctx;\n\n  if (error instanceof HttpError) {\n    if (error.status >= 500) {\n      // deno-lint-ignore no-console\n      console.error(error);\n    }\n    return new Response(error.message, { status: error.status });\n  }\n\n  // deno-lint-ignore no-console\n  console.error(error);\n  return new Response(\"Internal server error\", { status: 500 });\n};\n\nexport type ListenOptions =\n  & Partial<\n    Deno.ServeTcpOptions & Deno.TlsCertifiedKeyPem\n  >\n  & {\n    remoteAddress?: string;\n  };\nfunction createOnListen(\n  basePath: string,\n  options: ListenOptions,\n): (localAddr: Deno.NetAddr) => void {\n  return (params) => {\n    // Don't spam logs with this on live deployments\n    if (DENO_DEPLOYMENT_ID) return;\n\n    const pathname = basePath + \"/\";\n    const protocol = \"key\" in options && options.key && options.cert\n      ? \"https:\"\n      : \"http:\";\n\n    let hostname = params.hostname;\n    // Windows being windows...\n    if (\n      Deno.build.os === \"windows\" &&\n      (hostname === \"0.0.0.0\" || hostname === \"::\")\n    ) {\n      hostname = \"localhost\";\n    }\n    // Work around https://github.com/denoland/deno/issues/23650\n    hostname = hostname.startsWith(\"::\") ? `[${hostname}]` : hostname;\n\n    // deno-lint-ignore no-console\n    console.log();\n    // deno-lint-ignore no-console\n    console.log(\n      colors.bgRgb8(colors.rgb8(\" 🍋 Fresh ready   \", 0), 121),\n    );\n    const sep = options.remoteAddress ? \"\" : \"\\n\";\n    const space = options.remoteAddress ? \" \" : \"\";\n\n    const localLabel = colors.bold(\"Local:\");\n    const address = colors.cyan(\n      `${protocol}//${hostname}:${params.port}${pathname}`,\n    );\n    const helper = hostname === \"0.0.0.0\" || hostname === \"::\"\n      ? colors.cyan(` (${protocol}//localhost:${params.port}${pathname})`)\n      : \"\";\n    // deno-lint-ignore no-console\n    console.log(`    ${localLabel}  ${space}${address}${helper}${sep}`);\n    if (options.remoteAddress) {\n      const remoteLabel = colors.bold(\"Remote:\");\n      const remoteAddress = colors.cyan(options.remoteAddress);\n      // deno-lint-ignore no-console\n      console.log(`    ${remoteLabel}  ${remoteAddress}\\n`);\n    }\n  };\n}\n\nasync function listenOnFreePort(\n  options: ListenOptions,\n  handler: (\n    request: Request,\n    info?: Deno.ServeHandlerInfo,\n  ) => Promise<Response>,\n) {\n  // No port specified, check for a free port. Instead of picking just\n  // any port we'll check if the next one is free for UX reasons.\n  // That way the user only needs to increment a number when running\n  // multiple apps vs having to remember completely different ports.\n  let firstError = null;\n  for (let port = 8000; port < 8020; port++) {\n    try {\n      return await Deno.serve({ ...options, port }, handler);\n    } catch (err) {\n      if (err instanceof Deno.errors.AddrInUse) {\n        // Throw first EADDRINUSE error if no port is free\n        if (!firstError) firstError = err;\n        continue;\n      }\n      throw err;\n    }\n  }\n  throw firstError;\n}\n\nexport let getBuildCache: <State>(app: App<State>) => BuildCache<State> | null;\nexport let setBuildCache: <State>(\n  app: App<State>,\n  cache: BuildCache<State>,\n  mode: \"development\" | \"production\",\n) => void;\nexport let setErrorInterceptor: <State>(\n  app: App<State>,\n  fn: (err: unknown) => void,\n) => void;\n\nconst NOOP = () => {};\n\n/**\n * Create an application instance that passes the incoming `Request`\n * instance through middlewares and routes.\n */\nexport class App<State> {\n  #getBuildCache: () => BuildCache<State> | null = () => null;\n  #commands: Command<State>[] = [];\n  #onError: (err: unknown) => void = NOOP;\n\n  static {\n    getBuildCache = (app) => app.#getBuildCache();\n    setBuildCache = (app, cache, mode: \"development\" | \"production\") => {\n      app.config.root = cache.root;\n      app.config.mode = mode;\n      app.#getBuildCache = () => cache;\n    };\n    setErrorInterceptor = (app, fn) => {\n      app.#onError = fn;\n    };\n  }\n\n  /**\n   * The final resolved Fresh configuration.\n   */\n  config: ResolvedFreshConfig;\n\n  constructor(config: FreshConfig = {}) {\n    this.config = {\n      root: \".\",\n      basePath: config.basePath ?? \"\",\n      mode: config.mode ?? \"production\",\n    };\n  }\n\n  /**\n   * Add one or more middlewares at the top or the specified path.\n   */\n  use(...middleware: MaybeLazyMiddleware<State>[]): this;\n  use(path: string, ...middleware: MaybeLazyMiddleware<State>[]): this;\n  use(\n    pathOrMiddleware: string | MaybeLazyMiddleware<State>,\n    ...middlewares: MaybeLazyMiddleware<State>[]\n  ): this {\n    let pattern: string;\n    let fns: MaybeLazyMiddleware<State>[];\n    if (typeof pathOrMiddleware === \"string\") {\n      pattern = pathOrMiddleware;\n      fns = middlewares!;\n    } else {\n      pattern = \"*\";\n      middlewares.unshift(pathOrMiddleware);\n      fns = middlewares;\n    }\n\n    this.#commands.push(newMiddlewareCmd(pattern, fns, true));\n\n    return this;\n  }\n\n  /**\n   * Set the app's 404 error handler. Can be a {@linkcode Route} or a {@linkcode Middleware}.\n   */\n  notFound(routeOrMiddleware: Route<State> | Middleware<State>): this {\n    this.#commands.push(newNotFoundCmd(routeOrMiddleware));\n    return this;\n  }\n\n  onError(\n    path: string,\n    routeOrMiddleware: Route<State> | Middleware<State>,\n  ): this {\n    this.#commands.push(newErrorCmd(path, routeOrMiddleware, true));\n    return this;\n  }\n\n  appWrapper(component: RouteComponent<State>): this {\n    this.#commands.push(newAppCmd(component));\n    return this;\n  }\n\n  layout(\n    path: string,\n    component: RouteComponent<State>,\n    config?: LayoutConfig,\n  ): this {\n    this.#commands.push(newLayoutCmd(path, component, config, true));\n    return this;\n  }\n\n  route(\n    path: string,\n    route: MaybeLazy<Route<State>>,\n    config?: RouteConfig,\n  ): this {\n    this.#commands.push(newRouteCmd(path, route, config, false));\n    return this;\n  }\n\n  /**\n   * Add middlewares for GET requests at the specified path.\n   */\n  get(path: string, ...middlewares: MaybeLazy<Middleware<State>>[]): this {\n    this.#commands.push(newHandlerCmd(\"GET\", path, middlewares, false));\n    return this;\n  }\n  /**\n   * Add middlewares for POST requests at the specified path.\n   */\n  post(path: string, ...middlewares: MaybeLazy<Middleware<State>>[]): this {\n    this.#commands.push(newHandlerCmd(\"POST\", path, middlewares, false));\n    return this;\n  }\n  /**\n   * Add middlewares for PATCH requests at the specified path.\n   */\n  patch(path: string, ...middlewares: MaybeLazy<Middleware<State>>[]): this {\n    this.#commands.push(newHandlerCmd(\"PATCH\", path, middlewares, false));\n    return this;\n  }\n  /**\n   * Add middlewares for PUT requests at the specified path.\n   */\n  put(path: string, ...middlewares: MaybeLazy<Middleware<State>>[]): this {\n    this.#commands.push(newHandlerCmd(\"PUT\", path, middlewares, false));\n    return this;\n  }\n  /**\n   * Add middlewares for DELETE requests at the specified path.\n   */\n  delete(path: string, ...middlewares: MaybeLazy<Middleware<State>>[]): this {\n    this.#commands.push(newHandlerCmd(\"DELETE\", path, middlewares, false));\n    return this;\n  }\n  /**\n   * Add middlewares for HEAD requests at the specified path.\n   */\n  head(path: string, ...middlewares: MaybeLazy<Middleware<State>>[]): this {\n    this.#commands.push(newHandlerCmd(\"HEAD\", path, middlewares, false));\n    return this;\n  }\n\n  /**\n   * Add middlewares for all HTTP verbs at the specified path.\n   */\n  all(path: string, ...middlewares: MaybeLazy<Middleware<State>>[]): this {\n    this.#commands.push(newHandlerCmd(\"ALL\", path, middlewares, false));\n    return this;\n  }\n\n  /**\n   * Insert file routes collected in {@linkcode Builder} at this point.\n   * @param pattern Append file routes at this pattern instead of the root\n   * @returns\n   */\n  fsRoutes(pattern = \"*\"): this {\n    this.#commands.push({\n      type: CommandType.FsRoute,\n      pattern,\n      getItems: () => {\n        const buildCache = this.#getBuildCache();\n        if (buildCache === null) return [];\n        return buildCache.getFsRoutes();\n      },\n      includeLastSegment: false,\n    });\n    return this;\n  }\n\n  /**\n   * Merge another {@linkcode App} instance into this app at the\n   * specified path.\n   */\n  mountApp(path: string, app: App<State>): this {\n    for (let i = 0; i < app.#commands.length; i++) {\n      const cmd = app.#commands[i];\n\n      if (cmd.type !== CommandType.App && cmd.type !== CommandType.NotFound) {\n        // Apply the inner app's basePath if it exists\n        let effectivePattern = cmd.pattern;\n        if (app.config.basePath) {\n          effectivePattern = mergePath(app.config.basePath, cmd.pattern, false);\n        }\n\n        const clone = {\n          ...cmd,\n          pattern: mergePath(path, effectivePattern, true),\n          includeLastSegment: cmd.pattern === \"/\" || cmd.includeLastSegment,\n        };\n        this.#commands.push(clone);\n        continue;\n      }\n\n      this.#commands.push(cmd);\n    }\n\n    // deno-lint-ignore no-this-alias\n    const self = this;\n    app.#getBuildCache = () => self.#getBuildCache();\n\n    return this;\n  }\n\n  /**\n   * Create handler function for `Deno.serve` or to be used in\n   * testing.\n   */\n  handler(): (\n    request: Request,\n    info?: Deno.ServeHandlerInfo,\n  ) => Promise<Response> {\n    let buildCache = this.#getBuildCache();\n    if (buildCache === null) {\n      if (\n        this.config.mode === \"production\" &&\n        DENO_DEPLOYMENT_ID !== undefined\n      ) {\n        throw new Error(\n          `Could not find _fresh directory. Maybe you forgot to run \"deno task build\" or maybe you're trying to run \"main.ts\" directly instead of \"_fresh/server.js\"?`,\n        );\n      } else {\n        buildCache = new MockBuildCache([], this.config.mode);\n      }\n    }\n\n    const router = new UrlPatternRouter<MaybeLazyMiddleware<State>>();\n\n    const { rootMiddlewares } = applyCommands(\n      router,\n      this.#commands,\n      this.config.basePath,\n    );\n\n    return async (\n      req: Request,\n      conn: Deno.ServeHandlerInfo = DEFAULT_CONN_INFO,\n    ) => {\n      const url = new URL(req.url);\n      // Prevent open redirect attacks\n      url.pathname = url.pathname.replace(/\\/+/g, \"/\");\n\n      const method = req.method.toUpperCase() as Method;\n      const matched = router.match(method, url);\n      let { params, pattern, handlers, methodMatch } = matched;\n\n      const span = trace.getActiveSpan();\n      if (span && pattern) {\n        span.updateName(`${method} ${pattern}`);\n        span.setAttribute(\"http.route\", pattern);\n      }\n\n      let next: () => Promise<Response>;\n\n      if (pattern === null || !methodMatch) {\n        handlers = rootMiddlewares;\n      }\n\n      if (matched.pattern !== null && !methodMatch) {\n        if (method === \"OPTIONS\") {\n          const allowed = router.getAllowedMethods(matched.pattern);\n          next = defaultOptionsHandler(allowed);\n        } else {\n          next = DEFAULT_NOT_ALLOWED_METHOD;\n        }\n      } else {\n        next = DEFAULT_NOT_FOUND;\n      }\n\n      const ctx = new Context<State>(\n        req,\n        url,\n        conn,\n        matched.pattern,\n        params,\n        this.config,\n        next,\n        buildCache!,\n      );\n\n      try {\n        if (handlers.length === 0) return await next();\n\n        const result = await runMiddlewares(handlers, ctx, this.#onError);\n        if (!(result instanceof Response)) {\n          throw new Error(\n            `Expected a \"Response\" instance to be returned, but got: ${result}`,\n          );\n        }\n\n        if (method === \"HEAD\") {\n          return new Response(null, result);\n        }\n\n        return result;\n      } catch (err) {\n        ctx.error = err;\n        return await DEFAULT_ERROR_HANDLER(ctx);\n      }\n    };\n  }\n\n  /**\n   * Spawn a server for this app.\n   */\n  async listen(options: ListenOptions = {}): Promise<void> {\n    if (!options.onListen) {\n      options.onListen = createOnListen(this.config.basePath, options);\n    }\n\n    const handler = this.handler();\n    if (options.port) {\n      await Deno.serve(options, handler);\n      return;\n    }\n\n    await listenOnFreePort(options, handler);\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/app_test.tsx",
    "content": "import { expect } from \"@std/expect\";\nimport { App } from \"./app.ts\";\nimport { FakeServer } from \"./test_utils.ts\";\nimport { HttpError } from \"./error.ts\";\n\nDeno.test(\"App - .use()\", async () => {\n  const app = new App<{ text: string }>()\n    .use((ctx) => {\n      ctx.state.text = \"A\";\n      return ctx.next();\n    })\n    .use((ctx) => {\n      ctx.state.text += \"B\";\n      return ctx.next();\n    })\n    .get(\"/\", (ctx) => new Response(ctx.state.text));\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"AB\");\n});\n\nDeno.test(\"App - .use() #2\", async () => {\n  const app = new App<{ text: string }>()\n    .use(() => new Response(\"ok #1\"))\n    .get(\"/foo/bar\", () => new Response(\"ok #2\"))\n    .get(\"/\", () => new Response(\"ok #3\"));\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok #1\");\n});\n\nDeno.test(\"App - .get()\", async () => {\n  const app = new App()\n    .get(\"/\", () => new Response(\"ok\"))\n    .get(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.get(\"/foo\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - .get() with basePath\", async () => {\n  const app = new App({ basePath: \"/foo/bar\" })\n    .get(\"/\", () => new Response(\"ok\"))\n    .get(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/\");\n  expect(res.status).toEqual(404);\n  res = await server.get(\"/foo\");\n  expect(res.status).toEqual(404);\n\n  res = await server.get(\"/foo/bar\");\n  expect(res.status).toEqual(200);\n  res = await server.get(\"/foo/bar/foo\");\n  expect(res.status).toEqual(200);\n});\n\nDeno.test(\"App - .post()\", async () => {\n  const app = new App<{ text: string }>()\n    .get(\"/\", () => new Response(\"fail\"))\n    .get(\"/foo\", () => new Response(\"fail\"))\n    .post(\"/\", () => new Response(\"ok\"))\n    .post(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.post(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.post(\"/foo\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - .post() with basePath\", async () => {\n  const app = new App({ basePath: \"/foo/bar\" })\n    .post(\"/\", () => new Response(\"ok\"))\n    .post(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.post(\"/\");\n  expect(res.status).toEqual(404);\n  res = await server.post(\"/foo\");\n  expect(res.status).toEqual(404);\n\n  res = await server.post(\"/foo/bar\");\n  expect(res.status).toEqual(200);\n  res = await server.post(\"/foo/bar/foo\");\n  expect(res.status).toEqual(200);\n});\n\nDeno.test(\"App - .patch()\", async () => {\n  const app = new App<{ text: string }>()\n    .get(\"/\", () => new Response(\"fail\"))\n    .get(\"/foo\", () => new Response(\"fail\"))\n    .patch(\"/\", () => new Response(\"ok\"))\n    .patch(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.patch(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.patch(\"/foo\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - .patch() with basePath\", async () => {\n  const app = new App({ basePath: \"/foo/bar\" })\n    .patch(\"/\", () => new Response(\"ok\"))\n    .patch(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.patch(\"/\");\n  expect(res.status).toEqual(404);\n  res = await server.patch(\"/foo\");\n  expect(res.status).toEqual(404);\n\n  res = await server.patch(\"/foo/bar\");\n  expect(res.status).toEqual(200);\n  res = await server.patch(\"/foo/bar/foo\");\n  expect(res.status).toEqual(200);\n});\n\nDeno.test(\"App - .put()\", async () => {\n  const app = new App<{ text: string }>()\n    .get(\"/\", () => new Response(\"fail\"))\n    .get(\"/foo\", () => new Response(\"fail\"))\n    .put(\"/\", () => new Response(\"ok\"))\n    .put(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.put(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.put(\"/foo\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - .put() with basePath\", async () => {\n  const app = new App({ basePath: \"/foo/bar\" })\n    .put(\"/\", () => new Response(\"ok\"))\n    .put(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.put(\"/\");\n  expect(res.status).toEqual(404);\n  res = await server.put(\"/foo\");\n  expect(res.status).toEqual(404);\n\n  res = await server.put(\"/foo/bar\");\n  expect(res.status).toEqual(200);\n  res = await server.put(\"/foo/bar/foo\");\n  expect(res.status).toEqual(200);\n});\n\nDeno.test(\"App - .delete()\", async () => {\n  const app = new App<{ text: string }>()\n    .get(\"/\", () => new Response(\"fail\"))\n    .get(\"/foo\", () => new Response(\"fail\"))\n    .delete(\"/\", () => new Response(\"ok\"))\n    .delete(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.delete(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.delete(\"/foo\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - .delete() with basePath\", async () => {\n  const app = new App({ basePath: \"/foo/bar\" })\n    .delete(\"/\", () => new Response(\"ok\"))\n    .delete(\"/foo\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.delete(\"/\");\n  expect(res.status).toEqual(404);\n  res = await server.delete(\"/foo\");\n  expect(res.status).toEqual(404);\n\n  res = await server.delete(\"/foo/bar\");\n  expect(res.status).toEqual(200);\n  res = await server.delete(\"/foo/bar/foo\");\n  expect(res.status).toEqual(200);\n});\n\nDeno.test(\"App - wrong method match\", async () => {\n  const app = new App<{ text: string }>()\n    .get(\"/\", () => new Response(\"ok\"))\n    .post(\"/\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.put(\"/\");\n  expect(res.status).toEqual(405);\n  expect(await res.text()).toEqual(\"Method Not Allowed\");\n\n  res = await server.post(\"/\");\n  expect(res.status).toEqual(200);\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - methods with middleware\", async () => {\n  const app = new App<{ text: string }>()\n    .use((ctx) => {\n      ctx.state.text = \"A\";\n      return ctx.next();\n    })\n    .get(\"/\", (ctx) => new Response(ctx.state.text))\n    .post(\"/\", (ctx) => new Response(ctx.state.text));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"A\");\n\n  res = await server.post(\"/\");\n  expect(await res.text()).toEqual(\"A\");\n});\n\nDeno.test(\"App - .mountApp() compose apps\", async () => {\n  const innerApp = new App<{ text: string }>()\n    .use((ctx) => {\n      ctx.state.text = \"A\";\n      return ctx.next();\n    })\n    .get(\"/\", (ctx) => new Response(ctx.state.text))\n    .post(\"/\", (ctx) => new Response(ctx.state.text));\n\n  const app = new App<{ text: string }>()\n    .get(\"/\", () => new Response(\"ok\"))\n    .mountApp(\"/foo\", innerApp);\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.get(\"/foo\");\n  expect(await res.text()).toEqual(\"A\");\n\n  res = await server.post(\"/foo\");\n  expect(await res.text()).toEqual(\"A\");\n});\n\nDeno.test(\"App - .mountApp() compose apps with .route()\", async () => {\n  const innerApp = new App<{ text: string }>()\n    .use((ctx) => {\n      ctx.state.text = \"A\";\n      return ctx.next();\n    })\n    .route(\"/\", { handler: (ctx) => new Response(ctx.state.text) });\n\n  const app = new App<{ text: string }>()\n    .get(\"/\", () => new Response(\"ok\"))\n    .mountApp(\"/foo\", innerApp);\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.get(\"/foo\");\n  expect(await res.text()).toEqual(\"A\");\n});\n\nDeno.test(\"App - .mountApp() self mount, no middleware\", async () => {\n  const innerApp = new App<{ text: string }>()\n    .use((ctx) => {\n      ctx.state.text = \"A\";\n      return ctx.next();\n    })\n    .get(\"/foo\", (ctx) => new Response(ctx.state.text))\n    .post(\"/foo\", (ctx) => new Response(ctx.state.text));\n\n  const app = new App<{ text: string }>()\n    .get(\"/\", () => new Response(\"ok\"))\n    .mountApp(\"/\", innerApp);\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.get(\"/foo\");\n  expect(await res.text()).toEqual(\"A\");\n\n  res = await server.post(\"/foo\");\n  expect(await res.text()).toEqual(\"A\");\n});\n\nDeno.test(\n  \"App - .mountApp() self mount, with middleware\",\n  async () => {\n    const innerApp = new App<{ text: string }>()\n      .use(function B(ctx) {\n        ctx.state.text += \"B\";\n        return ctx.next();\n      })\n      .get(\"/foo\", (ctx) => new Response(ctx.state.text))\n      .post(\"/foo\", (ctx) => new Response(ctx.state.text));\n\n    const app = new App<{ text: string }>()\n      .use(function A(ctx) {\n        ctx.state.text = \"A\";\n        return ctx.next();\n      })\n      .get(\"/\", () => new Response(\"ok\"))\n      .mountApp(\"/\", innerApp);\n\n    const server = new FakeServer(app.handler());\n\n    let res = await server.get(\"/\");\n    expect(await res.text()).toEqual(\"ok\");\n\n    res = await server.get(\"/foo\");\n    expect(await res.text()).toEqual(\"AB\");\n\n    res = await server.post(\"/foo\");\n    expect(await res.text()).toEqual(\"AB\");\n  },\n);\n\nDeno.test(\n  \"App - .mountApp() self mount, different order\",\n  async () => {\n    const innerApp = new App<{ text: string }>()\n      .use(function B(ctx) {\n        ctx.state.text += \"B\";\n        return ctx.next();\n      })\n      .get(\"/foo\", (ctx) => new Response(ctx.state.text))\n      .post(\"/foo\", (ctx) => new Response(ctx.state.text));\n\n    const app = new App<{ text: string }>()\n      .use(function A(ctx) {\n        ctx.state.text = \"A\";\n        return ctx.next();\n      })\n      .get(\"/\", () => new Response(\"ok\"))\n      .mountApp(\"/\", innerApp);\n\n    const server = new FakeServer(app.handler());\n\n    let res = await server.get(\"/\");\n    expect(await res.text()).toEqual(\"ok\");\n\n    res = await server.get(\"/foo\");\n    expect(await res.text()).toEqual(\"AB\");\n\n    res = await server.post(\"/foo\");\n    expect(await res.text()).toEqual(\"AB\");\n  },\n);\n\nDeno.test(\"App - .mountApp() self mount empty\", async () => {\n  const innerApp = new App<{ text: string }>()\n    .use((ctx) => {\n      ctx.state.text = \"A\";\n      return ctx.next();\n    })\n    .get(\"/foo\", (ctx) => new Response(ctx.state.text));\n\n  const app = new App<{ text: string }>()\n    .mountApp(\"/\", innerApp);\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/foo\");\n  expect(await res.text()).toEqual(\"A\");\n});\n\nDeno.test(\n  \"App - .mountApp() self mount with middleware\",\n  async () => {\n    const innerApp = new App<{ text: string }>()\n      .use(function Inner(ctx) {\n        ctx.state.text += \"_Inner\";\n        return ctx.next();\n      })\n      .get(\"/\", (ctx) => new Response(ctx.state.text));\n\n    const app = new App<{ text: string }>()\n      .use(function Outer(ctx) {\n        ctx.state.text = \"Outer\";\n        return ctx.next();\n      })\n      .mountApp(\"/\", innerApp);\n\n    const server = new FakeServer(app.handler());\n\n    const res = await server.get(\"/\");\n    expect(await res.text()).toEqual(\"Outer_Inner\");\n  },\n);\n\n// https://github.com/denoland/fresh/issues/3033\nDeno.test(\n  \"App - .mountApp() self mount with dynamic routes\",\n  async () => {\n    const innerApp = new App<{ text: string }>()\n      .get(\"/\", () => new Response(\"list authors\"))\n      .get(\"/:name\", (ctx) => new Response(`show: ${ctx.params.name}`));\n\n    const app = new App<{ text: string }>()\n      .mountApp(\"/api/authors\", innerApp);\n\n    const server = new FakeServer(app.handler());\n\n    let res = await server.get(\"/api/authors\");\n    expect(await res.text()).toEqual(\"list authors\");\n\n    res = await server.get(\"/api/authors/foo\");\n    expect(await res.text()).toEqual(\"show: foo\");\n  },\n);\n\nDeno.test(\"App - .mountApp() fallback route\", async () => {\n  let called = \"\";\n  const innerApp = new App<{ text: string }>()\n    .use(function Inner(ctx) {\n      called += \"_Inner\";\n      return ctx.next();\n    })\n    .get(\"/\", (ctx) => new Response(ctx.state.text));\n\n  const app = new App<{ text: string }>()\n    .use(function Outer(ctx) {\n      called += \"Outer\";\n      return ctx.next();\n    })\n    .mountApp(\"/\", innerApp);\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/invalid\");\n  await res.body?.cancel();\n  expect(called).toEqual(\"Outer_Inner\");\n});\n\nDeno.test(\"App - catches errors\", async () => {\n  let thrownErr: unknown | null = null;\n  const app = new App<{ text: string }>()\n    .use(async (ctx) => {\n      ctx.state.text = \"A\";\n      try {\n        return await ctx.next();\n      } catch (err) {\n        thrownErr = err;\n        throw err;\n      }\n    })\n    .get(\"/\", () => {\n      throw new Error(\"fail\");\n    });\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(res.status).toEqual(500);\n  expect(thrownErr).toBeInstanceOf(Error);\n});\n\nDeno.test(\"App - sets error on context\", async () => {\n  const thrown: [unknown, unknown][] = [];\n  const app = new App()\n    .use(async (ctx) => {\n      try {\n        return await ctx.next();\n      } catch (err) {\n        thrown.push([err, ctx.error]);\n        throw err;\n      }\n    })\n    .use(async (ctx) => {\n      try {\n        return await ctx.next();\n      } catch (err) {\n        thrown.push([err, ctx.error]);\n        throw err;\n      }\n    })\n    .get(\"/\", () => {\n      throw \"<mock error>\";\n    });\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  await res.body?.cancel();\n  expect(thrown.length).toEqual(2);\n  expect(thrown[0][0]).toEqual(thrown[0][1]);\n  expect(thrown[1][0]).toEqual(thrown[1][1]);\n});\n\nDeno.test(\"App - support setting request init in ctx.render()\", async () => {\n  const app = new App<{ text: string }>()\n    .get(\"/\", (ctx) => {\n      return ctx.render(<div>ok</div>, {\n        status: 416,\n        headers: { \"X-Foo\": \"foo\" },\n      });\n    });\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(416);\n  expect(res.headers.get(\"X-Foo\")).toEqual(\"foo\");\n});\n\nDeno.test(\"App - throw when middleware returns no response\", async () => {\n  const app = new App<{ text: string }>()\n    .get(\n      \"/\",\n      // deno-lint-ignore no-explicit-any\n      (() => {}) as any,\n    );\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n  const text = await res.text();\n  expect(res.status).toEqual(500);\n  expect(text).toContain(\"Internal server error\");\n});\n\nDeno.test(\"App - overwrite default 404 handler\", async () => {\n  const app = new App()\n    .notFound(() => new Response(\"bar\", { status: 404 }))\n    .get(\"/foo\", () => new Response(\"foo\"))\n    .get(\"/thrower\", () => {\n      throw new HttpError(404);\n    });\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/invalid\");\n  expect(await res.text()).toEqual(\"bar\");\n\n  res = await server.get(\"/thrower\");\n  expect(await res.text()).toEqual(\"bar\");\n});\n\nDeno.test(\"App - uses error route\", async () => {\n  const app = new App()\n    .onError(\"*\", () => new Response(\"error route\", { status: 200 }))\n    .get(\"/thrower\", () => {\n      throw new HttpError(404);\n    });\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/invalid\");\n  expect(await res.text()).toEqual(\"error route\");\n\n  res = await server.get(\"/thrower\");\n  expect(await res.text()).toEqual(\"error route\");\n});\n\nDeno.test(\"App - uses notFound route on 404\", async () => {\n  const app = new App()\n    .notFound(() => new Response(\"not found route\", { status: 404 }))\n    .onError(\"*\", () => new Response(\"error route\", { status: 500 }))\n    .get(\"/thrower\", () => {\n      throw new HttpError(404);\n    })\n    .get(\"/thrower_2\", () => {\n      throw new HttpError(500);\n    });\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/invalid\");\n  expect(await res.text()).toEqual(\"not found route\");\n\n  res = await server.get(\"/thrower\");\n  expect(await res.text()).toEqual(\"not found route\");\n\n  res = await server.get(\"/thrower_2\");\n  expect(await res.text()).toEqual(\"error route\");\n});\n\n// Issue: https://github.com/denoland/fresh/issues/3115\nDeno.test(\"App - .route() with basePath\", async () => {\n  const app = new App({ basePath: \"/foo/bar\" })\n    .route(\"/\", { handler: () => new Response(\"ok\") });\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.delete(\"/\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(404);\n\n  res = await server.delete(\"/foo\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(404);\n\n  res = await server.delete(\"/foo/bar\");\n  expect(await res.text()).toEqual(\"ok\");\n  expect(res.status).toEqual(200);\n});\n\nDeno.test(\"App - .all() with *\", async () => {\n  const app = new App()\n    .all(\"*\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - .route() with *\", async () => {\n  const app = new App()\n    .route(\"*\", { handler: () => new Response(\"ok\") });\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - .route() handler returns headers\", async () => {\n  const app = new App()\n    .route(\"/\", {\n      handler: () => {\n        const headers = new Headers();\n        headers.set(\"X-Foo\", \"foo\");\n        return { data: {}, headers };\n      },\n      component() {\n        return <h1>foo</h1>;\n      },\n    })\n    .route(\"/obj\", {\n      handler: () => {\n        return { data: {}, headers: { \"X-Foo\": \"foo\" } };\n      },\n      component() {\n        return <h1>foo</h1>;\n      },\n    })\n    .route(\"/arr\", {\n      handler: () => {\n        return { data: {}, headers: [[\"X-Foo\", \"foo\"]] };\n      },\n      component() {\n        return <h1>foo</h1>;\n      },\n    });\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/\");\n  expect(await res.text()).toContain(\"<h1>foo</h1>\");\n  expect(res.headers.get(\"X-Foo\")).toEqual(\"foo\");\n\n  res = await server.get(\"/obj\");\n  expect(await res.text()).toContain(\"<h1>foo</h1>\");\n  expect(res.headers.get(\"X-Foo\")).toEqual(\"foo\");\n\n  res = await server.get(\"/arr\");\n  expect(await res.text()).toContain(\"<h1>foo</h1>\");\n  expect(res.headers.get(\"X-Foo\")).toEqual(\"foo\");\n});\n\nDeno.test(\"App - .use() - lazy\", async () => {\n  const app = new App<{ text: string }>()\n    // deno-lint-ignore require-await\n    .use(async () => {\n      return (ctx) => {\n        ctx.state.text = \"ok\";\n        return ctx.next();\n      };\n    })\n    .get(\"/\", (ctx) => new Response(ctx.state.text));\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - .route() - lazy\", async () => {\n  const app = new App()\n    // deno-lint-ignore require-await\n    .route(\"/\", async () => {\n      return { handler: () => new Response(\"ok\") };\n    });\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"App - .get/post/patch/put/delete/head/all() - lazy\", async () => {\n  const app = new App()\n    .get(\"/\", () => Promise.resolve(() => new Response(\"ok\")))\n    .post(\"/\", () => Promise.resolve(() => new Response(\"ok\")))\n    .patch(\"/\", () => Promise.resolve(() => new Response(\"ok\")))\n    .delete(\"/\", () => Promise.resolve(() => new Response(\"ok\")))\n    .put(\"/\", () => Promise.resolve(() => new Response(\"ok\")))\n    .head(\"/\", () => Promise.resolve(() => new Response(\"ok\")))\n    .all(\"/\", () => Promise.resolve(() => new Response(\"ok\")));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/\");\n  expect(res.status).toEqual(200);\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.post(\"/\");\n  expect(res.status).toEqual(200);\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.put(\"/\");\n  expect(res.status).toEqual(200);\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.patch(\"/\");\n  expect(res.status).toEqual(200);\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.delete(\"/\");\n  expect(res.status).toEqual(200);\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.head(\"/\");\n  expect(res.status).toEqual(200);\n  expect(res.body).toEqual(null);\n});\n\nDeno.test(\"App prefer .head over .get\", async () => {\n  const app = new App()\n    .get(\n      \"/\",\n      () => Promise.resolve(() => new Response(\"not ok\", { status: 500 })),\n    )\n    .head(\"/\", () => Promise.resolve(() => new Response(\"ok\")))\n    .get(\n      \"/:id\",\n      () => Promise.resolve(() => new Response(\"not ok\", { status: 500 })),\n    )\n    .head(\"/:id\", () => Promise.resolve(() => new Response(\"ok\")));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.head(\"/\");\n  expect(res.body).toEqual(null);\n  expect(res.status).toEqual(200);\n\n  res = await server.head(\"/foo\");\n  expect(res.body).toEqual(null);\n  expect(res.status).toEqual(200);\n});\n\nDeno.test(\"App support HEAD if only GET is registered\", async () => {\n  const app = new App()\n    .get(\"/\", () => Promise.resolve(() => new Response(\"ok\")))\n    .get(\"/:id\", () => Promise.resolve(() => new Response(\"ok\")));\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.head(\"/\");\n  expect(res.body).toEqual(null);\n  expect(res.status).toEqual(200);\n\n  res = await server.head(\"/foo\");\n  expect(res.body).toEqual(null);\n  expect(res.status).toEqual(200);\n});\n\nDeno.test(\n  \"App support HEAD in .route() when only GET is registered\",\n  async () => {\n    const app = new App()\n      .route(\"/\", {\n        handler: {\n          GET() {\n            return { data: {} };\n          },\n        },\n        component: () => <h1>foo</h1>,\n      });\n\n    const server = new FakeServer(app.handler());\n\n    const res = await server.head(\"/\");\n    expect(res.body).toEqual(null);\n    expect(res.status).toEqual(200);\n  },\n);\n\nDeno.test(\"App - .appWrapper()\", async () => {\n  const app = new App()\n    .appWrapper(({ Component }) => (\n      <>\n        app/<Component />\n      </>\n    ))\n    .get(\"/\", (ctx) => ctx.render(<>index</>));\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toContain(\"<body>app/index<\");\n});\n\nDeno.test.ignore(\"App - .layout()\", async () => {\n  const app = new App()\n    .layout(\"/\", ({ Component }) => (\n      <>\n        layout/<Component />\n      </>\n    ))\n    .layout(\"/foo\", ({ Component }) => (\n      <>\n        foo/<Component />\n      </>\n    ))\n    .get(\"/foo\", (ctx) => ctx.render(<>index</>));\n\n  const server = new FakeServer(app.handler());\n\n  // The `/foo` layout is not applied for GET `/foo`, is that correct?\n  const res = await server.get(\"/foo\");\n  expect(await res.text()).toContain(\"<body>layout/foo/index<\");\n});\n\nDeno.test(\"App - .appWrapper() + .layout()\", async () => {\n  const app = new App()\n    .appWrapper(({ Component }) => (\n      <>\n        app/<Component />\n      </>\n    ))\n    .layout(\"/\", ({ Component }) => (\n      <>\n        layout/<Component />\n      </>\n    ))\n    .get(\"/\", (ctx) => ctx.render(<>index</>));\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toContain(\"<body>app/layout/index<\");\n});\n\nDeno.test(\"App - ctx.render() without app wrapper or layout\", async () => {\n  const app = new App()\n    .appWrapper(({ Component }) => (\n      <>\n        app/<Component />\n      </>\n    ))\n    .layout(\"/\", ({ Component }) => (\n      <>\n        layout/<Component />\n      </>\n    ))\n    .get(\"/\", (ctx) =>\n      ctx.render(<>index</>, undefined, {\n        skipAppWrapper: true,\n        skipInheritedLayouts: true,\n      }));\n\n  const server = new FakeServer(app.handler());\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toContain(\"<body>index<\");\n});\n\nDeno.test(\"App - .mountApp() with basePath\", async () => {\n  const innerApp = new App({ basePath: \"/api\" })\n    .get(\"/users\", () => new Response(\"users\"));\n\n  const app = new App()\n    .get(\"/\", () => new Response(\"home\"))\n    .mountApp(\"/v1\", innerApp);\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"home\");\n\n  // Fixed behavior: inner app's basePath is now applied\n  res = await server.get(\"/v1/api/users\");\n  expect(await res.text()).toEqual(\"users\");\n\n  // Routes without the full basePath should not work\n  res = await server.get(\"/v1/users\");\n  expect(res.status).toEqual(404);\n});\n\nDeno.test(\"App - .mountApp() with main app basePath\", async () => {\n  const innerApp = new App()\n    .get(\"/data\", () => new Response(\"data\"));\n\n  const app = new App({ basePath: \"/main\" })\n    .get(\"/\", () => new Response(\"home\"))\n    .mountApp(\"/sub\", innerApp);\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/main\");\n  expect(await res.text()).toEqual(\"home\");\n\n  res = await server.get(\"/main/sub/data\");\n  expect(await res.text()).toEqual(\"data\");\n\n  // Should not work without main basePath\n  res = await server.get(\"/sub/data\");\n  expect(res.status).toEqual(404);\n});\n\nDeno.test(\"App - .mountApp() with both main and inner basePath\", async () => {\n  const innerApp = new App({ basePath: \"/api/v2\" })\n    .get(\"/users\", () => new Response(\"users\"))\n    .get(\"/posts\", () => new Response(\"posts\"));\n\n  const app = new App({ basePath: \"/main\" })\n    .get(\"/\", () => new Response(\"home\"))\n    .mountApp(\"/services\", innerApp);\n\n  const server = new FakeServer(app.handler());\n\n  let res = await server.get(\"/main\");\n  expect(await res.text()).toEqual(\"home\");\n\n  // Both basePaths should be applied: main basePath + mount path + inner basePath\n  res = await server.get(\"/main/services/api/v2/users\");\n  expect(await res.text()).toEqual(\"users\");\n\n  res = await server.get(\"/main/services/api/v2/posts\");\n  expect(await res.text()).toEqual(\"posts\");\n\n  // Partial paths should not work\n  res = await server.get(\"/services/api/v2/users\");\n  expect(res.status).toEqual(404);\n\n  res = await server.get(\"/main/services/users\");\n  expect(res.status).toEqual(404);\n});\n"
  },
  {
    "path": "packages/fresh/src/build_cache.ts",
    "content": "import * as path from \"@std/path\";\nimport type { Command } from \"./commands.ts\";\nimport { fsItemsToCommands, type FsRouteFile } from \"./fs_routes.ts\";\nimport type { ServerIslandRegistry } from \"./context.ts\";\nimport type { AnyComponent } from \"preact\";\nimport { UniqueNamer } from \"./utils.ts\";\nimport { setBuildId } from \"@fresh/build-id\";\n\nexport interface FileSnapshot {\n  name: string;\n  filePath: string;\n  hash: string | null;\n  contentType: string;\n}\n\nexport interface BuildSnapshot<State> {\n  version: string;\n  clientEntry: string;\n  fsRoutes: FsRouteFile<State>[];\n  staticFiles: Map<string, FileSnapshot>;\n  islands: ServerIslandRegistry;\n  entryAssets: string[];\n}\n\nexport interface StaticFile {\n  hash: string | null;\n  size: number;\n  contentType: string;\n  readable: ReadableStream<Uint8Array> | Uint8Array;\n  close(): void;\n}\n\n// deno-lint-ignore no-explicit-any\nexport interface BuildCache<State = any> {\n  root: string;\n  islandRegistry: ServerIslandRegistry;\n  clientEntry: string;\n  features: {\n    errorOverlay: boolean;\n  };\n  getFsRoutes(): Command<State>[];\n  readFile(pathname: string): Promise<StaticFile | null>;\n  getEntryAssets(): string[];\n}\n\nexport class ProdBuildCache<State> implements BuildCache<State> {\n  #snapshot: BuildSnapshot<State>;\n  islandRegistry: ServerIslandRegistry;\n  clientEntry: string;\n  features = { errorOverlay: false };\n\n  constructor(public root: string, snapshot: BuildSnapshot<State>) {\n    setBuildId(snapshot.version);\n    this.#snapshot = snapshot;\n    this.islandRegistry = snapshot.islands;\n    this.clientEntry = snapshot.clientEntry;\n  }\n\n  getEntryAssets(): string[] {\n    return this.#snapshot.entryAssets;\n  }\n\n  getFsRoutes(): Command<State>[] {\n    return fsItemsToCommands(this.#snapshot.fsRoutes);\n  }\n\n  async readFile(pathname: string): Promise<StaticFile | null> {\n    const { staticFiles } = this.#snapshot;\n\n    const info = staticFiles.get(pathname);\n    if (info === undefined) return null;\n\n    const filePath = path.isAbsolute(info.filePath)\n      ? info.filePath\n      : path.join(this.root, info.filePath);\n\n    const [stat, file] = await Promise.all([\n      Deno.stat(filePath),\n      Deno.open(filePath),\n    ]);\n\n    return {\n      hash: info.hash,\n      contentType: info.contentType,\n      size: stat.size,\n      readable: file.readable,\n      close: () => file.close(),\n    };\n  }\n}\n\nexport class IslandPreparer {\n  #namer = new UniqueNamer();\n\n  prepare(\n    registry: ServerIslandRegistry,\n    mod: Record<string, unknown>,\n    chunkName: string,\n    modName: string,\n    css: string[],\n  ) {\n    for (const [name, value] of Object.entries(mod)) {\n      if (typeof value !== \"function\") continue;\n\n      const islandName = name === \"default\" ? modName : name;\n      const uniqueName = this.#namer.getUniqueName(islandName);\n\n      const fn = value as AnyComponent;\n      registry.set(fn, {\n        exportName: name,\n        file: chunkName,\n        fn,\n        name: uniqueName,\n        css,\n      });\n    }\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/commands.ts",
    "content": "import { setAdditionalStyles } from \"./context.ts\";\nimport { HttpError } from \"./error.ts\";\nimport { isHandlerByMethod, type PageResponse } from \"./handlers.ts\";\nimport type { MaybeLazyMiddleware, Middleware } from \"./middlewares/mod.ts\";\nimport { mergePath, type Method, type Router, toRoutePath } from \"./router.ts\";\nimport {\n  getOrCreateSegment,\n  newSegment,\n  renderRoute,\n  type RouteComponent,\n  type Segment,\n  segmentToMiddlewares,\n} from \"./segments.ts\";\nimport type { LayoutConfig, MaybeLazy, Route, RouteConfig } from \"./types.ts\";\nimport { isLazy } from \"./utils.ts\";\n\nexport const DEFAULT_NOT_FOUND = (): Promise<Response> => {\n  throw new HttpError(404);\n};\nexport const DEFAULT_NOT_ALLOWED_METHOD = (): Promise<Response> => {\n  throw new HttpError(405);\n};\n\nconst DEFAULT_RENDER = <State>(): Promise<PageResponse<State>> =>\n  // deno-lint-ignore no-explicit-any\n  Promise.resolve({ data: {} as any });\n\nfunction ensureHandler<State>(route: Route<State>) {\n  if (route.handler === undefined) {\n    route.handler = route.component !== undefined\n      ? DEFAULT_RENDER\n      : DEFAULT_NOT_FOUND;\n  } else if (isHandlerByMethod(route.handler)) {\n    if (route.component !== undefined && !route.handler.GET) {\n      route.handler.GET = DEFAULT_RENDER;\n    }\n  }\n}\n\nexport const enum CommandType {\n  Middleware = \"middleware\",\n  Layout = \"layout\",\n  App = \"app\",\n  Route = \"route\",\n  Error = \"error\",\n  NotFound = \"notFound\",\n  Handler = \"handler\",\n  FsRoute = \"fsRoute\",\n}\n\nexport interface ErrorCmd<State> {\n  type: CommandType.Error;\n  pattern: string;\n  item: Route<State>;\n  includeLastSegment: boolean;\n}\nexport function newErrorCmd<State>(\n  pattern: string,\n  routeOrMiddleware: Route<State> | Middleware<State>,\n  includeLastSegment: boolean,\n): ErrorCmd<State> {\n  const route = typeof routeOrMiddleware === \"function\"\n    ? { handler: routeOrMiddleware }\n    : routeOrMiddleware;\n  ensureHandler(route);\n\n  return { type: CommandType.Error, pattern, item: route, includeLastSegment };\n}\n\nexport interface AppCommand<State> {\n  type: CommandType.App;\n  component: RouteComponent<State>;\n}\nexport function newAppCmd<State>(\n  component: RouteComponent<State>,\n): AppCommand<State> {\n  return { type: CommandType.App, component };\n}\n\nexport interface LayoutCommand<State> {\n  type: CommandType.Layout;\n  pattern: string;\n  component: RouteComponent<State>;\n  config?: LayoutConfig;\n  includeLastSegment: boolean;\n}\nexport function newLayoutCmd<State>(\n  pattern: string,\n  component: RouteComponent<State>,\n  config: LayoutConfig | undefined,\n  includeLastSegment: boolean,\n): LayoutCommand<State> {\n  return {\n    type: CommandType.Layout,\n    pattern,\n    component,\n    config,\n    includeLastSegment,\n  };\n}\n\nexport interface MiddlewareCmd<State> {\n  type: CommandType.Middleware;\n  pattern: string;\n  fns: MaybeLazyMiddleware<State>[];\n  includeLastSegment: boolean;\n}\nexport function newMiddlewareCmd<State>(\n  pattern: string,\n  fns: MaybeLazyMiddleware<State>[],\n  includeLastSegment: boolean,\n): MiddlewareCmd<State> {\n  return { type: CommandType.Middleware, pattern, fns, includeLastSegment };\n}\n\nexport interface NotFoundCmd<State> {\n  type: CommandType.NotFound;\n  fn: Middleware<State>;\n}\nexport function newNotFoundCmd<State>(\n  routeOrMiddleware: Route<State> | Middleware<State>,\n): NotFoundCmd<State> {\n  const route = typeof routeOrMiddleware === \"function\"\n    ? { handler: routeOrMiddleware }\n    : routeOrMiddleware;\n  ensureHandler(route);\n\n  return { type: CommandType.NotFound, fn: (ctx) => renderRoute(ctx, route) };\n}\n\nexport interface RouteCommand<State> {\n  type: CommandType.Route;\n  pattern: string;\n  route: MaybeLazy<Route<State>>;\n  config: RouteConfig | undefined;\n  includeLastSegment: boolean;\n}\nexport function newRouteCmd<State>(\n  pattern: string,\n  route: MaybeLazy<Route<State>>,\n  config: RouteConfig | undefined,\n  includeLastSegment: boolean,\n): RouteCommand<State> {\n  let normalized;\n  if (isLazy(route)) {\n    normalized = async () => {\n      const result = await route();\n      ensureHandler(result);\n      return result;\n    };\n  } else {\n    ensureHandler(route);\n    normalized = route;\n  }\n\n  return {\n    type: CommandType.Route,\n    pattern,\n    route: normalized,\n    config,\n    includeLastSegment,\n  };\n}\n\nexport interface HandlerCommand<State> {\n  type: CommandType.Handler;\n  pattern: string;\n  method: Method | \"ALL\";\n  fns: MaybeLazy<Middleware<State>>[];\n  includeLastSegment: boolean;\n}\nexport function newHandlerCmd<State>(\n  method: Method | \"ALL\",\n  pattern: string,\n  fns: MaybeLazy<Middleware<State>>[],\n  includeLastSegment: boolean,\n): HandlerCommand<State> {\n  return {\n    type: CommandType.Handler,\n    pattern,\n    method,\n    fns,\n    includeLastSegment,\n  };\n}\n\nexport interface FsRouteCommand<State> {\n  type: CommandType.FsRoute;\n  pattern: string;\n  getItems: () => Command<State>[];\n  includeLastSegment: boolean;\n}\n\nexport type Command<State> =\n  | ErrorCmd<State>\n  | AppCommand<State>\n  | LayoutCommand<State>\n  | NotFoundCmd<State>\n  | MiddlewareCmd<State>\n  | RouteCommand<State>\n  | HandlerCommand<State>\n  | FsRouteCommand<State>;\n\nexport function applyCommands<State>(\n  router: Router<MaybeLazyMiddleware<State>>,\n  commands: Command<State>[],\n  basePath: string,\n): { rootMiddlewares: MaybeLazyMiddleware<State>[] } {\n  const root = newSegment<State>(\"\", null);\n\n  applyCommandsInner(root, router, commands, basePath);\n\n  return { rootMiddlewares: segmentToMiddlewares(root) };\n}\n\nfunction applyCommandsInner<State>(\n  root: Segment<State>,\n  router: Router<MaybeLazyMiddleware<State>>,\n  commands: Command<State>[],\n  basePath: string,\n) {\n  for (let i = 0; i < commands.length; i++) {\n    const cmd = commands[i];\n\n    switch (cmd.type) {\n      case CommandType.Middleware: {\n        const segment = getOrCreateSegment(\n          root,\n          cmd.pattern,\n          cmd.includeLastSegment,\n        );\n        segment.middlewares.push(...cmd.fns);\n        break;\n      }\n      case CommandType.NotFound: {\n        root.notFound = cmd.fn;\n        break;\n      }\n      case CommandType.Error: {\n        const segment = getOrCreateSegment(\n          root,\n          cmd.pattern,\n          cmd.includeLastSegment,\n        );\n        segment.errorRoute = cmd.item;\n        break;\n      }\n      case CommandType.App: {\n        root.app = cmd.component;\n        break;\n      }\n      case CommandType.Layout: {\n        const segment = getOrCreateSegment(\n          root,\n          cmd.pattern,\n          cmd.includeLastSegment,\n        );\n        segment.layout = {\n          component: cmd.component,\n          config: cmd.config ?? null,\n        };\n        break;\n      }\n      case CommandType.Route: {\n        const { pattern, route, config } = cmd;\n        const segment = getOrCreateSegment(\n          root,\n          pattern,\n          cmd.includeLastSegment,\n        );\n        const fns = segmentToMiddlewares(segment);\n\n        if (isLazy(route)) {\n          const routePath = mergePath(\n            basePath,\n            config?.routeOverride ?? pattern,\n            false,\n          );\n\n          let def: Route<State>;\n          fns.push(async (ctx) => {\n            if (def === undefined) {\n              def = await route();\n            }\n\n            if (def.css !== undefined) {\n              setAdditionalStyles(ctx, def.css);\n            }\n\n            return renderRoute(ctx, def);\n          });\n\n          if (config === undefined || config.methods === \"ALL\") {\n            router.add(\"GET\", routePath, fns);\n            router.add(\"DELETE\", routePath, fns);\n            router.add(\"HEAD\", routePath, fns);\n            router.add(\"OPTIONS\", routePath, fns);\n            router.add(\"PATCH\", routePath, fns);\n            router.add(\"POST\", routePath, fns);\n            router.add(\"PUT\", routePath, fns);\n          } else if (Array.isArray(config.methods)) {\n            for (let i = 0; i < config.methods.length; i++) {\n              const method = config.methods[i];\n              router.add(method, routePath, fns);\n            }\n          }\n        } else {\n          fns.push((ctx) => renderRoute(ctx, route));\n\n          const routePath = toRoutePath(mergePath(\n            basePath,\n            route.config?.routeOverride ?? pattern,\n            false,\n          ));\n\n          if (typeof route.handler === \"function\") {\n            router.add(\"GET\", routePath, fns);\n            router.add(\"DELETE\", routePath, fns);\n            router.add(\"HEAD\", routePath, fns);\n            router.add(\"OPTIONS\", routePath, fns);\n            router.add(\"PATCH\", routePath, fns);\n            router.add(\"POST\", routePath, fns);\n            router.add(\"PUT\", routePath, fns);\n          } else if (isHandlerByMethod(route.handler!)) {\n            for (const method of Object.keys(route.handler)) {\n              router.add(method as Method, routePath, fns);\n            }\n          }\n        }\n        break;\n      }\n      case CommandType.Handler: {\n        const { pattern, fns, method } = cmd;\n        const segment = getOrCreateSegment(\n          root,\n          pattern,\n          cmd.includeLastSegment,\n        );\n        const result = segmentToMiddlewares(segment);\n\n        result.push(...fns);\n\n        const resPath = toRoutePath(mergePath(basePath, pattern, false));\n        if (method === \"ALL\") {\n          router.add(\"GET\", resPath, result);\n          router.add(\"DELETE\", resPath, result);\n          router.add(\"HEAD\", resPath, result);\n          router.add(\"OPTIONS\", resPath, result);\n          router.add(\"PATCH\", resPath, result);\n          router.add(\"POST\", resPath, result);\n          router.add(\"PUT\", resPath, result);\n        } else {\n          router.add(method, resPath, result);\n        }\n\n        break;\n      }\n      case CommandType.FsRoute: {\n        const items = cmd.getItems();\n        const base = mergePath(basePath, cmd.pattern, true);\n        applyCommandsInner(root, router, items, base);\n        break;\n      }\n      default:\n        throw new Error(`Unknown command: ${JSON.stringify(cmd)}`);\n    }\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/compat.ts",
    "content": "import type { ComponentChildren } from \"preact\";\nimport type { Context } from \"./context.ts\";\nimport type { PageProps } from \"./render.ts\";\nimport type { HandlerFn, RouteHandler } from \"./handlers.ts\";\n\n/**\n * @deprecated Use {@linkcode PageProps} instead.\n */\nexport type AppProps<_Data = unknown, T = unknown> = Context<T>;\n/**\n * @deprecated Use {@linkcode PageProps} instead.\n */\nexport type LayoutProps<_Data = unknown, T = unknown> = Context<T>;\n/**\n * @deprecated Use {@linkcode PageProps} instead.\n */\nexport type UnknownPageProps<_Data = unknown, T = unknown> = Context<T>;\n/**\n * @deprecated Use {@linkcode PageProps} instead.\n */\nexport type ErrorPageProps<_Data = unknown, T = unknown> = Context<T>;\n\n/**\n * @deprecated Use {@linkcode FreshContext} instead.\n */\nexport type RouteContext<_T = never, S = Record<string, unknown>> = Context<S>;\n\n/**\n * @deprecated Use {@linkcode RouteHandler} instead.\n */\n// deno-lint-ignore no-explicit-any\nexport type Handlers<T = any, State = Record<string, unknown>> = RouteHandler<\n  T,\n  State\n>;\n\n/**\n * @deprecated Use {@linkcode HandlerFn} instead.\n */\n// deno-lint-ignore no-explicit-any\nexport type Handler<T = any, State = Record<string, unknown>> = HandlerFn<\n  T,\n  State\n>;\n\nfunction defineFn<State>(\n  fn: (\n    ctx: PageProps<State>,\n  ) =>\n    | ComponentChildren\n    | Response\n    | Promise<Response | ComponentChildren>,\n): (\n  ctx: PageProps<State>,\n) =>\n  | ComponentChildren\n  | Response\n  | Promise<Response | ComponentChildren> {\n  return fn;\n}\n\n/**\n * @deprecated Use {@linkcode [./mod.ts].Define.page|define.page} instead.\n */\nexport const defineApp = defineFn;\n/**\n * @deprecated Use {@linkcode [./mod.ts].Define.page|define.page} instead.\n */\nexport const defineRoute = defineFn;\n/**\n * @deprecated Use {@linkcode [./mod.ts].Define.page|define.page} instead.\n */\nexport const defineLayout = defineFn;\n"
  },
  {
    "path": "packages/fresh/src/compat_test.tsx",
    "content": "import type { PageProps } from \"./render.ts\";\nimport { assertType, type IsExact } from \"@std/testing/types\";\nimport { expect } from \"@std/expect\";\nimport { type defineApp, type defineLayout, defineRoute } from \"./compat.ts\";\n\nDeno.test(\"compat - defineFn works\", () => {\n  const ctx = {} as PageProps<unknown>;\n  expect(defineRoute(() => new Response(\"test\"))(ctx)).toBeInstanceOf(Response);\n  expect(defineRoute(() => <span>test</span>)(ctx)).toBeInstanceOf(Object);\n  expect(defineRoute(() => null)(ctx)).toEqual(null);\n});\n\nDeno.test(\"compat - functions equivalent\", () => {\n  assertType<IsExact<typeof defineApp, typeof defineRoute>>(true);\n  assertType<IsExact<typeof defineRoute, typeof defineLayout>>(true);\n});\n"
  },
  {
    "path": "packages/fresh/src/config.ts",
    "content": "import * as path from \"@std/path\";\n\nexport interface FreshConfig {\n  /**\n   * Serve fresh from a base path instead of from the root.\n   *   \"/foo/bar\" -> http://localhost:8000/foo/bar\n   * @default undefined\n   */\n  basePath?: string;\n  /**\n   * The mode Fresh can run in.\n   */\n  mode?: \"development\" | \"production\";\n}\n\n/**\n * The final resolved Fresh configuration where fields the user didn't specify are set to the default values.\n */\nexport interface ResolvedFreshConfig {\n  root: string;\n  /**\n   * Serve fresh from a base path instead of from the root.\n   * \"/foo/bar\" -> http://localhost:8000/foo/bar\n   */\n  basePath: string;\n  /**\n   * The mode Fresh can run in.\n   */\n  mode: \"development\" | \"production\";\n}\n\nexport function parseDirPath(\n  dirPath: string,\n  root: string,\n): string {\n  if (dirPath.startsWith(\"file://\")) {\n    dirPath = path.fromFileUrl(dirPath);\n  } else if (!path.isAbsolute(dirPath)) {\n    dirPath = path.join(root, dirPath);\n  }\n\n  if (Deno.build.os === \"windows\") {\n    dirPath = dirPath.replaceAll(\"\\\\\", \"/\");\n  }\n\n  return dirPath;\n}\n"
  },
  {
    "path": "packages/fresh/src/config_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport { parseDirPath } from \"./config.ts\";\n\nDeno.test(\"parseDirPath\", () => {\n  const cwd = Deno.cwd().replaceAll(\"\\\\\", \"/\");\n\n  // File paths\n  expect(parseDirPath(\"file:///foo/bar\", cwd)).toEqual(\"/foo/bar\");\n  if (Deno.build.os === \"windows\") {\n    expect(parseDirPath(\"file:///C:/foo/bar\", cwd)).toEqual(\"C:/foo/bar\");\n  }\n\n  // Relative paths\n  expect(parseDirPath(\"./foo/bar\", cwd)).toEqual(`${cwd}/foo/bar`);\n\n  // Absolute paths\n  expect(parseDirPath(\"/foo/bar\", cwd)).toEqual(\"/foo/bar\");\n  if (Deno.build.os === \"windows\") {\n    expect(parseDirPath(\"C:/foo/bar\", cwd)).toEqual(\"C:/foo/bar\");\n  }\n});\n"
  },
  {
    "path": "packages/fresh/src/constants.ts",
    "content": "export const INTERNAL_PREFIX = \"/_frsh\";\nexport const DEV_ERROR_OVERLAY_URL = `${INTERNAL_PREFIX}/error_overlay`;\nexport const ALIVE_URL = `${INTERNAL_PREFIX}/alive`;\nexport const PARTIAL_SEARCH_PARAM = \"fresh-partial\";\n\nexport const SECOND = 1000;\nexport const MINUTE = SECOND * 60;\nexport const HOUR = MINUTE * 60;\nexport const DAY = HOUR * 24;\nexport const WEEK = DAY * 7;\n\nexport const ASSET_CACHE_BUST_KEY = \"__frsh_c\";\n\nexport const UPDATE_INTERVAL = DAY;\n\nexport const TEST_FILE_PATTERN = /[._]test\\.(?:[tj]sx?|[mc][tj]s)$/;\n"
  },
  {
    "path": "packages/fresh/src/context.ts",
    "content": "import {\n  type AnyComponent,\n  type ComponentType,\n  Fragment,\n  type FunctionComponent,\n  h,\n  isValidElement,\n  type VNode,\n} from \"preact\";\nimport { jsxTemplate } from \"preact/jsx-runtime\";\nimport { SpanStatusCode } from \"@opentelemetry/api\";\nimport type { ResolvedFreshConfig } from \"./config.ts\";\nimport type { BuildCache } from \"./build_cache.ts\";\nimport type { LayoutConfig } from \"./types.ts\";\nimport {\n  FreshScripts,\n  RenderState,\n  setRenderState,\n} from \"./runtime/server/preact_hooks.ts\";\nimport { DEV_ERROR_OVERLAY_URL, PARTIAL_SEARCH_PARAM } from \"./constants.ts\";\nimport { tracer } from \"./otel.ts\";\nimport {\n  type ComponentDef,\n  isAsyncAnyComponent,\n  type PageProps,\n  renderAsyncAnyComponent,\n  renderRouteComponent,\n} from \"./render.ts\";\nimport { renderToString } from \"preact-render-to-string\";\n\nconst ENCODER = new TextEncoder();\n\nexport interface Island {\n  file: string;\n  name: string;\n  exportName: string;\n  fn: ComponentType;\n  css: string[];\n}\n\nexport type ServerIslandRegistry = Map<ComponentType, Island>;\n\nexport const internals: unique symbol = Symbol(\"fresh_internal\");\n\nexport interface UiTree<Data, State> {\n  app: AnyComponent<PageProps<Data, State>> | null;\n  layouts: ComponentDef<Data, State>[];\n}\n\n/**\n * @deprecated Use {@linkcode Context} instead.\n */\nexport type FreshContext<State = unknown> = Context<State>;\n\nexport let getBuildCache: <T>(ctx: Context<T>) => BuildCache<T>;\nexport let getInternals: <T>(ctx: Context<T>) => UiTree<unknown, T>;\nexport let setAdditionalStyles: <T>(ctx: Context<T>, css: string[]) => void;\n\n/**\n * The context passed to every middleware. It is unique for every request.\n */\nexport class Context<State> {\n  #internal: UiTree<unknown, State> = {\n    app: null,\n    layouts: [],\n  };\n  /** Reference to the resolved Fresh configuration */\n  readonly config: ResolvedFreshConfig;\n  /**\n   * The request url parsed into an `URL` instance. This is typically used\n   * to apply logic based on the pathname of the incoming url or when\n   * certain search parameters are set.\n   */\n  readonly url: URL;\n  /** The original incoming {@linkcode Request} object. */\n  readonly req: Request;\n  /** The matched route pattern. */\n  readonly route: string | null;\n  /** The url parameters of the matched route pattern. */\n  readonly params: Record<string, string>;\n  /** State object that is shared with all middlewares. */\n  readonly state: State = {} as State;\n  data: unknown = undefined;\n  /** Error value if an error was caught (Default: null) */\n  error: unknown | null = null;\n  readonly info: Deno.ServeHandlerInfo;\n  /**\n   * Whether the current Request is a partial request.\n   *\n   * Partials in Fresh will append the query parameter\n   * {@linkcode PARTIAL_SEARCH_PARAM} to the URL. This property can\n   * be used to determine if only `<Partial>`'s need to be rendered.\n   */\n  readonly isPartial: boolean;\n\n  /**\n   * Call the next middleware.\n   * ```ts\n   * const myMiddleware: Middleware = (ctx) => {\n   *   // do something\n   *\n   *   // Call the next middleware\n   *   return ctx.next();\n   * }\n   *\n   * const myMiddleware2: Middleware = async (ctx) => {\n   *   // do something before the next middleware\n   *   doSomething()\n   *\n   *   const res = await ctx.next();\n   *\n   *   // do something after the middleware\n   *   doSomethingAfter()\n   *\n   *   // Return the `Response`\n   *   return res\n   * }\n   */\n  next: () => Promise<Response>;\n\n  #buildCache: BuildCache<State>;\n  #additionalStyles: string[] | null = null;\n\n  Component!: FunctionComponent;\n\n  static {\n    // deno-lint-ignore no-explicit-any\n    getInternals = <T>(ctx: Context<T>) => ctx.#internal as any;\n    getBuildCache = <T>(ctx: Context<T>) => ctx.#buildCache;\n    setAdditionalStyles = <T>(ctx: Context<T>, css: string[]) =>\n      ctx.#additionalStyles = css;\n  }\n\n  constructor(\n    req: Request,\n    url: URL,\n    info: Deno.ServeHandlerInfo,\n    route: string | null,\n    params: Record<string, string>,\n    config: ResolvedFreshConfig,\n    next: () => Promise<Response>,\n    buildCache: BuildCache<State>,\n  ) {\n    this.url = url;\n    this.req = req;\n    this.info = info;\n    this.params = params;\n    this.route = route;\n    this.config = config;\n    this.isPartial = url.searchParams.has(PARTIAL_SEARCH_PARAM);\n    this.next = next;\n    this.#buildCache = buildCache;\n  }\n\n  /**\n   * Return a redirect response to the specified path. This is the\n   * preferred way to do redirects in Fresh.\n   *\n   * ```ts\n   * ctx.redirect(\"/foo/bar\") // redirect user to \"<yoursite>/foo/bar\"\n   *\n   * // Disallows protocol relative URLs for improved security. This\n   * // redirects the user to `<yoursite>/evil.com` which is safe,\n   * // instead of redirecting to `http://evil.com`.\n   * ctx.redirect(\"//evil.com/\");\n   * ```\n   */\n  redirect(pathOrUrl: string, status = 302): Response {\n    let location = pathOrUrl;\n\n    // Disallow protocol relative URLs\n    if (pathOrUrl !== \"/\" && pathOrUrl.startsWith(\"/\")) {\n      let idx = pathOrUrl.indexOf(\"?\");\n      if (idx === -1) {\n        idx = pathOrUrl.indexOf(\"#\");\n      }\n\n      const pathname = idx > -1 ? pathOrUrl.slice(0, idx) : pathOrUrl;\n      const search = idx > -1 ? pathOrUrl.slice(idx) : \"\";\n\n      // Remove double slashes to prevent open redirect vulnerability.\n      location = `${pathname.replaceAll(/\\/+/g, \"/\")}${search}`;\n    }\n\n    return new Response(null, {\n      status,\n      headers: {\n        location,\n      },\n    });\n  }\n\n  /**\n   * Render JSX and return an HTML `Response` instance.\n   * ```tsx\n   * ctx.render(<h1>hello world</h1>);\n   * ```\n   */\n  async render(\n    // deno-lint-ignore no-explicit-any\n    vnode: VNode<any> | null,\n    init: ResponseInit | undefined = {},\n    config: LayoutConfig = {},\n  ): Promise<Response> {\n    if (arguments.length === 0) {\n      throw new Error(`No arguments passed to: ctx.render()`);\n    } else if (vnode !== null && !isValidElement(vnode)) {\n      throw new Error(`Non-JSX element passed to: ctx.render()`);\n    }\n\n    const defs = config.skipInheritedLayouts ? [] : this.#internal.layouts;\n    const appDef = config.skipAppWrapper ? null : this.#internal.app;\n    const props = this as Context<State>;\n\n    // Compose final vnode tree\n    for (let i = defs.length - 1; i >= 0; i--) {\n      const child = vnode;\n      props.Component = () => child;\n\n      const def = defs[i];\n\n      const result = await renderRouteComponent(this, def, () => child);\n      if (result instanceof Response) {\n        return result;\n      }\n\n      vnode = result;\n    }\n\n    let appChild = vnode;\n    // deno-lint-ignore no-explicit-any\n    let appVNode: VNode<any>;\n\n    let hasApp = true;\n\n    if (isAsyncAnyComponent(appDef)) {\n      props.Component = () => appChild;\n      const result = await renderAsyncAnyComponent(appDef, props);\n      if (result instanceof Response) {\n        return result;\n      }\n\n      appVNode = result;\n    } else if (appDef !== null) {\n      appVNode = h(appDef, {\n        Component: () => appChild,\n        config: this.config,\n        data: null,\n        error: this.error,\n        info: this.info,\n        isPartial: this.isPartial,\n        params: this.params,\n        req: this.req,\n        state: this.state,\n        url: this.url,\n        route: this.route,\n      });\n    } else {\n      hasApp = false;\n      appVNode = appChild ?? h(Fragment, null);\n    }\n\n    const headers = getHeadersFromInit(init);\n\n    headers.set(\"Content-Type\", \"text/html; charset=utf-8\");\n    const responseInit: ResponseInit = {\n      status: init.status ?? 200,\n      headers,\n      statusText: init.statusText,\n    };\n\n    let partialId = \"\";\n    if (this.url.searchParams.has(PARTIAL_SEARCH_PARAM)) {\n      partialId = crypto.randomUUID();\n      headers.set(\"X-Fresh-Id\", partialId);\n    }\n\n    const html = tracer.startActiveSpan(\"render\", (span) => {\n      span.setAttribute(\"fresh.span_type\", \"render\");\n      const state = new RenderState(\n        this,\n        this.#buildCache,\n        partialId,\n      );\n\n      if (this.#additionalStyles !== null) {\n        for (let i = 0; i < this.#additionalStyles.length; i++) {\n          const css = this.#additionalStyles[i];\n          state.islandAssets.add(css);\n        }\n      }\n\n      try {\n        setRenderState(state);\n\n        let html = renderToString(\n          vnode ?? h(Fragment, null),\n        );\n\n        if (hasApp) {\n          appChild = jsxTemplate([html]);\n          html = renderToString(appVNode);\n        }\n\n        if (\n          !state.renderedHtmlBody || !state.renderedHtmlHead ||\n          !state.renderedHtmlTag\n        ) {\n          let fallback: VNode = jsxTemplate([html]);\n          if (!state.renderedHtmlBody) {\n            let scripts: VNode | null = null;\n\n            if (\n              this.url.pathname !== this.config.basePath + DEV_ERROR_OVERLAY_URL\n            ) {\n              scripts = h(FreshScripts, null) as VNode;\n            }\n\n            fallback = h(\"body\", null, fallback, scripts);\n          }\n          if (!state.renderedHtmlHead) {\n            fallback = h(\n              Fragment,\n              null,\n              h(\"head\", null, h(\"meta\", { charset: \"utf-8\" })),\n              fallback,\n            );\n          }\n          if (!state.renderedHtmlTag) {\n            fallback = h(\"html\", null, fallback);\n          }\n\n          html = renderToString(fallback);\n        }\n\n        return `<!DOCTYPE html>${html}`;\n      } catch (err) {\n        if (err instanceof Error) {\n          span.recordException(err);\n        } else {\n          span.setStatus({\n            code: SpanStatusCode.ERROR,\n            message: String(err),\n          });\n        }\n        throw err;\n      } finally {\n        // Add preload headers\n        const basePath = this.config.basePath;\n        const runtimeUrl = state.buildCache.clientEntry.startsWith(\".\")\n          ? state.buildCache.clientEntry.slice(1)\n          : state.buildCache.clientEntry;\n        let link = `<${\n          encodeURI(`${basePath}${runtimeUrl}`)\n        }>; rel=\"modulepreload\"; as=\"script\"`;\n        state.islands.forEach((island) => {\n          const specifier = `${basePath}${\n            island.file.startsWith(\".\") ? island.file.slice(1) : island.file\n          }`;\n          link += `, <${\n            encodeURI(specifier)\n          }>; rel=\"modulepreload\"; as=\"script\"`;\n        });\n\n        if (link !== \"\") {\n          headers.append(\"Link\", link);\n        }\n\n        state.clear();\n        setRenderState(null);\n\n        span.end();\n      }\n    });\n    return new Response(html, responseInit);\n  }\n\n  /**\n   * Respond with text. Sets `Content-Type: text/plain`.\n   * ```tsx\n   * app.use(ctx => ctx.text(\"Hello World!\"));\n   * ```\n   */\n  text(content: string, init?: ResponseInit): Response {\n    return new Response(content, init);\n  }\n\n  /**\n   * Respond with html string. Sets `Content-Type: text/html`.\n   * ```tsx\n   * app.get(\"/\", ctx => ctx.html(\"<h1>foo</h1>\"));\n   * ```\n   */\n  html(content: string, init?: ResponseInit): Response {\n    const headers = getHeadersFromInit(init);\n    headers.set(\"Content-Type\", \"text/html; charset=utf-8\");\n\n    return new Response(content, { ...init, headers });\n  }\n\n  /**\n   * Respond with json string, same as `Response.json()`. Sets\n   * `Content-Type: application/json`.\n   * ```tsx\n   * app.get(\"/\", ctx => ctx.json({ foo: 123 }));\n   * ```\n   */\n  // deno-lint-ignore no-explicit-any\n  json(content: any, init?: ResponseInit): Response {\n    return Response.json(content, init);\n  }\n\n  /**\n   * Helper to stream a sync or async iterable and encode text\n   * automatically.\n   *\n   * ```tsx\n   * function* gen() {\n   *   yield \"foo\";\n   *   yield \"bar\";\n   * }\n   *\n   * app.use(ctx => ctx.stream(gen()))\n   * ```\n   *\n   * Or pass in the function directly:\n   *\n   * ```tsx\n   * app.use(ctx => {\n   *   return ctx.stream(function* gen() {\n   *     yield \"foo\";\n   *     yield \"bar\";\n   *   });\n   * );\n   * ```\n   */\n  stream<U extends string | Uint8Array>(\n    stream:\n      | Iterable<U>\n      | AsyncIterable<U>\n      | (() => Iterable<U> | AsyncIterable<U>),\n    init?: ResponseInit,\n  ): Response {\n    const raw = typeof stream === \"function\" ? stream() : stream;\n\n    const body = ReadableStream.from(raw)\n      .pipeThrough(\n        new TransformStream({\n          transform(chunk, controller) {\n            if (chunk instanceof Uint8Array) {\n              // deno-lint-ignore no-explicit-any\n              controller.enqueue(chunk as any);\n            } else if (chunk === undefined) {\n              controller.enqueue(undefined);\n            } else {\n              const raw = ENCODER.encode(String(chunk));\n              controller.enqueue(raw);\n            }\n          },\n        }),\n      );\n\n    return new Response(body, init);\n  }\n}\n\nfunction getHeadersFromInit(init?: ResponseInit) {\n  if (init === undefined) {\n    return new Headers();\n  }\n\n  return init.headers !== undefined\n    ? init.headers instanceof Headers ? init.headers : new Headers(init.headers)\n    : new Headers();\n}\n"
  },
  {
    "path": "packages/fresh/src/context_test.tsx",
    "content": "import { expect } from \"@std/expect\";\nimport { Context } from \"./context.ts\";\nimport { App } from \"fresh\";\nimport { asset } from \"fresh/runtime\";\nimport { FakeServer } from \"./test_utils.ts\";\nimport { BUILD_ID } from \"@fresh/build-id\";\nimport { parseHtml } from \"../tests/test_utils.tsx\";\n\nDeno.test(\"FreshReqContext.prototype.redirect\", () => {\n  let res = Context.prototype.redirect(\"/\");\n  expect(res.status).toEqual(302);\n  expect(res.headers.get(\"Location\")).toEqual(\"/\");\n\n  res = Context.prototype.redirect(\"//evil.com\");\n  expect(res.status).toEqual(302);\n  expect(res.headers.get(\"Location\")).toEqual(\"/evil.com\");\n\n  res = Context.prototype.redirect(\"//evil.com/foo//bar\");\n  expect(res.status).toEqual(302);\n  expect(res.headers.get(\"Location\")).toEqual(\"/evil.com/foo/bar\");\n\n  res = Context.prototype.redirect(\"https://deno.com\");\n  expect(res.status).toEqual(302);\n  expect(res.headers.get(\"Location\")).toEqual(\"https://deno.com\");\n\n  res = Context.prototype.redirect(\"/\", 307);\n  expect(res.status).toEqual(307);\n});\n\nDeno.test(\"render asset()\", async () => {\n  const app = new App()\n    .get(\"/\", (ctx) =>\n      ctx.render(\n        <>\n          <p class=\"raw\">{asset(\"/foo\")}</p>\n          <img src=\"/foo\" srcset=\"/foo-bar\" />\n          <source src=\"/foo\" />\n        </>,\n      ));\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n\n  expect(doc.querySelector(\".raw\")!.textContent).toContain(BUILD_ID);\n  expect(doc.querySelector(\"img\")!.src).toContain(BUILD_ID);\n  expect(doc.querySelector(\"img\")!.srcset).toContain(BUILD_ID);\n  expect(doc.querySelector(\"source\")!.src).toContain(BUILD_ID);\n});\n\nDeno.test(\"ctx.render - throw with no arguments\", async () => {\n  const app = new App()\n    // deno-lint-ignore no-explicit-any\n    .get(\"/\", (ctx) => (ctx as any).render());\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n\n  await res.body?.cancel();\n  expect(res.status).toEqual(500);\n});\n\nDeno.test(\"ctx.render - throw with invalid first arg\", async () => {\n  const app = new App()\n    // deno-lint-ignore no-explicit-any\n    .get(\"/\", (ctx) => (ctx as any).render({}));\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n\n  await res.body?.cancel();\n  expect(res.status).toEqual(500);\n});\n\nDeno.test(\"ctx.isPartial - should indicate whether request is partial or not\", async () => {\n  const isPartials: boolean[] = [];\n  const app = new App()\n    .get(\"/\", (ctx) => {\n      isPartials.push(ctx.isPartial);\n      return new Response(\"ok\");\n    });\n  const server = new FakeServer(app.handler());\n\n  await server.get(\"/\");\n  await server.get(\"/?fresh-partial\");\n\n  expect(isPartials).toEqual([false, true]);\n});\n\nDeno.test(\"ctx.route - should contain matched route\", async () => {\n  let route: string | null = null;\n  const app = new App()\n    .use((ctx) => {\n      route = ctx.route;\n      return ctx.next();\n    })\n    .get(\"/foo/bar\", () => new Response(\"ok\"))\n    .get(\"/foo/:id\", () => new Response(\"ok\"));\n\n  const server = new FakeServer(app.handler());\n\n  await server.get(\"/invalid\");\n  expect(route).toEqual(null);\n\n  await server.get(\"/foo/bar\");\n  expect(route).toEqual(\"/foo/bar\");\n\n  await server.get(\"/foo/123\");\n  expect(route).toEqual(\"/foo/:id\");\n});\n\nDeno.test(\"ctx.text()\", async () => {\n  const app = new App()\n    .get(\"/\", (ctx) => ctx.text(\"foobar\"));\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n\n  expect(res.headers.get(\"Content-Type\")).toEqual(\"text/plain;charset=UTF-8\");\n  const text = await res.text();\n  expect(text).toEqual(\"foobar\");\n});\n\nDeno.test(\"ctx.html()\", async () => {\n  const app = new App()\n    .get(\"/\", (ctx) => ctx.html(\"<h1>foo</h1>\"));\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n\n  expect(res.headers.get(\"Content-Type\")).toEqual(\"text/html; charset=utf-8\");\n  const text = await res.text();\n  expect(text).toEqual(\"<h1>foo</h1>\");\n});\n\nDeno.test(\"ctx.json()\", async () => {\n  const app = new App()\n    .get(\"/\", (ctx) => ctx.json({ foo: 123 }));\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n\n  expect(res.headers.get(\"Content-Type\")).toEqual(\"application/json\");\n  const text = await res.text();\n  expect(text).toEqual('{\"foo\":123}');\n});\n\nDeno.test(\"ctx.stream() - enqueue values\", async () => {\n  function* gen() {\n    yield \"foo\";\n    yield new TextEncoder().encode(\"bar\");\n  }\n\n  const app = new App()\n    .get(\"/\", (ctx) => ctx.stream(gen()));\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n  const text = await res.text();\n  expect(text).toEqual(\"foobar\");\n});\n\nDeno.test(\"ctx.stream() - pass function\", async () => {\n  const app = new App()\n    .get(\"/\", (ctx) =>\n      ctx.stream(function* () {\n        yield \"foo\";\n        yield \"bar\";\n      }));\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n  const text = await res.text();\n  expect(text).toEqual(\"foobar\");\n});\n\nDeno.test(\"ctx.stream() - support iterable\", async () => {\n  function* gen() {\n    yield \"foo\";\n    yield \"bar\";\n  }\n\n  const app = new App()\n    .get(\"/\", (ctx) => ctx.stream(gen()));\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n  const text = await res.text();\n\n  expect(text).toEqual(\"foobar\");\n});\n\nDeno.test(\"ctx.stream() - support async iterable\", async () => {\n  async function* gen() {\n    yield \"foo\";\n    yield \"bar\";\n  }\n\n  const app = new App()\n    .get(\"/\", (ctx) => ctx.stream(gen()));\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\");\n  const text = await res.text();\n\n  expect(text).toEqual(\"foobar\");\n});\n"
  },
  {
    "path": "packages/fresh/src/define.ts",
    "content": "import type { AnyComponent } from \"preact\";\nimport type { HandlerByMethod, HandlerFn, RouteHandler } from \"./handlers.ts\";\nimport type { Middleware } from \"./middlewares/mod.ts\";\nimport type { PageProps } from \"./render.ts\";\n\n/**\n * A set of define functions that enable better type inference and code\n * completion when defining routes and middleware.\n *\n * To create a define object, call {@link createDefine}.\n */\nexport interface Define<State> {\n  /**\n   * Define a {@link RouteHandler} object. This function returns the passed\n   * input as-is.\n   *\n   * You can use this function to help the TypeScript compiler infer the types\n   * of your route handlers. For example:\n   *\n   * ```ts\n   * import { define } from \"../utils.ts\";\n   *\n   * export const handler = define.handlers((ctx) => {\n   *   ctx.url; // ctx is inferred to be a Context object, so this is a URL\n   *   return new Response(\"Hello, world!\");\n   * });\n   * ```\n   *\n   * This is particularly useful when combined with the {@link Define.page}\n   * helper function, which can infer the data type from the handler function.\n   * For more information, see {@link Define.page}.\n   *\n   * You can also pass an explicit type argument to ensure that all data\n   * returned from the handler is of the correct type:\n   *\n   * ```ts\n   * import { page } from \"fresh\";\n   * import { define } from \"../utils.ts\";\n   *\n   * export const handler = define.handlers<{ slug: string }>({\n   *   async GET(ctx) {\n   *     const slug = ctx.params.slug; // slug is inferred to be a string\n   *     return page({ slug });\n   *   },\n   *\n   *   // This method will cause a type error because the data object is missing\n   *   // the required `slug` property.\n   *   async POST(ctx) {\n   *     return page({ });\n   *   },\n   * });\n   * ```\n   *\n   * @typeParam Data The type of data that the handler returns. This will be inferred from the handler methods if not provided.\n   * @typeParam Handlers This will always be inferred from the input object. Do not manually specify this type.\n   */\n  handlers<\n    Data,\n    Handlers extends RouteHandler<Data, State> = RouteHandler<Data, State>,\n  >(handlers: Handlers): typeof handlers;\n\n  /**\n   * Define a page component that will be rendered when a route handler returns\n   * data. This function returns the passed input as-is.\n   *\n   * You can use this function to help the TypeScript compiler infer the types\n   * of the data that your page component receives. For example:\n   *\n   * ```ts\n   * import { define } from \"../utils.ts\";\n   *\n   * export default define.page((props) => {\n   *   const slug = props.params.slug; // Because props is inferred to be a Context object, slug is inferred to be a string\n   *   return <h1>{slug}</h1>;\n   * });\n   * ```\n   *\n   * This is particularly useful when combined with the {@link handlers}\n   * helper function, in which case the data type will be inferred from the\n   * return type of the handler method.\n   *\n   * ```ts\n   * import { page } from \"fresh\";\n   * import { define } from \"../utils.ts\";\n   *\n   * export const handler = define.handlers({\n   *   async GET(ctx) {\n   *     const slug = ctx.params.slug; // slug is inferred to be a string\n   *     return page({ slug });\n   *  },\n   * });\n   *\n   * export default define.page<typeof handler>(({ data }) => {\n   *   const slug = data.slug; // slug is inferred to be a string here\n   *   return <h1>{slug}</h1>;\n   * });\n   * ```\n   *\n   * As a rule of thumb, always use this function to define your page\n   * components. If you also have a handler for this route, pass the handler\n   * object as a type argument to this function. If you do not have a handler,\n   * omit the type argument.\n   *\n   * @typeParam Handler The type of the handler object that this page component is associated with. If this route has a handler, pass the handler object as a type argument to this function, e.g. `typeof handler`. If this route does not have a handler, omit this type argument.\n   * @typeParam Data The type of data that the page component receives. This will be inferred from the handler methods if not provided. In very advanced use cases, you can specify `never` to the `Handler` type argument and provide the `Data` type explicitly.\n   */\n  page<\n    // deno-lint-ignore no-explicit-any\n    Handler extends RouteHandler<any, State> = never,\n    Data = Handler extends HandlerFn<infer Data, State> ? Data\n      : Handler extends HandlerByMethod<infer Data, State> ? Data\n      : never,\n  >(render: AnyComponent<PageProps<Data, State>>): typeof render;\n\n  /**\n   * Define a {@link Middleware} that will be used to process requests before\n   * they are passed to the route handler. This function returns the passed\n   * input as-is.\n   *\n   * You can use this function to help the TypeScript compiler infer the types\n   * of the context object that your middleware receives. For example:\n   *\n   * ```ts\n   * import { define } from \"../utils.ts\";\n   *\n   * export const middleware = define.middleware((ctx) => {\n   *   ctx.url; // ctx is inferred to be a Context object, so this is a URL\n   *   return ctx.next();\n   * });\n   * ```\n   *\n   * You may also pass an array of middleware functions to this function.\n   *\n   * @typeParam M The type of the middleware function. This will be inferred from the input function. Do not manually specify this type.\n   */\n  middleware<M extends Middleware<State> | Middleware<State>[]>(\n    middleware: M,\n  ): typeof middleware;\n  /**\n   * Define a layout component. This function returns the passed input as-is.\n   *\n   * You can use this function to help the TypeScript compiler infer the types\n   * of `props` that your component receives. For example:\n   *\n   * ```ts\n   * import { define } from \"../utils.ts\";\n   *\n   * export default define.layout((props) => {\n   *   const slug = props.params.slug; // Because props is inferred to be a Context object, slug is inferred to be a string\n   *   return <h1>{slug}</h1>;\n   * });\n   * ```\n   */\n  layout(render: AnyComponent<PageProps<unknown, State>>): typeof render;\n}\n\n/**\n * Create a set of define functions that enable better type inference and code\n * completion when defining routes and middleware.\n *\n * To use, call this function in a central file and export the result. In your\n * route and middleware files, import the {@link Define|define object} and use\n * it to define your routes and middleware using the\n * {@link Define.handlers|define.handlers},\n * {@link Define.page|define.page}, and\n * {@link Define.middleware|define.middleware} functions.\n *\n * @typeParam State The type of the state object that is passed to all middleware and route handlers.\n */\nexport function createDefine<State>(): Define<State> {\n  return {\n    handlers(handlers) {\n      return handlers;\n    },\n    page(render) {\n      return render;\n    },\n    layout(render) {\n      return render;\n    },\n    middleware(middleware) {\n      return middleware;\n    },\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/define_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport { createDefine } from \"./define.ts\";\nimport { page } from \"./handlers.ts\";\n\nDeno.test.ignore(\"createDefine\", () => {\n  const define = createDefine<{ foo: number }>();\n\n  // Testing the types\n  const handlerFn = define.handlers((ctx) => {\n    ctx.state.foo satisfies number;\n    return page({ bar: true });\n  });\n  const handlerObj = define.handlers({\n    GET(ctx) {\n      ctx.state.foo satisfies number;\n      return page({ bar: [1, 2, 3] });\n    },\n    POST: () => {\n      return page({ baz: \"hello\" });\n    },\n  });\n\n  define.page<typeof handlerFn>(({ data }) => {\n    data.bar satisfies boolean;\n    return \"page\";\n  });\n\n  define.page<typeof handlerObj>(({ data }) => {\n    if (\"baz\" in data) {\n      data.baz satisfies string;\n    } else {\n      data.bar satisfies number[];\n    }\n    return \"page\";\n  });\n\n  define.middleware((ctx) => {\n    ctx.state.foo satisfies number;\n    return new Response(\"Hello\");\n  });\n\n  expect(typeof define.page).toBe(\"function\");\n  expect(typeof define.handlers).toBe(\"function\");\n  expect(typeof define.middleware).toBe(\"function\");\n});\n"
  },
  {
    "path": "packages/fresh/src/dev/builder.ts",
    "content": "import { App, type ListenOptions, setBuildCache } from \"../app.ts\";\nimport { fsAdapter } from \"../fs.ts\";\nimport * as path from \"@std/path\";\nimport * as colors from \"@std/fmt/colors\";\nimport { bundleJs, type FreshBundleOptions } from \"./esbuild.ts\";\n\nimport { liveReload } from \"./middlewares/live_reload.ts\";\nimport {\n  cssAssetHash,\n  FileTransformer,\n  type OnTransformOptions,\n} from \"./file_transformer.ts\";\nimport type { TransformFn } from \"./file_transformer.ts\";\nimport {\n  type DevBuildCache,\n  DiskBuildCache,\n  type FsRoute,\n  MemoryBuildCache,\n} from \"./dev_build_cache.ts\";\nimport { BUILD_ID } from \"@fresh/build-id\";\nimport { updateCheck } from \"./update_check.ts\";\nimport { devErrorOverlay } from \"./middlewares/error_overlay/middleware.tsx\";\nimport { automaticWorkspaceFolders } from \"./middlewares/automatic_workspace_folders.ts\";\nimport { parseDirPath } from \"../config.ts\";\nimport { pathToExportName, UniqueNamer } from \"../utils.ts\";\nimport { checkDenoCompilerOptions } from \"./check.ts\";\nimport { crawlFsItem } from \"./fs_crawl.ts\";\nimport { TEST_FILE_PATTERN, UPDATE_INTERVAL } from \"../constants.ts\";\n\nexport interface BuildOptions {\n  /**\n   * This sets the target environment for the generated code. Newer\n   * language constructs will be transformed to match the specified\n   * support range. See https://esbuild.github.io/api/#target\n   * @default {\"es2022\"}\n   */\n  target?: string | string[];\n  /**\n   * The root directory of the Fresh project.\n   *\n   * Other paths, such as `build.outDir`, `staticDir`, and `fsRoutes()`\n   * are resolved relative to this directory.\n   * @default Deno.cwd()\n   */\n  root?: string;\n  /**\n   * The directory to write generated files to when `dev.ts build` is run.\n   *\n   * This can be an absolute path, a file URL or a relative path.\n   * Relative paths are resolved against the `root` option.\n   * @default \"_fresh\"\n   */\n  outDir?: string;\n  /**\n   * The directory to serve static files from.\n   *\n   * This can be an absolute path, a file URL or a relative path.\n   * Relative paths are resolved against the `root` option.\n   * @default \"static\"\n   */\n  staticDir?: string;\n  /**\n   * The directory which contains islands.\n   *\n   * This can be an absolute path, a file URL or a relative path.\n   * Relative paths are resolved against the `root` option.\n   * @default \"islands\"\n   */\n  islandDir?: string;\n  /**\n   * The directory which contains routes.\n   *\n   * This can be an absolute path, a file URL or a relative path.\n   * Relative paths are resolved against the `root` option.\n   * @default \"routes\"\n   */\n  routeDir?: string;\n  /**\n   * The entrypoint for your server.\n   *\n   * This can be an absolute path, a file URL or a relative path.\n   * Relative paths are resolved against the `root` option.\n   * @default \"main.ts\"\n   */\n  serverEntry?: string;\n  /**\n   * File paths which should be ignored when crawling the file system.\n   */\n  ignore?: RegExp[];\n\n  /**\n   * Control if/how production source maps should be handled.\n   * See https://esbuild.github.io/api/#source-maps for more information.\n   */\n  sourceMap?: FreshBundleOptions[\"sourceMap\"];\n}\n\n/**\n * The final resolved Builder configuration.\n */\nexport type ResolvedBuildConfig = Required<Omit<BuildOptions, \"sourceMap\">> & {\n  mode: \"development\" | \"production\";\n  buildId: string;\n  sourceMap?: FreshBundleOptions[\"sourceMap\"];\n};\n\n// deno-lint-ignore no-explicit-any\nexport class Builder<State = any> {\n  #transformer: FileTransformer;\n  #addedInternalTransforms = false;\n  config: ResolvedBuildConfig;\n  #islandSpecifiers = new Set<string>();\n  #fsRoutes: FsRoute<State>;\n  #ready = Promise.withResolvers<void>();\n\n  constructor(options?: BuildOptions) {\n    const root = parseDirPath(options?.root ?? \".\", Deno.cwd());\n    const serverEntry = parseDirPath(options?.serverEntry ?? \"main.ts\", root);\n    const outDir = parseDirPath(options?.outDir ?? \"_fresh\", root);\n    const staticDir = parseDirPath(options?.staticDir ?? \"static\", root);\n    const islandDir = parseDirPath(options?.islandDir ?? \"islands\", root);\n    const routeDir = parseDirPath(options?.routeDir ?? \"routes\", root);\n\n    this.#fsRoutes = { dir: routeDir, files: [], id: \"default\" };\n\n    this.#transformer = new FileTransformer(fsAdapter, root);\n\n    this.config = {\n      serverEntry,\n      target: options?.target ?? [\"chrome99\", \"firefox99\", \"safari15\"],\n      root,\n      outDir,\n      staticDir,\n      islandDir,\n      routeDir,\n      ignore: options?.ignore ?? [TEST_FILE_PATTERN],\n      mode: \"production\",\n      buildId: BUILD_ID,\n      sourceMap: options?.sourceMap,\n    };\n  }\n\n  registerIsland(specifier: string): void {\n    this.#islandSpecifiers.add(specifier);\n  }\n\n  onTransformStaticFile(\n    options: OnTransformOptions,\n    callback: TransformFn,\n  ): void {\n    this.#transformer.onTransform(options, callback);\n  }\n\n  async listen(\n    importApp: () => Promise<{ app: App<State> } | App<State>>,\n    options: ListenOptions = {},\n  ): Promise<void> {\n    // Run update check in background\n    updateCheck(UPDATE_INTERVAL).catch(() => {});\n\n    this.config.mode = \"development\";\n\n    await this.#crawlFsItems();\n\n    let app = await importApp();\n    if (!(app instanceof App) && \"app\" in app) {\n      app = app.app;\n    }\n\n    const buildCache = new MemoryBuildCache<State>(\n      this.config,\n      this.#fsRoutes,\n      this.#transformer,\n    );\n\n    await buildCache.prepare();\n\n    app.config.root = this.config.root;\n    app.config.mode = \"development\";\n    setBuildCache(app, buildCache, \"development\");\n\n    const appHandler = app.handler();\n\n    const devApp = new App<State>(app.config)\n      .use(liveReload())\n      .use(devErrorOverlay())\n      .use(automaticWorkspaceFolders(this.config.root))\n      // Wait for islands to be ready\n      .use(async (ctx) => {\n        await this.#ready.promise;\n        return ctx.next();\n      })\n      .all(\"*\", (ctx) => appHandler(ctx.req, ctx.info));\n\n    devApp.config.root = this.config.root;\n    devApp.config.mode = \"development\";\n\n    setBuildCache(devApp, buildCache, \"development\");\n\n    // Boot in parallel to spin up the server quicker. We'll hold\n    // requests until the required assets are processed.\n    await Promise.all([\n      devApp.listen(options),\n      this.#build(buildCache, true),\n    ]);\n    return;\n  }\n\n  /**\n   * Build optimized assets for your app. By default this will create\n   * a production build.\n   *\n   * This can also be used for testing to apply a snapshot to a particular\n   * {@linkcode App} instance.\n   *\n   * @example Testing\n   * ```ts\n   * const builder = new Builder();\n   * const applySnapshot = await builder.build({ snapshot: \"memory\" });\n   *\n   * Deno.test(\"My Test\", () => {\n   *   const app = new App()\n   *     .get(\"/\", () => new Response(\"hello\"))\n   *\n   *   applySnapshot(app)\n   *\n   *   // ... your usual testing\n   * })\n   * ```\n   * @param options\n   * @returns Apply a snapshot to a particular {@linkcode App} instance.\n   */\n  async build(\n    options?: {\n      mode?: ResolvedBuildConfig[\"mode\"];\n      snapshot?: \"disk\" | \"memory\";\n    },\n  ): Promise<(app: App<State>) => void> {\n    this.config.mode = options?.mode ?? \"production\";\n\n    await this.#crawlFsItems();\n\n    const buildCache = options?.snapshot === \"memory\"\n      ? new MemoryBuildCache(\n        this.config,\n        this.#fsRoutes,\n        this.#transformer,\n      )\n      : new DiskBuildCache(\n        this.config,\n        this.#fsRoutes,\n        this.#transformer,\n      );\n\n    await this.#build(buildCache, this.config.mode === \"development\");\n    await buildCache.prepare();\n\n    return (app) => {\n      setBuildCache(app, buildCache, app.config.mode);\n    };\n  }\n\n  async #crawlFsItems() {\n    const { islands, routes } = await crawlFsItem(\n      {\n        islandDir: this.config.islandDir,\n        routeDir: this.config.routeDir,\n        ignore: this.config.ignore,\n      },\n    );\n\n    for (let i = 0; i < islands.length; i++) {\n      this.registerIsland(islands[i]);\n    }\n\n    this.#fsRoutes.files = routes;\n  }\n\n  async #build<T>(buildCache: DevBuildCache<T>, dev: boolean): Promise<void> {\n    const { target, outDir, root } = this.config;\n    const staticOutDir = path.join(outDir, \"static\");\n\n    const { denoJson, jsxImportSource } = await checkDenoCompilerOptions(root);\n\n    if (!this.#addedInternalTransforms) {\n      this.#addedInternalTransforms = true;\n      cssAssetHash(this.#transformer);\n    }\n\n    try {\n      await Deno.remove(staticOutDir);\n    } catch {\n      // Ignore\n    }\n\n    const runtimePath = dev\n      ? \"../runtime/client/dev.ts\"\n      : \"../runtime/client/mod.ts\";\n\n    const entryPoints: Record<string, string> = {\n      \"fresh-runtime\": new URL(runtimePath, import.meta.url).href,\n    };\n\n    const namer = new UniqueNamer();\n    for (const spec of this.#islandSpecifiers) {\n      const specName = specToName(spec);\n      const name = namer.getUniqueName(specName);\n\n      entryPoints[name] = spec;\n\n      buildCache.islandModNameToChunk.set(name, {\n        name,\n        server: spec,\n        browser: null,\n        css: [],\n      });\n    }\n\n    const output = await bundleJs({\n      cwd: root,\n      outDir: staticOutDir,\n      dev: dev ?? false,\n      target,\n      buildId: BUILD_ID,\n      entryPoints,\n      jsxImportSource,\n      denoJsonPath: denoJson,\n      sourceMap: this.config.sourceMap,\n    });\n\n    const prefix = `/_fresh/js/${BUILD_ID}/`;\n\n    for (const name of buildCache.islandModNameToChunk.keys()) {\n      const chunkName = output.entryToChunk.get(name);\n      if (chunkName === undefined) {\n        throw new Error(`Could not find chunk for island ${name}`);\n      }\n\n      const pathname = `${prefix}${chunkName}`;\n      buildCache.islandModNameToChunk.get(name)!.browser = pathname;\n    }\n\n    for (let i = 0; i < output.files.length; i++) {\n      const file = output.files[i];\n      const pathname = `${prefix}${file.path}`;\n      await buildCache.addProcessedFile(pathname, file.contents, file.hash);\n    }\n\n    await buildCache.flush();\n\n    if (!dev) {\n      // deno-lint-ignore no-console\n      console.log(\n        `Assets written to: ${colors.cyan(outDir)}`,\n      );\n    }\n\n    this.#ready.resolve();\n  }\n}\n\nexport function specToName(spec: string): string {\n  if (/^(https?:|file:)/.test(spec)) {\n    const url = new URL(spec);\n    if (url.pathname === \"/\") {\n      return pathToExportName(url.hostname);\n    }\n\n    const idx = spec.lastIndexOf(\"/\");\n    return pathToExportName(spec.slice(idx + 1));\n  } else if (spec.startsWith(\"jsr:\")) {\n    const match = spec.match(\n      /jsr:@([^/]+)\\/([^@/]+)(@[\\^~]?\\d+\\.\\d+\\.\\d+([^/]+)?)?(\\/.*)?$/,\n    )!;\n    if (match[5] === undefined) {\n      return pathToExportName(`${match[1]}_${match[2]}`);\n    }\n\n    return pathToExportName(match[5]);\n  } else if (spec.startsWith(\"npm:\")) {\n    const match = spec.match(\n      /npm:(@([^/]+)\\/([^@/]+)|[^@/]+)(@[\\^~]?\\d+\\.\\d+\\.\\d+([^/]+)?)?(\\/.*)?$/,\n    )!;\n\n    if (match[6] === undefined) {\n      if (match[2] === undefined) {\n        return pathToExportName(match[1]);\n      }\n      return pathToExportName(`${match[2]}_${match[3]}`);\n    }\n\n    return pathToExportName(match[6]);\n  }\n\n  const match = spec.match(/^(@([^/]+)\\/([^@/]+)|[^@/]+)(\\/.*)?$/);\n  if (match !== null) {\n    if (match[4] === undefined) {\n      if (match[2] !== undefined) {\n        return pathToExportName(`${match[2]}_${match[3]}`);\n      }\n\n      return pathToExportName(match[1]);\n    }\n\n    return pathToExportName(match[4]);\n  }\n\n  return pathToExportName(spec);\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/builder_test.ts",
    "content": "import { expect, fn } from \"@std/expect\";\nimport * as path from \"@std/path\";\nimport { Builder, specToName } from \"./builder.ts\";\nimport { App } from \"../app.ts\";\nimport { DEV_ERROR_OVERLAY_URL } from \"../constants.ts\";\nimport { BUILD_ID } from \"@fresh/build-id\";\nimport { withTmpDir, writeFiles } from \"../test_utils.ts\";\nimport {\n  getStdOutput,\n  withChildProcessServer,\n} from \"../../tests/test_utils.tsx\";\nimport { staticFiles } from \"../middlewares/static_files.ts\";\n\nDeno.test({\n  name: \"Builder - chain onTransformStaticFile\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n\n    const logs: string[] = [];\n    const builder = new Builder({\n      outDir: path.join(tmp, \"dist\"),\n      staticDir: tmp,\n    });\n    builder.onTransformStaticFile(\n      { pluginName: \"A\", filter: /\\.css$/ },\n      () => {\n        logs.push(\"A\");\n      },\n    );\n    builder.onTransformStaticFile(\n      { pluginName: \"B\", filter: /\\.css$/ },\n      () => {\n        logs.push(\"B\");\n      },\n    );\n    builder.onTransformStaticFile(\n      { pluginName: \"C\", filter: /\\.css$/ },\n      () => {\n        logs.push(\"C\");\n      },\n    );\n\n    await Deno.writeTextFile(path.join(tmp, \"foo.css\"), \"body { color: red; }\");\n    await builder.build();\n\n    expect(logs).toEqual([\"A\", \"B\", \"C\"]);\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - handles Windows paths\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n\n    const builder = new Builder({\n      outDir: path.join(tmp, \"dist\"),\n      staticDir: tmp,\n    });\n    await Deno.mkdir(path.join(tmp, \"images\"));\n    await Deno.writeTextFile(\n      path.join(tmp, \"images\", \"batman.svg\"),\n      \"<svg></svg>\",\n    );\n    await builder.build();\n\n    const snapshotJson = await Deno.readTextFile(\n      path.join(tmp, \"dist\", \"snapshot.js\"),\n    );\n    expect(snapshotJson).toContain(\"/images/batman.svg\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - hashes CSS urls by default\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n    const builder = new Builder({\n      outDir: path.join(tmp, \"dist\"),\n      staticDir: tmp,\n    });\n\n    await Deno.writeTextFile(\n      path.join(tmp, \"foo.css\"),\n      \"body { background: url('/foo.jpg'); }\",\n    );\n    await builder.build();\n\n    const css = await Deno.readTextFile(\n      path.join(tmp, \"dist\", \"static\", \"foo.css\"),\n    );\n    expect(css).toContain('body { background: url(\"/foo.jpg?__frsh_c=');\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\n// Issue https://github.com/denoland/fresh/issues/2599\nDeno.test({\n  name: \"Builder - hashes CSS urls by default\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n    const builder = new Builder({\n      outDir: path.join(tmp, \"dist\"),\n      staticDir: tmp,\n    });\n    await Deno.writeTextFile(\n      path.join(tmp, \"foo.css\"),\n      `:root { --icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 154.5, 137.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\"); }`,\n    );\n    await builder.build();\n\n    const css = await Deno.readTextFile(\n      path.join(tmp, \"dist\", \"static\", \"foo.css\"),\n    );\n    expect(css).toEqual(\n      `:root { --icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 154.5, 137.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E\"); }`,\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - can bundle islands from JSR\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n\n    const outDir = path.join(tmp, \"dist\");\n    const builder = new Builder({ outDir });\n\n    const specifier = \"jsr:@marvinh-test/fresh-island\";\n    builder.registerIsland(specifier);\n\n    await builder.build();\n\n    const name = specToName(specifier);\n    const code = await Deno.readTextFile(\n      path.join(\n        tmp,\n        \"dist\",\n        \"static\",\n        \"_fresh\",\n        \"js\",\n        BUILD_ID,\n        `${name}.js`,\n      ),\n    );\n    expect(code).toContain('\"remote-island\"');\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - can bundle islands with import.meta.resolve()\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n\n    const outDir = path.join(tmp, \"dist\");\n    const builder = new Builder({ outDir });\n\n    const specifier = import.meta.resolve(\n      \"../../tests/fixtures_islands/Counter.tsx\",\n    );\n    builder.registerIsland(specifier);\n\n    await builder.build({ mode: \"production\", snapshot: \"disk\" });\n\n    const name = specToName(specifier);\n    const code = await Deno.readTextFile(\n      path.join(\n        tmp,\n        \"dist\",\n        \"static\",\n        \"_fresh\",\n        \"js\",\n        BUILD_ID,\n        `${name}.js`,\n      ),\n    );\n    expect(code).toContain('\"decrement\"');\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - exclude files\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n\n    const logs: string[] = [];\n    const builder = new Builder({\n      outDir: path.join(tmp, \"dist\"),\n      staticDir: tmp,\n    });\n\n    // String\n    builder.onTransformStaticFile(\n      { pluginName: \"A\", filter: /\\.css$/, exclude: [\"foo.css\"] },\n      (args) => {\n        logs.push(`A: ${path.basename(args.path)}`);\n      },\n    );\n\n    // Regex\n    builder.onTransformStaticFile(\n      { pluginName: \"B\", filter: /\\.css$/, exclude: [/foo\\.css$/] },\n      (args) => {\n        logs.push(`B: ${path.basename(args.path)}`);\n      },\n    );\n\n    // Glob\n    builder.onTransformStaticFile(\n      { pluginName: \"C\", filter: /\\.css$/, exclude: [\"**/foo.css\"] },\n      (args) => {\n        logs.push(`C: ${path.basename(args.path)}`);\n      },\n    );\n\n    await Deno.writeTextFile(path.join(tmp, \"foo.css\"), \"body { color: red; }\");\n    await Deno.writeTextFile(\n      path.join(tmp, \"bar.css\"),\n      \"body { color: blue; }\",\n    );\n    await builder.build();\n\n    expect(logs).toEqual([\"A: bar.css\", \"B: bar.css\", \"C: bar.css\"]);\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - workspace folder middleware on listen\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n\n    const builder = new Builder({\n      outDir: path.join(tmp, \"dist\"),\n      staticDir: tmp,\n    });\n    const app = new App();\n    const abort = new AbortController();\n    const port = 8011;\n    await builder.listen(() => Promise.resolve(app), {\n      port,\n      signal: abort.signal,\n    });\n\n    const res = await fetch(\n      `http://localhost:${port}/.well-known/appspecific/com.chrome.devtools.json`,\n    );\n    const json = await res.json();\n    await abort.abort();\n\n    expect(res.ok).toBe(true);\n    expect(res.status).toBe(200);\n    expect(res.headers.get(\"etag\")).toEqual(expect.any(String));\n    expect(json).toEqual({\n      workspace: {\n        root: builder.config.root,\n        uuid: expect.any(String),\n      },\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - .well-known static files\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n\n    const foo = path.join(tmp, \".well-known\", \"foo.json\");\n    await Deno.mkdir(path.dirname(foo), { recursive: true });\n    await Deno.writeTextFile(foo, JSON.stringify({ ok: true }));\n\n    const builder = new Builder({\n      outDir: path.join(tmp, \"dist\"),\n      staticDir: tmp,\n    });\n    const app = new App().use(staticFiles());\n    const abort = new AbortController();\n    const port = 8011;\n    await builder.listen(() => Promise.resolve(app), {\n      port,\n      signal: abort.signal,\n    });\n\n    const res = await fetch(\n      `http://localhost:${port}/.well-known/foo.json`,\n    );\n    const json = await res.json();\n    await abort.abort();\n\n    expect(res.ok).toBe(true);\n    expect(res.status).toBe(200);\n    expect(json).toEqual({ ok: true });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - write prod routePattern\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    await writeFiles(tmp, {\n      \"routes/foo/index.ts\": `export const handler = () => new Response(\"ok\")`,\n      \"main.ts\": `import { App } from \"fresh\";\nexport const app = new App().fsRoutes()`,\n    });\n\n    const builder = new Builder({\n      root: tmp,\n      outDir: path.join(tmp, \"dist\"),\n    });\n\n    await builder.build();\n\n    let text = \"fail\";\n    await withChildProcessServer(\n      { cwd: tmp, args: [\"serve\", \"-A\", \"--port=0\", \"dist/server.js\"] },\n      async (address) => {\n        const res = await fetch(`${address}/foo`);\n        text = await res.text();\n      },\n    );\n\n    expect(text).toEqual(\"ok\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - file:// islands\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    await writeFiles(tmp, {\n      \"other/Foo.tsx\": `export default () => <h1>ok</h1>;`,\n      \"routes/index.tsx\": `import Foo from \"../other/Foo.tsx\";\n      export default () => <Foo />;`,\n      \"main.ts\": `import { App } from \"fresh\";\nexport const app = new App().fsRoutes()`,\n    });\n\n    const builder = new Builder({\n      root: tmp,\n      outDir: path.join(tmp, \"dist\"),\n    });\n\n    const islandPath = path.join(tmp, \"other\", \"Foo.tsx\");\n    builder.registerIsland(path.toFileUrl(islandPath).href);\n\n    await builder.build();\n\n    let text = \"fail\";\n    await withChildProcessServer(\n      { cwd: tmp, args: [\"serve\", \"-A\", \"--port=0\", \"dist/server.js\"] },\n      async (address) => {\n        const res = await fetch(address);\n        text = await res.text();\n      },\n    );\n\n    expect(text).toContain(\"<h1>ok</h1>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - dev server doesn't overtake _error handler\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    const app = new App()\n      .onError(\"*\", () => new Response(\"it works\"))\n      .get(\"/\", () => new Response(\"no\"));\n\n    const builder = new Builder({\n      root: tmp,\n      outDir: path.join(tmp, \"dist\"),\n    });\n\n    const controller = new AbortController();\n    await builder.listen(() => Promise.resolve<App<unknown>>(app), {\n      signal: controller.signal,\n      async onListen(addr) {\n        const res = await fetch(`http://localhost:${addr.port}/invalid`);\n\n        const text = await res.text();\n        expect(text).toEqual(\"it works\");\n\n        controller.abort();\n      },\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - dev server supports basePath\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    const app = new App({ basePath: \"/foo/bar\" })\n      .get(\"/\", () => new Response(\"ok\"))\n      .get(\"/asdf\", () => new Response(\"ok\"));\n\n    const builder = new Builder({\n      root: tmp,\n      outDir: path.join(tmp, \"dist\"),\n    });\n\n    const controller = new AbortController();\n    let address;\n    await builder.listen(() => Promise.resolve<App<unknown>>(app), {\n      signal: controller.signal,\n      onListen(addr) {\n        address = `http://localhost:${addr.port}`;\n      },\n    });\n\n    const res = await fetch(`${address}/foo/bar/asdf`);\n\n    const text = await res.text();\n    expect(text).toEqual(\"ok\");\n\n    controller.abort();\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - serves static files in subdir\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    const app = new App()\n      .use(staticFiles())\n      .get(\"/\", () => new Response(\"no\"));\n\n    await writeFiles(tmp, {\n      \"static/foo.txt\": \"ok\",\n      \"static/test/foo.txt\": \"ok\",\n    });\n\n    const builder = new Builder({\n      root: tmp,\n    });\n\n    const controller = new AbortController();\n    const waiter = Promise.withResolvers<void>();\n    await builder.listen(() => Promise.resolve<App<unknown>>(app), {\n      signal: controller.signal,\n      async onListen(addr) {\n        try {\n          let res = await fetch(`http://localhost:${addr.port}/foo.txt`);\n          expect(await res.text()).toEqual(\"ok\");\n\n          res = await fetch(`http://localhost:${addr.port}/test/foo.txt`);\n          expect(await res.text()).toEqual(\"ok\");\n\n          controller.abort();\n          waiter.resolve();\n        } catch (err) {\n          waiter.reject(err);\n        }\n      },\n    });\n\n    await waiter.promise;\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - serves static files in subdir in prod\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    await writeFiles(tmp, {\n      \"main.ts\": `import { App, staticFiles } from \"fresh\";\nexport const app = new App()\n  .use(staticFiles());`,\n      \"static/foo.txt\": \"ok\",\n      \"static/test/foo.txt\": \"ok\",\n    });\n\n    await new Builder({ root: tmp }).build();\n\n    await withChildProcessServer(\n      { cwd: tmp, args: [\"serve\", \"-A\", \"--port=0\", \"_fresh/server.js\"] },\n      async (address) => {\n        let res = await fetch(`${address}/foo.txt`);\n        expect(await res.text()).toEqual(\"ok\");\n\n        res = await fetch(`${address}/test/foo.txt`);\n        expect(await res.text()).toEqual(\"ok\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - custom server entry\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    await writeFiles(tmp, {\n      \"other.ts\": `import { App, staticFiles } from \"fresh\";\nexport const app = new App()\n  .get(\"/\", () => new Response(\"ok\"));`,\n    });\n\n    await new Builder({ root: tmp, serverEntry: \"other.ts\" }).build();\n\n    await withChildProcessServer(\n      { cwd: tmp, args: [\"serve\", \"-A\", \"--port=0\", \"_fresh/server.js\"] },\n      async (address) => {\n        const res = await fetch(`${address}`);\n        expect(await res.text()).toEqual(\"ok\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - dev server error_overlay ignores _app and _layout\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n    const appLayoutSpy = fn(() => \"app or layout\") as () => string;\n\n    const app = new App()\n      .appWrapper(appLayoutSpy)\n      .layout(\"/\", appLayoutSpy);\n\n    const builder = new Builder({\n      root: tmp,\n      outDir: path.join(tmp, \"dist\"),\n    });\n\n    const controller = new AbortController();\n    await builder.listen(() => Promise.resolve<App<unknown>>(app), {\n      signal: controller.signal,\n      async onListen(addr) {\n        const res = await fetch(\n          `http://localhost:${addr.port}/${DEV_ERROR_OVERLAY_URL}?message=__ok__`,\n        );\n\n        const text = await res.text();\n        expect(text).toMatch(/__ok__/);\n        expect(appLayoutSpy).toHaveBeenCalledTimes(0);\n\n        controller.abort();\n      },\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  // Currently waiting on bug fixes in @deno/loader\n  // to support resolving without passing entry points\n  ignore: true,\n  name: \"Builder - mapped islands\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    await writeFiles(tmp, {\n      \"other/Foo.tsx\": `export default () => <h1>ok</h1>;`,\n      \"routes/index.tsx\": `import Foo from \"foo-island\";\n      export default () => <Foo />;`,\n      \"main.ts\": `import { App } from \"fresh\";\nexport const app = new App().fsRoutes()`,\n      \"deno.json\": JSON.stringify({\n        imports: { \"foo-island\": \"other/Foo.tsx\" },\n      }),\n    });\n\n    const builder = new Builder({\n      root: tmp,\n      outDir: path.join(tmp, \"dist\"),\n    });\n\n    builder.registerIsland(\"foo-island\");\n\n    await builder.build();\n\n    let text = \"fail\";\n    await withChildProcessServer(\n      { cwd: tmp, args: [\"serve\", \"-A\", \"--port=0\", \"dist/server.js\"] },\n      async (address) => {\n        const res = await fetch(address);\n        text = await res.text();\n      },\n    );\n\n    expect(text).toContain(\"<h1>ok</h1>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - source maps\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    await writeFiles(tmp, {\n      \"islands/Foo.tsx\": `export const Foo = () => <h1>hello</h1>`,\n      \"routes/index.ts\": `export const handler = () => new Response(\"ok\")`,\n      \"main.ts\": `import { App } from \"fresh\";\nexport const app = new App().fsRoutes()`,\n    });\n\n    const builder = new Builder({\n      root: tmp,\n      outDir: path.join(tmp, \"dist\"),\n      sourceMap: {\n        kind: \"external\",\n        sourceRoot: \"foo\",\n        sourcesContent: true,\n      },\n    });\n    await builder.build();\n\n    const assetDir = path.join(\n      builder.config.outDir,\n      \"static\",\n      \"_fresh\",\n      \"js\",\n      builder.config.buildId,\n    );\n    const entries = await Array.fromAsync(Deno.readDir(assetDir));\n\n    const map = entries.find((entry) =>\n      entry.isFile && entry.name.endsWith(\".js.map\")\n    );\n    if (!map) throw new Error(`Sourcemap not found`);\n\n    const content = await Deno.readTextFile(path.join(assetDir, map.name));\n\n    const json = JSON.parse(content) as {\n      version: 3;\n      sources: string[];\n      sourceRoot?: string;\n      sourcesContent: string[];\n      mappings: string;\n      names?: string[];\n    };\n\n    expect(json.sourcesContent.length).toBeGreaterThan(0);\n    expect(json.sourcesContent.length).toBeGreaterThan(0);\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - compile entry\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    await using _outDir = await withTmpDir();\n    const tmp = _tmp.dir;\n\n    await writeFiles(tmp, {\n      \"deno.json\": JSON.stringify({\n        links: [path.relative(tmp, root)],\n        workspace: [],\n        compilerOptions: {\n          jsx: \"react-jsx\",\n          jsxImportSource: \"preact\",\n        },\n        imports: {\n          \"fresh\": \"jsr:@fresh/core@*\",\n          \"preact\": \"npm:preact@*\",\n          \"@preact/signals\": \"npm:@preact/signals@*\",\n        },\n      }),\n      \"islands/Foo.tsx\": `export const Foo = () => <h1>hello</h1>`,\n      \"routes/index.ts\": `export const handler = () => new Response(\"ok\")`,\n      \"static/foo.txt\": \"ok\",\n      \"main.ts\": `import { App, staticFiles } from \"fresh\";\nexport const app = new App()\n  .use(staticFiles())\n  .fsRoutes();`,\n    });\n\n    const builder = new Builder({\n      root: tmp,\n    });\n    await builder.build();\n\n    const outBin = path.join(_outDir.dir, \"fresh-compiled\");\n    const bin = Deno.build.os === \"windows\" ? \"deno.exe\" : \"deno\";\n    const cp = await new Deno.Command(bin, {\n      args: [\n        \"compile\",\n        \"-A\",\n        \"--include\",\n        \"static/\",\n        \"--include\",\n        \"_fresh\",\n        \"--output\",\n        outBin,\n        path.join(\"_fresh\", \"compiled-entry.js\"),\n      ],\n      cwd: tmp,\n    }).output();\n\n    const { stderr, stdout } = getStdOutput(cp);\n    // deno-lint-ignore no-console\n    console.log(stdout);\n    // deno-lint-ignore no-console\n    console.log(stderr);\n\n    await withChildProcessServer({\n      bin: outBin,\n      cwd: _outDir.dir,\n      args: [],\n      env: {\n        PORT: \"0\",\n        HOSTNAME: \"127.0.0.1\",\n      },\n    }, async (address) => {\n      let res = await fetch(address);\n      expect(await res.text()).toEqual(\"ok\");\n\n      res = await fetch(`${address}/foo.txt`);\n      expect(await res.text()).toEqual(\"ok\");\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"Builder - island that imports json entry\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    await using _outDir = await withTmpDir();\n    const tmp = _tmp.dir;\n\n    await writeFiles(tmp, {\n      \"deno.json\": JSON.stringify({\n        links: [path.relative(tmp, root)],\n        workspace: [],\n        compilerOptions: {\n          jsx: \"react-jsx\",\n          jsxImportSource: \"preact\",\n        },\n        imports: {\n          \"fresh\": \"jsr:@fresh/core@*\",\n          \"preact\": \"npm:preact@*\",\n          \"@preact/signals\": \"npm:@preact/signals@*\",\n          \"mime-db\": \"npm:mime-db@*\",\n        },\n      }),\n      \"islands/Foo.tsx\": `import * as mime from \"mime-db\"\nexport const Foo = () => {\n  console.log(mime);\n  return <h1>ok</h1>\n}`,\n      \"routes/index.ts\": `export default () => <Foo />`,\n      \"main.ts\": `import { App, staticFiles } from \"fresh\";\nexport const app = new App()\n  .use(staticFiles())\n  .fsRoutes();`,\n    });\n\n    const builder = new Builder({\n      root: tmp,\n    });\n    await builder.build();\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test(\"specToName\", () => {\n  // HTTP\n  expect(specToName(\"http://example.com\")).toEqual(\"example\");\n  expect(specToName(\"http://example.com:8000\")).toEqual(\"example\");\n  expect(specToName(\"http://example.com:8000/foo/bar\")).toEqual(\n    \"bar\",\n  );\n\n  // HTTPS\n  expect(specToName(\"https://example.com\")).toEqual(\"example\");\n  expect(specToName(\"https://example.com:8000\")).toEqual(\"example\");\n  expect(specToName(\"https://example.com:8000/foo/bar\")).toEqual(\n    \"bar\",\n  );\n\n  // JSR\n  expect(specToName(\"jsr:@foo/bar\")).toEqual(\"foo_bar\");\n  expect(specToName(\"jsr:@foo/bar@1.0.0\")).toEqual(\"foo_bar\");\n  expect(specToName(\"jsr:@foo/bar@^1.0.0\")).toEqual(\"foo_bar\");\n  expect(specToName(\"jsr:@foo/bar@~1.0.0\")).toEqual(\"foo_bar\");\n  expect(specToName(\"jsr:@foo/bar@~1.0.0-alpha.32\")).toEqual(\"foo_bar\");\n  expect(specToName(\"jsr:@foo/bar@~1.0.0-alpha.32/asdf\")).toEqual(\"asdf\");\n  expect(specToName(\"jsr:@foo/bar/asdf\")).toEqual(\"asdf\");\n\n  // npm\n  expect(specToName(\"npm:foo\")).toEqual(\"foo\");\n  expect(specToName(\"npm:foo/bar\")).toEqual(\"bar\");\n  expect(specToName(\"npm:foo@1.0.0\")).toEqual(\"foo\");\n  expect(specToName(\"npm:foo@^1.0.0\")).toEqual(\"foo\");\n  expect(specToName(\"npm:foo@~1.0.0-alpha.32\")).toEqual(\"foo\");\n  expect(specToName(\"npm:@foo/bar\")).toEqual(\"foo_bar\");\n  expect(specToName(\"npm:@foo/bar/asdf\")).toEqual(\"asdf\");\n  expect(specToName(\"npm:@foo/bar@1.0.0\")).toEqual(\"foo_bar\");\n  expect(specToName(\"npm:@foo/bar@^1.0.0\")).toEqual(\"foo_bar\");\n  expect(specToName(\"npm:@foo/bar@~1.0.0-alpha.32\")).toEqual(\"foo_bar\");\n\n  // other\n  expect(specToName(\"foo\")).toEqual(\"foo\");\n  expect(specToName(\"@foo/bar\")).toEqual(\"foo_bar\");\n  expect(specToName(\"foo/bar\")).toEqual(\"bar\");\n  expect(specToName(\"@foo/bar/asdf\")).toEqual(\"asdf\");\n\n  expect(specToName(\"islands/foo.v2.tsx\")).toEqual(\"foo_v2\");\n  expect(specToName(\"/islands/_bar-baz-...-$.tsx\")).toEqual(\"_bar_baz_$\");\n  expect(specToName(\"/islands/1_hello.tsx\")).toEqual(\"_hello\");\n});\n\nDeno.test({\n  name: \"Builder - island named after global object (Map)\",\n  fn: async () => {\n    const root = path.join(import.meta.dirname!, \"..\", \"..\");\n    await using _tmp = await withTmpDir({ dir: root, prefix: \"tmp_builder_\" });\n    const tmp = _tmp.dir;\n\n    await writeFiles(tmp, {\n      \"islands/Map.tsx\": `import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport default function Map() {\n  const ready = useSignal(false);\n  const count = useSignal(0);\n\n  useEffect(() => {\n    ready.value = true;\n  }, []);\n\n  return (\n    <div class={ready.value ? \"ready\" : \"\"}>\n      <p class=\"output\">{count.value}</p>\n      <button\n        type=\"button\"\n        class=\"increment\"\n        onClick={() => count.value = count.peek() + 1}\n      >\n        increment\n      </button>\n    </div>\n  );\n}`,\n      \"routes/index.tsx\": `import Map from \"../islands/Map.tsx\";\nexport default () => <Map />;`,\n      \"main.ts\": `import { App } from \"fresh\";\nexport const app = new App().fsRoutes();`,\n    });\n\n    const builder = new Builder({\n      root: tmp,\n      outDir: path.join(tmp, \"dist\"),\n    });\n\n    // Register the island manually\n    const islandPath = path.join(tmp, \"islands\", \"Map.tsx\");\n    builder.registerIsland(path.toFileUrl(islandPath).href);\n\n    await builder.build();\n\n    await withChildProcessServer(\n      { cwd: tmp, args: [\"serve\", \"-A\", \"--port=0\", \"dist/server.js\"] },\n      async (address) => {\n        const res = await fetch(address);\n        const html = await res.text();\n\n        // Verify the fix: UniqueNamer prefixes \"Map\" with underscore to prevent shadowing\n        // Without the fix: import Map from \"...\" and boot({Map}, ...) - shadows global Map\n        // With the fix: import _Map_N from \"...\" and boot({_Map_N}, ...) - no shadowing\n        expect(html).toMatch(/import\\s+_Map_\\d+\\s+from/);\n        expect(html).toMatch(/boot\\(\\s*\\{\\s*_Map_\\d+\\s*\\}/);\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n"
  },
  {
    "path": "packages/fresh/src/dev/check.ts",
    "content": "import * as JSONC from \"@std/jsonc\";\nimport * as path from \"@std/path\";\n\nexport interface DenoConfig {\n  workspace?: string[];\n  compilerOptions?: {\n    jsx?: string;\n    jsxImportSource?: string;\n    jsxPrecompileSkipElements?: string[];\n  };\n}\n\nexport async function checkDenoCompilerOptions(root: string) {\n  const denoJson = await findNearestDenoConfigWithCompilerOptions(\n    root,\n  );\n\n  const jsxImportSource = denoJson.config.compilerOptions?.jsxImportSource;\n  if (jsxImportSource === undefined) {\n    throw new Error(\n      `Option compilerOptions > jsxImportSource not set in: ${denoJson.filePath}`,\n    );\n  }\n\n  // Check precompile option\n  if (denoJson.config.compilerOptions?.jsx === \"precompile\") {\n    const expected = [\n      \"a\",\n      \"img\",\n      \"source\",\n      \"body\",\n      \"html\",\n      \"head\",\n      \"title\",\n      \"meta\",\n      \"script\",\n      \"link\",\n      \"style\",\n      \"base\",\n      \"noscript\",\n      \"template\",\n    ];\n    const skipped = denoJson.config.compilerOptions.jsxPrecompileSkipElements;\n    if (!skipped || expected.some((name) => !skipped.includes(name))) {\n      throw new Error(\n        `Expected option compilerOptions > jsxPrecompileSkipElements to contain ${\n          expected.map((name) => `\"${name}\"`).join(\", \")\n        }`,\n      );\n    }\n  }\n\n  return { jsxImportSource, denoJson: denoJson.filePath };\n}\n\nexport async function findNearestDenoConfigWithCompilerOptions(\n  directory: string,\n): Promise<{ config: DenoConfig; filePath: string }> {\n  let dir = directory;\n  while (true) {\n    for (const name of [\"deno.json\", \"deno.jsonc\"]) {\n      const filePath = path.join(dir, name);\n      try {\n        const file = await Deno.readTextFile(filePath);\n        let config;\n        if (name.endsWith(\".jsonc\")) {\n          config = JSONC.parse(file);\n        } else {\n          config = JSON.parse(file);\n        }\n        if (config.compilerOptions) return { config, filePath };\n        if (config.workspace) break;\n        break;\n      } catch (err) {\n        if (!(err instanceof Deno.errors.NotFound)) {\n          throw err;\n        }\n      }\n    }\n    const parent = path.dirname(dir);\n    if (parent === dir) break;\n    dir = parent;\n  }\n\n  throw new Error(\n    `Could not find a deno.json or deno.jsonc file in the current directory or any parent directory that contains a 'compilerOptions' field.`,\n  );\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/dev_build_cache.ts",
    "content": "import {\n  type BuildCache,\n  IslandPreparer,\n  type StaticFile,\n} from \"../build_cache.ts\";\nimport * as path from \"@std/path\";\nimport * as pathWin32 from \"@std/path/windows\";\nimport { encodeHex } from \"@std/encoding/hex\";\nimport { fsAdapter } from \"../fs.ts\";\nimport type { FileTransformer } from \"./file_transformer.ts\";\nimport { assertInDir, pathToSpec } from \"../utils.ts\";\nimport type { ResolvedBuildConfig } from \"./builder.ts\";\nimport { fsItemsToCommands, type FsRouteFile } from \"../fs_routes.ts\";\nimport type { Command } from \"../commands.ts\";\nimport type { ServerIslandRegistry } from \"../context.ts\";\nimport { contentType as getStdContentType } from \"@std/media-types/content-type\";\n\nconst WINDOWS_SEPARATOR = pathWin32.SEPARATOR;\n\nexport interface MemoryFile {\n  hash: string | null;\n  contentType: string;\n  content: Uint8Array;\n}\n\nexport interface IslandModChunk {\n  name: string;\n  server: string;\n  browser: string | null;\n  css: string[];\n}\n\nexport type FsRouteFileNoMod<State> = Omit<FsRouteFile<State>, \"mod\"> & {\n  lazy: boolean;\n};\n\nexport interface FsRoute<State> {\n  id: string;\n  dir: string;\n  files: FsRouteFileNoMod<State>[];\n}\n\nexport interface DevBuildCache<State> extends BuildCache<State> {\n  islandModNameToChunk: Map<string, IslandModChunk>;\n  addUnprocessedFile(pathname: string, dir: string): void;\n  addProcessedFile(\n    pathname: string,\n    content: Uint8Array,\n    hash: string | null,\n  ): Promise<void>;\n  flush(): Promise<void>;\n  prepare(): Promise<void>;\n}\n\nexport class MemoryBuildCache<State> implements DevBuildCache<State> {\n  #processedFiles = new Map<string, MemoryFile>();\n  #unprocessedFiles = new Map<string, string>();\n  #config: ResolvedBuildConfig;\n  #transformer: FileTransformer;\n  islandModNameToChunk = new Map<string, IslandModChunk>();\n  #fsRoutes: FsRoute<State>;\n  #commands: Command<State>[] = [];\n  root: string;\n  islandRegistry: ServerIslandRegistry = new Map();\n  clientEntry: string;\n  features = { errorOverlay: false };\n\n  constructor(\n    config: ResolvedBuildConfig,\n    fsRoutes: FsRoute<State>,\n    transformer: FileTransformer,\n  ) {\n    if (config.mode === \"development\") {\n      this.features.errorOverlay = true;\n    }\n\n    this.#config = config;\n    this.#fsRoutes = fsRoutes;\n    this.#transformer = transformer;\n    this.root = config.root;\n\n    this.clientEntry = getClientEntry(config.buildId);\n  }\n\n  getEntryAssets(): string[] {\n    return [];\n  }\n\n  getFsRoutes(): Command<State>[] {\n    return this.#commands;\n  }\n\n  async readFile(pathname: string): Promise<StaticFile | null> {\n    const processed = this.#processedFiles.get(pathname);\n    if (processed !== undefined) {\n      return {\n        hash: processed.hash,\n        readable: processed.content,\n        size: processed.content.byteLength,\n        contentType: processed.contentType,\n        close: () => {},\n      };\n    }\n\n    const unprocessed = this.#unprocessedFiles.get(pathname);\n    if (unprocessed !== undefined) {\n      try {\n        const [stat, file] = await Promise.all([\n          Deno.stat(unprocessed),\n          Deno.open(unprocessed, { read: true }),\n        ]);\n\n        return {\n          hash: null,\n          size: stat.size,\n          readable: file.readable,\n          contentType: getContentType(unprocessed),\n          close: () => file.close(),\n        };\n      } catch {\n        return null;\n      }\n    }\n\n    let entry = pathname.startsWith(\"/\") ? pathname.slice(1) : pathname;\n    entry = path.join(this.#config.staticDir, entry);\n    const relative = path.relative(this.#config.staticDir, entry);\n    if (relative.startsWith(\"..\")) {\n      throw new Error(\n        `Processed file resolved outside of static dir ${entry}`,\n      );\n    }\n\n    // Might be a file that we still need to process\n    const transformed = await this.#transformer.process(\n      entry,\n      \"development\",\n      this.#config.target,\n    );\n\n    if (transformed !== null) {\n      for (let i = 0; i < transformed.length; i++) {\n        const file = transformed[i];\n        const relative = path.relative(this.#config.staticDir, file.path);\n        if (relative.startsWith(\"..\")) {\n          throw new Error(\n            `Processed file resolved outside of static dir ${file.path}`,\n          );\n        }\n        const pathname = new URL(relative, \"http://localhost\").pathname;\n\n        this.addProcessedFile(pathname, file.content, null);\n      }\n      if (this.#processedFiles.has(pathname)) {\n        return this.readFile(pathname);\n      }\n    } else {\n      try {\n        const filePath = path.join(this.#config.staticDir, pathname);\n        const relative = path.relative(this.#config.staticDir, filePath);\n        if (!relative.startsWith(\"..\") && (await Deno.stat(filePath)).isFile) {\n          const pathname = new URL(relative, \"http://localhost\").pathname;\n          this.addUnprocessedFile(pathname, this.#config.staticDir);\n          return this.readFile(pathname);\n        }\n      } catch (err) {\n        if (!(err instanceof Deno.errors.NotFound)) {\n          throw err;\n        }\n      }\n    }\n\n    return null;\n  }\n\n  addUnprocessedFile(pathname: string, dir: string): void {\n    this.#unprocessedFiles.set(\n      pathname,\n      path.join(dir, pathname),\n    );\n  }\n\n  // deno-lint-ignore require-await\n  async addProcessedFile(\n    pathname: string,\n    content: Uint8Array,\n    hash: string | null,\n  ): Promise<void> {\n    this.#processedFiles.set(pathname, {\n      content,\n      hash,\n      contentType: getContentType(pathname),\n    });\n  }\n\n  async flush(): Promise<void> {\n    const preparer = new IslandPreparer();\n\n    // Load islands\n    await Promise.all(\n      Array.from(this.islandModNameToChunk.entries()).map(\n        async ([name, chunk]) => {\n          const fileUrl = maybeToFileUrl(chunk.server);\n          const mod = await import(fileUrl);\n\n          if (chunk.browser === null) {\n            throw new Error(`Unexpected missing browser chunk`);\n          }\n\n          preparer.prepare(this.islandRegistry, mod, chunk.browser, name, []);\n        },\n      ),\n    );\n  }\n\n  async prepare(): Promise<void> {\n    // Load FS routes\n    const files = await Promise.all(this.#fsRoutes.files.map(async (file) => {\n      const fileUrl = maybeToFileUrl(file.filePath);\n      return {\n        ...file,\n        mod: file.lazy ? () => import(fileUrl) : await import(fileUrl),\n      };\n    }));\n    this.#commands = fsItemsToCommands(files);\n  }\n}\n\nexport class DiskBuildCache<State> implements DevBuildCache<State> {\n  #processedFiles = new Map<string, string | null>();\n  #unprocessedFiles = new Map<string, string>();\n  #transformer: FileTransformer;\n  #config: ResolvedBuildConfig;\n  islandModNameToChunk = new Map<string, IslandModChunk>();\n  #fsRoutes: FsRoute<State>;\n  root: string;\n  islandRegistry: ServerIslandRegistry = new Map();\n  clientEntry: string = \"\";\n  features = { errorOverlay: false };\n\n  constructor(\n    config: ResolvedBuildConfig,\n    fsRoutes: FsRoute<State>,\n    transformer: FileTransformer,\n  ) {\n    if (config.mode === \"development\") {\n      this.features.errorOverlay = true;\n    }\n    this.#fsRoutes = fsRoutes;\n    this.#transformer = transformer;\n    this.#config = config;\n    this.root = config.root;\n  }\n\n  getEntryAssets(): string[] {\n    return [];\n  }\n\n  getFsRoutes(): Command<State>[] {\n    return [];\n  }\n\n  addUnprocessedFile(pathname: string, dir: string): void {\n    this.#unprocessedFiles.set(\n      pathname.replaceAll(WINDOWS_SEPARATOR, \"/\"),\n      path.join(dir, pathname),\n    );\n  }\n\n  async addProcessedFile(\n    pathname: string,\n    content: Uint8Array,\n    hash: string | null,\n  ) {\n    this.#processedFiles.set(pathname, hash);\n\n    const outDir = pathname === \"/metafile.json\"\n      ? this.#config.outDir\n      : path.join(this.#config.outDir, \"static\");\n    const filePath = path.join(outDir, pathname);\n    assertInDir(filePath, outDir);\n\n    await fsAdapter.mkdirp(path.dirname(filePath));\n    await Deno.writeFile(filePath, content);\n  }\n\n  // deno-lint-ignore require-await\n  async readFile(_pathname: string): Promise<StaticFile | null> {\n    throw new Error(\"Not implemented in build mode\");\n  }\n\n  async prepare(): Promise<void> {\n    // not needed\n  }\n\n  async flush(): Promise<void> {\n    const { staticDir, outDir, target, root } = this.#config;\n\n    if (await fsAdapter.isDirectory(staticDir)) {\n      const entries = fsAdapter.walk(staticDir, {\n        includeDirs: false,\n        includeFiles: true,\n        followSymlinks: false,\n        // Skip any folder or file starting with a \".\", but allow\n        // \".well-known\" for things like PWA manifests\n        skip: [/\\/\\.(?!well-known)[^/]+(\\/|$)/],\n      });\n\n      for await (const entry of entries) {\n        // OutDir might be inside static dir\n        if (!path.relative(outDir, entry.path).startsWith(\"..\")) {\n          continue;\n        }\n\n        const result = await this.#transformer.process(\n          entry.path,\n          \"production\",\n          target,\n        );\n\n        if (result !== null) {\n          for (let i = 0; i < result.length; i++) {\n            const file = result[i];\n            assertInDir(file.path, staticDir);\n            const pathname = `/${path.relative(staticDir, file.path)}`;\n            await this.addProcessedFile(pathname, file.content, null);\n          }\n        } else {\n          const relative = path.relative(staticDir, entry.path);\n          const pathname = `/${relative}`;\n          this.addUnprocessedFile(pathname, staticDir);\n        }\n      }\n    }\n\n    const staticFiles: PendingStaticFile[] = [];\n    for (const [name, filePath] of this.#unprocessedFiles.entries()) {\n      staticFiles.push({ filePath, pathname: name, hash: null });\n    }\n\n    for (const [name, maybeHash] of this.#processedFiles.entries()) {\n      // Ignore esbuild meta file. It's not intended for serving\n      if (name === \"/metafile.json\") {\n        continue;\n      }\n\n      const filePath = path.join(outDir, \"static\", name);\n      staticFiles.push({ filePath, pathname: name, hash: maybeHash });\n    }\n\n    const islandSpecifiers: string[] = [];\n    for (const spec of this.islandModNameToChunk.keys()) {\n      islandSpecifiers.push(spec);\n    }\n\n    const islands = Array.from(this.islandModNameToChunk.values());\n\n    await Deno.writeTextFile(\n      path.join(outDir, \"snapshot.js\"),\n      await generateSnapshotServer({\n        buildId: this.#config.buildId,\n        clientEntry: getClientEntry(this.#config.buildId),\n        staticFiles,\n        islands,\n        writeSpecifier: (filePath) => {\n          return pathToSpec(outDir, filePath);\n        },\n        fsRoutesFiles: this.#fsRoutes.files,\n        outDir: root,\n        entryAssets: [],\n      }),\n    );\n\n    // TODO: Make main file configurable\n    const appPath = path.relative(outDir, root);\n    await Deno.writeTextFile(\n      path.join(outDir, \"server.js\"),\n      generateServerEntry({\n        root: appPath,\n        serverEntry: pathToSpec(outDir, this.#config.serverEntry),\n        snapshotSpecifier: \"./snapshot.js\",\n      }),\n    );\n\n    await writeCompiledEntry(outDir);\n  }\n}\n\nconst EDIT_WARNING =\n  `// WARNING: DO NOT EDIT THIS FILE. It is autogenerated by Fresh.`;\n\nexport async function hashContent(\n  // This error poppued up since TS 5.9.2 but not sure why\n  // deno-lint-ignore no-explicit-any\n  content: Uint8Array<any> | ReadableStream<Uint8Array>,\n): Promise<string> {\n  const buffer = await new Response(content).arrayBuffer();\n\n  const hashBuf = await crypto.subtle.digest(\n    \"SHA-256\",\n    buffer,\n  );\n  return encodeHex(hashBuf);\n}\n\nexport function getContentType(filePath: string): string {\n  const ext = path.extname(filePath);\n  return getStdContentType(ext) ?? \"text/plain\";\n}\n\nfunction maybeToFileUrl(file: string) {\n  return file.startsWith(\"file://\") ? file : path.toFileUrl(file).href;\n}\n\nexport interface PendingStaticFile {\n  pathname: string;\n  filePath: string;\n  hash: string | null;\n}\n\nexport async function writeCompiledEntry(outDir: string) {\n  await Deno.writeTextFile(\n    path.join(outDir, \"compiled-entry.js\"),\n    `import fetcher from \"./server.js\";\n\nDeno.serve(\n  { port: Deno.env.get(\"PORT\"), hostname: Deno.env.get(\"HOSTNAME\") },\n  fetcher.fetch\n);`,\n  );\n}\n\nexport async function generateSnapshotServer(\n  options: {\n    outDir: string;\n    buildId: string;\n    clientEntry: string;\n    islands: IslandModChunk[];\n    // deno-lint-ignore no-explicit-any\n    fsRoutesFiles: FsRouteFileNoMod<any>[];\n    staticFiles: PendingStaticFile[];\n    entryAssets: string[];\n    writeSpecifier: (filePath: string) => string;\n  },\n): Promise<string> {\n  const {\n    islands,\n    writeSpecifier,\n    fsRoutesFiles,\n    outDir,\n  } = options;\n\n  const islandImports = islands\n    .map((item) => {\n      const spec = writeSpecifier(item.server);\n      return `import * as ${item.name} from \"${spec}\";`;\n    })\n    .join(\"\\n\");\n\n  const fsRouteImports = fsRoutesFiles\n    .map((item, i) => {\n      if (item.lazy) return null;\n      const spec = writeSpecifier(item.filePath);\n      return `import * as fsRoute_${i} from \"${spec}\"`;\n    })\n    .filter(Boolean)\n    .join(\"\\n\");\n\n  const islandMarkers = islands.map((item) => {\n    const browser = JSON.stringify(item.browser);\n    const name = JSON.stringify(item.name);\n    const css = JSON.stringify(item.css);\n    return `islandPreparer.prepare(islands, ${item.name}, ${browser}, ${name}, ${css});`;\n  }).join(\"\\n\");\n\n  const serializedFsRoutes = fsRoutesFiles\n    .map((item, i) => {\n      const id = JSON.stringify(item.id);\n      const pattern = JSON.stringify(item.pattern);\n      const type = JSON.stringify(item.type);\n      const routePattern = JSON.stringify(item.routePattern);\n\n      let mod = \"\";\n      if (item.lazy) {\n        const spec = writeSpecifier(item.filePath);\n        mod = `() => import(${JSON.stringify(spec)})`;\n      } else {\n        mod = `fsRoute_${i}`;\n      }\n\n      return `  { id: ${id}, mod: ${mod}, type: ${type}, pattern: ${pattern}, routePattern: ${routePattern} },`;\n    })\n    .join(\"\\n\");\n\n  const staticFiles = await Promise.all(\n    options.staticFiles.map(async (item) => {\n      return await prepareStaticFile(item, outDir);\n    }),\n  );\n\n  const entryAssets = options.entryAssets.map((url) => JSON.stringify(url))\n    .join(\",\\n\");\n\n  return `${EDIT_WARNING}\nimport { IslandPreparer } from \"fresh/internal\";\n${islandImports}\n${fsRouteImports}\n\nexport const clientEntry = ${JSON.stringify(options.clientEntry)}\nexport const version = ${JSON.stringify(options.buildId)}\n\nexport const islands = new Map();\nconst islandPreparer = new IslandPreparer();\n${islandMarkers}\n\nexport const staticFiles = new Map([\n${\n    staticFiles.map((def) =>\n      `  [${JSON.stringify(def.name)}, ${JSON.stringify(def)}]`\n    ).join(\",\\n\")\n  }\n]);\n\nexport const entryAssets = [${entryAssets}];\n\nexport const fsRoutes = [\n${serializedFsRoutes}\n];\n`.replaceAll(/\\n[\\n]+/g, \"\\n\\n\");\n}\n\nexport async function prepareStaticFile(\n  item: PendingStaticFile,\n  outDir: string,\n): Promise<\n  { name: string; hash: string; filePath: string; contentType: string }\n> {\n  const file = await Deno.open(item.filePath);\n  const hash = item.hash ? item.hash : await hashContent(file.readable);\n  const url = new URL(item.pathname, \"http://localhost\");\n\n  return {\n    name: url.pathname,\n    hash,\n    filePath: path.isAbsolute(item.filePath)\n      ? path.relative(outDir, item.filePath)\n      : item.filePath,\n    contentType: getContentType(item.filePath),\n  };\n}\n\nexport function generateServerEntry(\n  options: {\n    root: string;\n    serverEntry: string;\n    snapshotSpecifier: string;\n  },\n): string {\n  let rootPath = `path.join(import.meta.dirname, ${\n    JSON.stringify(options.root)\n  })`;\n  if (path.isAbsolute(options.root)) {\n    // deno-lint-ignore no-console\n    console.warn(\n      `WARN: using absolute root path in snapshot: \"${options.root}\"`,\n    );\n\n    rootPath = JSON.stringify(options.root);\n  }\n\n  return `${EDIT_WARNING}\nimport { setBuildCache, ProdBuildCache, path } from \"fresh/internal\";\nimport * as snapshot from \"${options.snapshotSpecifier}\";\nimport { app } from \"${options.serverEntry}\";\n\nconst root = ${rootPath};\nsetBuildCache(app, new ProdBuildCache(root, snapshot), \"production\");\n\nexport default {\n  fetch: app.handler()\n};\n`;\n}\n\nfunction getClientEntry(buildId: string) {\n  return `/_fresh/js/${buildId}/fresh-runtime.js`;\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/dev_build_cache_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport { MemoryBuildCache } from \"./dev_build_cache.ts\";\nimport { FileTransformer } from \"./file_transformer.ts\";\nimport { createFakeFs, withTmpDir } from \"../test_utils.ts\";\nimport type { ResolvedBuildConfig } from \"./builder.ts\";\n\nDeno.test({\n  name: \"MemoryBuildCache - should error if reading outside of staticDir\",\n  fn: async () => {\n    await using _tmp = await withTmpDir();\n    const tmp = _tmp.dir;\n    const config: ResolvedBuildConfig = {\n      serverEntry: \"main.ts\",\n      root: tmp,\n      mode: \"development\",\n      buildId: \"\",\n      ignore: [],\n      islandDir: \"\",\n      outDir: \"\",\n      routeDir: \"\",\n      staticDir: \"\",\n      target: \"latest\",\n    };\n    const fileTransformer = new FileTransformer(createFakeFs({}), tmp);\n    const buildCache = new MemoryBuildCache(\n      config,\n      { dir: \"\", files: [], id: \"\" },\n      fileTransformer,\n    );\n\n    const thrown = buildCache.readFile(\"../SECRETS.txt\");\n    const thrown2 = buildCache.readFile(\"./../../SECRETS.txt\");\n    const noThrown = buildCache.readFile(\"styles.css\");\n    const noThrown2 = buildCache.readFile(\".well-known/foo.txt\");\n    const noThrown3 = buildCache.readFile(\"./styles.css\");\n    const noThrown4 = buildCache.readFile(\"./.well-known/foo.txt\");\n    await buildCache.flush();\n\n    const err = \"Processed file resolved outside of static dir\";\n    await expect(thrown).rejects.toThrow(err);\n    await expect(thrown2).rejects.toThrow(err);\n    await expect(noThrown).resolves.toBe(null);\n    await expect(noThrown2).resolves.toBe(null);\n    await expect(noThrown3).resolves.toBe(null);\n    await expect(noThrown4).resolves.toBe(null);\n  },\n});\n"
  },
  {
    "path": "packages/fresh/src/dev/esbuild.ts",
    "content": "import { denoPlugin } from \"@deno/esbuild-plugin\";\nimport type { BuildOptions, Plugin as EsbuildPlugin } from \"esbuild\";\nimport * as path from \"@std/path\";\n\nexport interface FreshBundleOptions {\n  dev: boolean;\n  cwd: string;\n  buildId: string;\n  outDir: string;\n  denoJsonPath: string;\n  entryPoints: Record<string, string>;\n  target: string | string[];\n  jsxImportSource?: string;\n  sourceMap?: {\n    /** Documentation: https://esbuild.github.io/api/#sourcemap */\n    kind: BuildOptions[\"sourcemap\"];\n    /** Documentation: https://esbuild.github.io/api/#source-root */\n    sourceRoot?: BuildOptions[\"sourceRoot\"];\n    /** Documentation: https://esbuild.github.io/api/#sources-content */\n    sourcesContent?: BuildOptions[\"sourcesContent\"];\n  };\n}\n\nexport interface BuildOutput {\n  entryToChunk: Map<string, string>;\n  dependencies: Map<string, string[]>;\n  files: Array<{ hash: string | null; contents: Uint8Array; path: string }>;\n}\n\nlet esbuild: null | typeof import(\"esbuild\") = null;\n\nconst PREACT_ENV = Deno.env.get(\"PREACT_PATH\");\n\nexport async function bundleJs(\n  options: FreshBundleOptions,\n): Promise<BuildOutput> {\n  if (esbuild === null) {\n    await startEsbuild();\n  }\n\n  try {\n    await Deno.mkdir(options.cwd, { recursive: true });\n  } catch (err) {\n    if (!(err instanceof Deno.errors.AlreadyExists)) {\n      throw err;\n    }\n  }\n\n  const bundle = await esbuild!.build({\n    entryPoints: options.entryPoints,\n\n    platform: \"browser\",\n    target: options.target,\n\n    format: \"esm\",\n    bundle: true,\n    splitting: true,\n    treeShaking: true,\n    sourcemap: options.dev ? \"linked\" : options.sourceMap?.kind,\n    sourceRoot: options.dev ? undefined : options.sourceMap?.sourceRoot,\n    sourcesContent: options.dev ? undefined : options.sourceMap?.sourcesContent,\n    minify: !options.dev,\n    logOverride: {\n      \"suspicious-nullish-coalescing\": \"silent\",\n      \"unsupported-jsx-comment\": \"silent\",\n    },\n\n    jsxDev: options.dev,\n    jsx: \"automatic\",\n    jsxImportSource: options.jsxImportSource ?? \"preact\",\n\n    absWorkingDir: options.cwd,\n    outdir: \".\",\n    write: false,\n    metafile: true,\n\n    define: {\n      \"process.env.NODE_ENV\": JSON.stringify(\n        options.dev ? \"development\" : \"production\",\n      ),\n    },\n\n    plugins: [\n      preactDebugger(PREACT_ENV),\n      buildIdPlugin(options.buildId),\n      windowsPathFixer(),\n      denoPlugin({\n        preserveJsx: true,\n        debug: false,\n        publicEnvVarPrefix: \"FRESH_PUBLIC_\",\n      }),\n    ],\n  });\n\n  const files: BuildOutput[\"files\"] = [];\n  for (let i = 0; i < bundle.outputFiles.length; i++) {\n    const outputFile = bundle.outputFiles[i];\n    const relative = path.relative(options.cwd, outputFile.path);\n    files.push({\n      path: relative,\n      contents: outputFile.contents,\n      hash: outputFile.hash,\n    });\n  }\n\n  files.push({\n    path: \"metafile.json\",\n    contents: new TextEncoder().encode(JSON.stringify(bundle.metafile)),\n    hash: null,\n  });\n\n  const entryToChunk = new Map<string, string>();\n  const dependencies = new Map<string, string[]>();\n\n  const entryToName = new Map(\n    Array.from(Object.entries(options.entryPoints)).map(\n      (entry) => [entry[1], entry[0]],\n    ),\n  );\n\n  if (bundle.metafile) {\n    const metaOutputs = new Map(Object.entries(bundle.metafile.outputs));\n\n    for (const [entryPath, entry] of metaOutputs.entries()) {\n      const imports = entry.imports\n        .filter(({ kind }) => kind === \"import-statement\")\n        .map(({ path }) => path);\n      dependencies.set(entryPath, imports);\n\n      // Map entries to chunks\n      if (entryPath !== \"fresh-runtime.js\" && entry.entryPoint !== undefined) {\n        const basename = path.basename(\n          entryPath,\n          path.extname(entryPath),\n        );\n\n        const filePath = options.entryPoints[basename];\n\n        const name = entryToName.get(filePath)!;\n        entryToChunk.set(name, entryPath);\n      }\n    }\n  }\n\n  if (!options.dev) {\n    esbuild = null;\n  }\n\n  return {\n    files,\n    entryToChunk,\n    dependencies,\n  };\n}\n\nlet initialized = false;\n\nexport async function startEsbuild() {\n  esbuild = Deno.env.get(\"FRESH_ESBUILD_LOADER\") === \"portable\"\n    ? await import(\"esbuild-wasm\")\n    : await import(\"esbuild\");\n\n  if (!initialized) {\n    await esbuild.initialize({});\n    initialized = true;\n  }\n}\n\nfunction buildIdPlugin(buildId: string): EsbuildPlugin {\n  return {\n    name: \"fresh-build-id\",\n    setup(build) {\n      build.onResolve({ filter: /^(jsr:)?@fresh\\/build-id/ }, (args) => {\n        return {\n          path: args.path,\n          namespace: \"fresh-internal-build-id\",\n        };\n      });\n      build.onLoad({\n        filter: /.*/,\n        namespace: \"fresh-internal-build-id\",\n      }, () => {\n        return {\n          contents: `export const BUILD_ID = \"${buildId}\";`,\n        };\n      });\n    },\n  };\n}\n\nfunction toPreactModPath(mod: string): string {\n  if (mod === \"preact/debug\") {\n    return path.join(\"debug\", \"dist\", \"debug.module.js\");\n  } else if (mod === \"preact/hooks\") {\n    return path.join(\"hooks\", \"dist\", \"hooks.module.js\");\n  } else if (mod === \"preact/devtools\") {\n    return path.join(\"devtools\", \"dist\", \"devtools.module.js\");\n  } else if (mod === \"preact/compat\") {\n    return path.join(\"compat\", \"dist\", \"compat.module.js\");\n  } else if (mod === \"preact/jsx-runtime\" || mod === \"preact/jsx-dev-runtime\") {\n    return path.join(\"jsx-runtime\", \"dist\", \"jsxRuntime.module.js\");\n  } else {\n    return path.join(\"dist\", \"preact.module.js\");\n  }\n}\n\nfunction preactDebugger(preactPath: string | undefined): EsbuildPlugin {\n  return {\n    name: \"fresh-preact-debugger\",\n    setup(build) {\n      if (preactPath === undefined) return;\n\n      build.onResolve({ filter: /^preact/ }, (args) => {\n        const resolved = path.resolve(preactPath, toPreactModPath(args.path));\n\n        return {\n          path: resolved,\n        };\n      });\n    },\n  };\n}\n\nfunction windowsPathFixer(): EsbuildPlugin {\n  return {\n    name: \"fresh-fix-windows\",\n    setup(build) {\n      if (Deno.build.os === \"windows\") {\n        build.onResolve({ filter: /\\.*/ }, (args) => {\n          if (args.path.startsWith(\"\\\\\")) {\n            const normalized = path.resolve(args.path);\n            return {\n              path: normalized,\n            };\n          }\n        });\n      }\n    },\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/file_transformer.ts",
    "content": "import { globToRegExp, isGlob } from \"@std/path\";\nimport type { FsAdapter } from \"../fs.ts\";\nimport { BUILD_ID } from \"@fresh/build-id\";\nimport { assetInternal } from \"../runtime/shared_internal.ts\";\n\nexport type TransformMode = \"development\" | \"production\";\n\nexport interface OnTransformOptions {\n  pluginName: string;\n  filter: RegExp;\n  exclude?: Array<string | RegExp>;\n}\n\nexport interface OnTransformResult {\n  content: string | Uint8Array;\n  path?: string;\n  map?: string | Uint8Array;\n}\n\nexport interface OnTransformArgs {\n  path: string;\n  target: string | string[];\n  text: string;\n  content: Uint8Array;\n  mode: TransformMode;\n  root: string;\n}\nexport type TransformFn = (\n  args: OnTransformArgs,\n) =>\n  | void\n  | OnTransformResult\n  | Array<{ path: string } & Omit<OnTransformResult, \"path\">>\n  | Promise<\n    | void\n    | OnTransformResult\n    | Array<{ path: string } & Omit<OnTransformResult, \"path\">>\n  >;\n\nexport interface Transformer {\n  options: OnTransformOptions;\n  fn: TransformFn;\n}\n\nexport interface ProcessedFile {\n  path: string;\n  content: Uint8Array;\n  map: Uint8Array | null;\n  inputFiles: string[];\n}\n\ninterface TransformReq {\n  newFile: boolean;\n  filePath: string;\n  content: Uint8Array;\n  map: null | Uint8Array;\n  inputFiles: string[];\n}\n\nexport class FileTransformer {\n  #transformers: Transformer[] = [];\n  #fs: FsAdapter;\n  #root: string;\n\n  constructor(fs: FsAdapter, root: string) {\n    this.#fs = fs;\n    this.#root = root;\n  }\n\n  onTransform(options: OnTransformOptions, callback: TransformFn): void {\n    this.#transformers.push({ options, fn: callback });\n  }\n\n  async process(\n    filePath: string,\n    mode: TransformMode,\n    target: string | string[],\n  ): Promise<ProcessedFile[] | null> {\n    // Pre-check if we have any transformer for this file at all\n    let hasTransformer = false;\n    for (let i = 0; i < this.#transformers.length; i++) {\n      if (this.#transformers[i].options.filter.test(filePath)) {\n        hasTransformer = true;\n        break;\n      }\n    }\n\n    if (!hasTransformer) {\n      return null;\n    }\n\n    let content: Uint8Array;\n    try {\n      content = await this.#fs.readFile(filePath);\n    } catch (err) {\n      if (err instanceof Deno.errors.NotFound) {\n        return null;\n      }\n\n      throw err;\n    }\n\n    const queue: TransformReq[] = [{\n      newFile: false,\n      content,\n      filePath,\n      map: null,\n      inputFiles: [filePath],\n    }];\n    const outFiles: ProcessedFile[] = [];\n\n    const seen = new Set<string>();\n\n    let req: TransformReq | undefined = undefined;\n    while ((req = queue.pop()) !== undefined) {\n      if (seen.has(req.filePath)) continue;\n      seen.add(req.filePath);\n\n      let transformed = false;\n      outer: for (let i = 0; i < this.#transformers.length; i++) {\n        const transformer = this.#transformers[i];\n\n        const { options, fn } = transformer;\n        options.filter.lastIndex = 0;\n        if (!options.filter.test(req.filePath)) {\n          continue;\n        }\n\n        // Check if file is excluded\n        if (options.exclude !== undefined) {\n          for (let j = 0; j < options.exclude.length; j++) {\n            const exclude = options.exclude[j];\n            if (exclude instanceof RegExp) {\n              if (exclude.test(filePath)) {\n                continue outer;\n              }\n            } else if (isGlob(exclude)) {\n              const regex = globToRegExp(exclude);\n              if (regex.test(filePath)) {\n                continue outer;\n              }\n            } else if (filePath.includes(exclude)) {\n              continue outer;\n            }\n          }\n        }\n\n        const result = await fn({\n          path: req.filePath,\n          mode,\n          target,\n          content: req!.content,\n          root: this.#root,\n          get text() {\n            return new TextDecoder().decode(req!.content);\n          },\n        });\n\n        if (result !== undefined) {\n          if (Array.isArray(result)) {\n            for (let i = 0; i < result.length; i++) {\n              const item = result[i];\n              if (item.path === undefined) {\n                throw new Error(\n                  `The \".path\" property must be set when returning multiple files in a transformer. [${transformer.options.pluginName}]`,\n                );\n              }\n\n              const outContent = typeof item.content === \"string\"\n                ? new TextEncoder().encode(item.content)\n                : item.content;\n\n              const outMap = item.map !== undefined\n                ? typeof item.map === \"string\"\n                  ? new TextEncoder().encode(item.map)\n                  : item.map\n                : null;\n\n              if (req.filePath === item.path) {\n                if (req.content === outContent && req.map === outMap) {\n                  continue;\n                }\n\n                transformed = true;\n                req.content = outContent;\n                req.map = outMap;\n              } else {\n                let found = false;\n                for (let i = 0; i < queue.length; i++) {\n                  const req = queue[i];\n                  if (req.filePath === item.path) {\n                    found = true;\n                    transformed = true;\n                    req.content = outContent;\n                    req.map = outMap;\n                  }\n                }\n\n                if (!found) {\n                  queue.push({\n                    newFile: true,\n                    filePath: item.path,\n                    content: outContent,\n                    map: outMap,\n                    inputFiles: req.inputFiles.slice(),\n                  });\n                }\n              }\n            }\n          } else {\n            const outContent = typeof result.content === \"string\"\n              ? new TextEncoder().encode(result.content)\n              : result.content;\n\n            const outMap = result.map !== undefined\n              ? typeof result.map === \"string\"\n                ? new TextEncoder().encode(result.map)\n                : result.map\n              : null;\n\n            if (req.content === outContent && req.map === outMap) {\n              continue;\n            }\n\n            transformed = true;\n            req.content = outContent;\n            req.map = outMap;\n            req.filePath = result.path ?? req.filePath;\n          }\n        }\n      }\n\n      // TODO: Keep transforming until no one processes anymore\n      if (transformed || req.newFile) {\n        outFiles.push({\n          content: req.content,\n          map: req.map,\n          path: req.filePath,\n          inputFiles: req.inputFiles,\n        });\n      }\n    }\n\n    return outFiles.length > 0 ? outFiles : null;\n  }\n}\n\nconst CSS_URL_REGEX = /url\\((\"[^\"]+\"|'[^']+'|[^)]+)\\)/g;\n\nexport function cssAssetHash(transformer: FileTransformer) {\n  transformer.onTransform({\n    pluginName: \"fresh-css\",\n    filter: /\\.css$/,\n  }, (args) => {\n    const replaced = args.text.replaceAll(CSS_URL_REGEX, (_, str) => {\n      let rawUrl = str;\n      if (str[0] === \"'\" || str[0] === '\"') {\n        rawUrl = str.slice(1, -1);\n      }\n\n      if (rawUrl.length === 0) {\n        return str;\n      }\n\n      return `url(${JSON.stringify(assetInternal(rawUrl, BUILD_ID))})`;\n    });\n\n    return {\n      content: replaced,\n    };\n  });\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/file_transformer_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport type { FsAdapter } from \"../fs.ts\";\nimport { FileTransformer, type ProcessedFile } from \"./file_transformer.ts\";\nimport { delay } from \"../test_utils.ts\";\n\nfunction testTransformer(files: Record<string, string>, root = \"/\") {\n  const mockFs: FsAdapter = {\n    cwd: () => root,\n    isDirectory: () => Promise.resolve(false),\n    mkdirp: () => Promise.resolve(),\n    walk: async function* foo() {\n    },\n    readFile: (file) => {\n      if (file instanceof URL) throw new Error(\"Not supported\");\n      // deno-lint-ignore no-explicit-any\n      const content = (files as any)[file];\n      const buf = new TextEncoder().encode(content);\n      return Promise.resolve(buf);\n    },\n    readTextFile: (file) => {\n      if (file instanceof URL) throw new Error(\"Not supported\");\n      // deno-lint-ignore no-explicit-any\n      const content = (files as any)[file];\n      return Promise.resolve(content);\n    },\n  };\n  return new FileTransformer(mockFs, root);\n}\n\nfunction consumeResult(result: ProcessedFile[]) {\n  const out: {\n    path: string;\n    content: string;\n    map: string | null;\n    inputFiles: string[];\n  }[] = [];\n  for (let i = 0; i < result.length; i++) {\n    const file = result[i];\n\n    out.push({\n      path: file.path,\n      content: typeof file.content === \"string\"\n        ? file.content\n        : new TextDecoder().decode(file.content),\n      map: file.map !== null\n        ? typeof file.map === \"string\"\n          ? file.map\n          : new TextDecoder().decode(file.map)\n        : null,\n      inputFiles: file.inputFiles,\n    });\n  }\n\n  return out.sort((a, b) => a.path.localeCompare(b.path));\n}\n\nDeno.test(\"FileTransformer - transform sync\", async () => {\n  const transformer = testTransformer({\n    \"foo.txt\": \"foo\",\n  });\n\n  transformer.onTransform({ pluginName: \"foo\", filter: /.*/ }, (args) => {\n    return {\n      content: args.text + \"bar\",\n    };\n  });\n\n  const result = await transformer.process(\"foo.txt\", \"development\", \"\");\n  const files = await consumeResult(result!);\n  expect(files).toEqual([\n    { content: \"foobar\", map: null, path: \"foo.txt\", inputFiles: [\"foo.txt\"] },\n  ]);\n});\n\nDeno.test(\"FileTransformer - transform async\", async () => {\n  const transformer = testTransformer({\n    \"foo.txt\": \"foo\",\n  });\n\n  transformer.onTransform({ pluginName: \"foo\", filter: /.*/ }, async (args) => {\n    await delay(1);\n    return {\n      content: args.text + \"bar\",\n    };\n  });\n\n  const result = await transformer.process(\"foo.txt\", \"development\", \"\");\n  const files = await consumeResult(result!);\n  expect(files).toEqual([\n    { content: \"foobar\", map: null, path: \"foo.txt\", inputFiles: [\"foo.txt\"] },\n  ]);\n});\n\nDeno.test(\"FileTransformer - transform return Uint8Array\", async () => {\n  const transformer = testTransformer({\n    \"foo.txt\": \"foo\",\n  });\n\n  transformer.onTransform({ pluginName: \"foo\", filter: /.*/ }, () => {\n    return {\n      content: new TextEncoder().encode(\"foobar\"),\n    };\n  });\n\n  const result = await transformer.process(\"foo.txt\", \"development\", \"\");\n  const files = await consumeResult(result!);\n  expect(files).toEqual([\n    { content: \"foobar\", map: null, path: \"foo.txt\", inputFiles: [\"foo.txt\"] },\n  ]);\n});\n\nDeno.test(\"FileTransformer - pass transformed content\", async () => {\n  const transformer = testTransformer({\n    \"input.txt\": \"input\",\n  });\n\n  transformer.onTransform({ pluginName: \"A\", filter: /.*/ }, (args) => {\n    return {\n      content: args.text + \" -> A\",\n    };\n  });\n  transformer.onTransform({ pluginName: \"B\", filter: /.*/ }, (args) => {\n    return {\n      content: args.text + \" -> B\",\n    };\n  });\n\n  const result = await transformer.process(\"input.txt\", \"development\", \"\");\n  const files = await consumeResult(result!);\n  expect(files).toEqual([\n    {\n      content: \"input -> A -> B\",\n      map: null,\n      path: \"input.txt\",\n      inputFiles: [\"input.txt\"],\n    },\n  ]);\n});\n\nDeno.test(\n  \"FileTransformer - pass transformed content with multiple\",\n  async () => {\n    const transformer = testTransformer({\n      \"input.txt\": \"input\",\n    });\n\n    transformer.onTransform({ pluginName: \"A\", filter: /.*/ }, (args) => {\n      return [{\n        path: args.path,\n        content: args.text + \" -> A\",\n      }];\n    });\n    transformer.onTransform({ pluginName: \"B\", filter: /.*/ }, (args) => {\n      return {\n        content: args.text + \" -> B\",\n      };\n    });\n\n    const result = await transformer.process(\"input.txt\", \"development\", \"\");\n    const files = await consumeResult(result!);\n    expect(files).toEqual([\n      {\n        content: \"input -> A -> B\",\n        map: null,\n        path: \"input.txt\",\n        inputFiles: [\"input.txt\"],\n      },\n    ]);\n  },\n);\n\nDeno.test(\"FileTransformer - return multiple results\", async () => {\n  const transformer = testTransformer({\n    \"foo.txt\": \"foo\",\n  });\n\n  const received: string[] = [];\n  transformer.onTransform({ pluginName: \"A\", filter: /foo\\.txt$/ }, () => {\n    return [{\n      path: \"a.txt\",\n      content: \"A\",\n    }, {\n      path: \"b.txt\",\n      content: \"B\",\n    }];\n  });\n  transformer.onTransform({ pluginName: \"B\", filter: /.*/ }, (args) => {\n    received.push(args.path);\n  });\n\n  const result = await transformer.process(\"foo.txt\", \"development\", \"\");\n  const files = await consumeResult(result!);\n  expect(files).toEqual([\n    { content: \"A\", map: null, path: \"a.txt\", inputFiles: [\"foo.txt\"] },\n    { content: \"B\", map: null, path: \"b.txt\", inputFiles: [\"foo.txt\"] },\n  ]);\n  expect(received).toEqual([\"foo.txt\", \"b.txt\", \"a.txt\"]);\n});\n\nDeno.test(\n  \"FileTransformer - track input files through temporary results\",\n  async () => {\n    const transformer = testTransformer({\n      \"foo.txt\": \"foo\",\n    });\n\n    transformer.onTransform({ pluginName: \"A\", filter: /foo\\.txt$/ }, () => {\n      return [{\n        path: \"a.txt\",\n        content: \"A\",\n      }, {\n        path: \"b.txt\",\n        content: \"B\",\n      }];\n    });\n    transformer.onTransform(\n      { pluginName: \"B\", filter: /[ab]\\.txt$/ },\n      (args) => {\n        return {\n          path: \"c\" + args.path,\n          content: args.text + \"C\",\n        };\n      },\n    );\n\n    const result = await transformer.process(\"foo.txt\", \"development\", \"\");\n    const files = await consumeResult(result!);\n    expect(files).toEqual([\n      { content: \"AC\", map: null, path: \"ca.txt\", inputFiles: [\"foo.txt\"] },\n      { content: \"BC\", map: null, path: \"cb.txt\", inputFiles: [\"foo.txt\"] },\n    ]);\n  },\n);\n\nDeno.test(\"FileTransformer - pass root to args\", async () => {\n  const transformer = testTransformer({ \"foo.txt\": \"foo\" }, \"/<root>/\");\n\n  let root = \"\";\n  transformer.onTransform({ pluginName: \"A\", filter: /.*/ }, (args) => {\n    root = args.root;\n    return undefined;\n  });\n\n  await transformer.process(\"foo.txt\", \"development\", \"\");\n\n  expect(root).toEqual(\"/<root>/\");\n});\n"
  },
  {
    "path": "packages/fresh/src/dev/fs_crawl.ts",
    "content": "import { type FsAdapter, fsAdapter } from \"../fs.ts\";\nimport type { WalkEntry } from \"@std/fs/walk\";\nimport type { FsRouteFileNoMod } from \"./dev_build_cache.ts\";\nimport * as path from \"@std/path\";\nimport { pathToPattern } from \"../router.ts\";\nimport { CommandType } from \"../commands.ts\";\nimport { sortRoutePaths } from \"../fs_routes.ts\";\nimport type { RouteConfig } from \"../types.ts\";\n\nconst GROUP_REG = /[/\\\\\\\\]\\((_[^/\\\\\\\\]+)\\)[/\\\\\\\\]/;\n\nexport async function crawlRouteDir<State>(\n  fs: FsAdapter,\n  routeDir: string,\n  ignore: RegExp[],\n  onIslandSpecifier: (spec: string) => void,\n): Promise<FsRouteFileNoMod<State>[]> {\n  const files: FsRouteFileNoMod<State>[] = [];\n\n  await walkDir(fs, routeDir, async (entry) => {\n    // A `(_islands)` path segment is a local island folder.\n    // Any route path segment wrapped in `(_...)` is ignored\n    // during route collection.\n    const match = entry.path.match(GROUP_REG);\n    if (match !== null) {\n      if (match[1] === \"_islands\") {\n        onIslandSpecifier(entry.path);\n      }\n      return;\n    }\n\n    let lazy = false;\n    const relative = path.relative(routeDir, entry.path);\n    const url = new URL(relative, \"http://localhost/\");\n    const id = url.pathname.slice(0, url.pathname.lastIndexOf(\".\"));\n\n    let overrideConfig: RouteConfig | undefined;\n    let pattern = \"*\";\n    let routePattern = pattern;\n    let type = CommandType.Route;\n    if (id.endsWith(\"/_middleware\")) {\n      type = CommandType.Middleware;\n      pattern = pathToPattern(\n        id.slice(1, -\"/_middleware\".length),\n        { keepGroups: true },\n      );\n      routePattern = pattern;\n    } else if (id.endsWith(\"/_layout\")) {\n      type = CommandType.Layout;\n      pattern = pathToPattern(\n        id.slice(1, -\"/_layout\".length),\n        { keepGroups: true },\n      );\n      routePattern = pattern;\n    } else if (id.endsWith(\"/_app\")) {\n      type = CommandType.App;\n    } else if (id.endsWith(\"/_404\")) {\n      type = CommandType.NotFound;\n    } else if (id.endsWith(\"/_error\") || id.endsWith(\"/_500\")) {\n      type = CommandType.Error;\n      pattern = pathToPattern(\n        id.slice(1, -\"/_error\".length),\n        { keepGroups: true },\n      );\n      routePattern = pattern;\n    } else {\n      pattern = pathToPattern(id.slice(1), { keepGroups: true });\n      if (id.endsWith(\"/index\")) {\n        if (!pattern.endsWith(\"/\")) {\n          pattern += \"/\";\n        }\n      }\n\n      routePattern = pathToPattern(id.slice(1));\n\n      const code = await fs.readTextFile(entry.path);\n      lazy = !code.includes(\"routeOverride\");\n\n      // TODO: We could do an AST parse here to detect the\n      // kind of handler that's used to get a more accurate\n      // list of methods this route supports.\n      overrideConfig = {\n        methods: \"ALL\",\n      };\n    }\n\n    files.push({\n      id,\n      filePath: entry.path,\n      type,\n      pattern,\n      routePattern,\n      lazy,\n      css: [],\n      overrideConfig,\n    });\n  }, ignore);\n\n  files.sort((a, b) => sortRoutePaths(a.id, b.id));\n\n  return files;\n}\n\nexport async function walkDir(\n  fs: FsAdapter,\n  dir: string,\n  callback: (entry: WalkEntry) => void | Promise<void>,\n  ignore: RegExp[],\n) {\n  if (!await fs.isDirectory(dir)) return;\n\n  const entries = fs.walk(dir, {\n    includeDirs: false,\n    includeFiles: true,\n    exts: [\"tsx\", \"jsx\", \"ts\", \"js\"],\n    skip: ignore,\n  });\n\n  for await (const entry of entries) {\n    await callback(entry);\n  }\n}\n\nexport async function crawlFsItem(\n  options: { islandDir: string; routeDir: string; ignore: RegExp[] },\n): Promise<{ islands: string[]; routes: FsRouteFileNoMod<unknown>[] }> {\n  const islands: string[] = [];\n\n  const [, routes] = await Promise.all([\n    walkDir(\n      fsAdapter,\n      options.islandDir,\n      (entry) => {\n        islands.push(entry.path);\n      },\n      options.ignore,\n    ),\n    crawlRouteDir(\n      fsAdapter,\n      options.routeDir,\n      options.ignore,\n      (entry) => islands.push(entry),\n    ),\n  ]);\n\n  return { islands, routes };\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/fs_crawl_test.ts",
    "content": "import { expect } from \"@std/expect/expect\";\nimport { createFakeFs } from \"../test_utils.ts\";\nimport { walkDir } from \"./fs_crawl.ts\";\n\nDeno.test(\"walkDir - \", async () => {\n  const fs = createFakeFs({\n    \"foo/bar/baz.txt\": \"foo\",\n    \"foo/bar.txt\": \"foo\",\n  });\n\n  const files: string[] = [];\n\n  await walkDir(fs, \"foo\", async (entry) => {\n    // Purposely delay\n    await new Promise((r) => setTimeout(r, 100));\n    files.push(entry.path);\n  }, []);\n\n  expect(files).toEqual([\"foo/bar/baz.txt\", \"foo/bar.txt\"]);\n});\n\nDeno.test(\"walkDir - respects skip patterns\", async () => {\n  const fs = createFakeFs({\n    \"routes/index.tsx\": \"foo\",\n    \"routes/index_test.tsx\": \"test\",\n    \"routes/about.tsx\": \"foo\",\n    \"routes/about.test.ts\": \"test\",\n    \"routes/api/users.ts\": \"foo\",\n    \"routes/api/users_test.ts\": \"test\",\n  });\n\n  const files: string[] = [];\n  const TEST_FILE_PATTERN = /[._]test\\.(?:[tj]sx?|[mc][tj]s)$/;\n\n  await walkDir(fs, \"routes\", (entry) => {\n    files.push(entry.path);\n  }, [TEST_FILE_PATTERN]);\n\n  // Should only include non-test files\n  expect(files).toEqual([\n    \"routes/index.tsx\",\n    \"routes/about.tsx\",\n    \"routes/api/users.ts\",\n  ]);\n});\n"
  },
  {
    "path": "packages/fresh/src/dev/middlewares/automatic_workspace_folders.ts",
    "content": "import { eTag, ifNoneMatch } from \"@std/http/etag\";\nimport { generate } from \"@std/uuid/v5\";\nimport { NAMESPACE_URL } from \"@std/uuid/constants\";\nimport type { Middleware } from \"../../middlewares/mod.ts\";\n\n/**\n * Automatically detects workspace folders for DevTools in Chromium browsers.\n *\n * Without this middleware, if the feature is enabled and devTools is opened\n * there will be 404 errors in the console. This can be annoying for developers,\n * especially if there is logging for incoming requests.\n *\n * Enabling this feature can be helpful to make edits in DevTools change source\n * files directly, for a small amount of code.\n *\n * The UUID is generated with a UUIDv5, so that the same directory should\n * always generate the same UUID. This way, we can preserve the connected\n * workspace across reloads without writing anything to disk.\n *\n * @see https://chromium.googlesource.com/devtools/devtools-frontend/+/main/docs/ecosystem/automatic_workspace_folders.md\n */\nexport function automaticWorkspaceFolders<T>(root: string): Middleware<T> {\n  let uuid: string | undefined;\n  let etag: string | undefined;\n  let content: string | undefined;\n\n  return async (ctx) => {\n    const { pathname } = ctx.url;\n\n    if (pathname !== \"/.well-known/appspecific/com.chrome.devtools.json\") {\n      return ctx.next();\n    }\n\n    uuid ??= await generate(\n      NAMESPACE_URL,\n      new TextEncoder().encode(\n        `https://fresh.deno.dev?root=${encodeURIComponent(root)}&v=${0}`,\n      ),\n    );\n    content ??= JSON.stringify({ workspace: { root, uuid } }, undefined, 2);\n    etag ??= await eTag(content);\n\n    const noneMatchValue = ctx.req.headers.get(\"if-none-match\");\n    if (!ifNoneMatch(noneMatchValue, etag)) {\n      return new Response(undefined, { status: 304, headers: { etag } });\n    }\n    return new Response(content, { status: 200, headers: { etag } });\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/middlewares/error_overlay/code_frame.ts",
    "content": "import * as path from \"@std/path\";\n\nfunction tabs2Spaces(str: string) {\n  return str.replace(/^\\t+/, (tabs) => \"  \".repeat(tabs.length));\n}\n\n/**\n * Generate an excerpt of the location in the source around the\n * specified position.\n */\nexport function createCodeFrame(\n  text: string,\n  lineNum: number,\n  columnNum: number,\n): string | undefined {\n  // Default settings\n  const before = 2;\n  const after = 3;\n\n  const lines = text.split(\"\\n\");\n\n  // Check if specified range is valid\n  if (lines.length <= lineNum || lines[lineNum].length < columnNum) {\n    return;\n  }\n\n  const start = Math.max(0, lineNum - before);\n  const end = Math.min(lines.length, lineNum + after + 1);\n\n  // Maximum space needed for line numbering in the current range.\n  // Necessary when the amount of digits of the line numbering grows:\n  //  999 | asdf\n  // 1000 | asdjadfjsa\n  const maxLineNum = String(end).length;\n  const padding = \" \".repeat(maxLineNum);\n\n  // Normalize all indentation (=tabs) to use 2 spaces. We need to\n  // apply the difference to the marker position to move it back in\n  // place.\n  const spaceLines: string[] = [];\n  let maxLineLen = 0;\n  for (let i = start; i < end; i++) {\n    const line = tabs2Spaces(lines[i]);\n    spaceLines.push(line);\n\n    if (line.length > maxLineLen) maxLineLen = line.length;\n  }\n\n  const activeLine = spaceLines[lineNum - start];\n  // Move marker into correct place by taking the amount of\n  // normalized tabs into account\n  const count = Math.max(\n    0,\n    activeLine.length - lines[lineNum].length + columnNum,\n  );\n\n  const sep = \"|\";\n  let out = \"\";\n\n  for (let i = 0; i < spaceLines.length; i++) {\n    const line = spaceLines[i];\n    const currentLine = (padding + (i + start + 1)).slice(-maxLineNum);\n\n    // Line where the error occurred\n    if (i === lineNum - start) {\n      out += \">\" + ` ${currentLine} ${sep} ${line}\\n`;\n\n      const columnMarker = \"^\";\n      out += `  ${padding} ${sep} ${\" \".repeat(count)}${columnMarker}\\n`;\n    } else {\n      out += `  ${currentLine} ${sep} ${line}\\n`;\n    }\n  }\n\n  return out;\n}\n\nconst STACK_FRAME = /^\\s*at\\s+(?:(.*)\\s+)?\\((.*):(\\d+):(\\d+)\\)$/;\nexport interface StackFrame {\n  fnName: string;\n  file: string;\n  line: number;\n  column: number;\n}\nfunction getFirstUserFile(\n  stack: string,\n  rootDir: string,\n): StackFrame | undefined {\n  const lines = stack.split(\"\\n\");\n  for (let i = 0; i < lines.length; i++) {\n    const match = lines[i].match(STACK_FRAME);\n    if (match) {\n      const fnName = match[1] ?? \"\";\n      const file = match[2];\n      const line = +match[3];\n      const column = +match[4];\n\n      if (file.startsWith(\"file://\")) {\n        const filePath = path.fromFileUrl(file);\n        if (path.relative(rootDir, filePath).startsWith(\".\")) {\n          continue;\n        }\n\n        return {\n          fnName,\n          file,\n          line,\n          column,\n        };\n      }\n    }\n  }\n}\n\nexport function getCodeFrame(stack: string, rootDir: string) {\n  const file = getFirstUserFile(stack, rootDir);\n  if (file) {\n    try {\n      const filePath = path.fromFileUrl(file.file);\n      const text = Deno.readTextFileSync(filePath);\n      return createCodeFrame(\n        text,\n        file.line - 1,\n        file.column - 1,\n      );\n    } catch {\n      // Ignore\n    }\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/middlewares/error_overlay/middleware.tsx",
    "content": "import { DEV_ERROR_OVERLAY_URL } from \"../../../constants.ts\";\nimport { HttpError } from \"../../../error.ts\";\nimport type { Middleware } from \"../../../middlewares/mod.ts\";\nimport { FreshScripts } from \"../../../runtime/server/preact_hooks.ts\";\nimport { ErrorOverlay } from \"./overlay.tsx\";\n\nexport function devErrorOverlay<T>(): Middleware<T> {\n  return async (ctx) => {\n    const { config, url } = ctx;\n    if (url.pathname === config.basePath + DEV_ERROR_OVERLAY_URL) {\n      return ctx.render(<ErrorOverlay url={url} />);\n    }\n\n    try {\n      return await ctx.next();\n    } catch (err) {\n      if (ctx.req.headers.get(\"accept\")?.includes(\"text/html\")) {\n        let init: ResponseInit | undefined;\n        if (err instanceof HttpError) {\n          if (err.status < 500) throw err;\n          init = { status: err.status };\n        }\n\n        // At this point we're pretty sure to have a server error\n        // deno-lint-ignore no-console\n        console.error(err);\n\n        return ctx.render(<FreshScripts />, init);\n      }\n      throw err;\n    }\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/middlewares/error_overlay/middleware_test.tsx",
    "content": "import { expect } from \"@std/expect\";\nimport { App } from \"../../../app.ts\";\nimport { FakeServer } from \"../../../test_utils.ts\";\nimport { devErrorOverlay } from \"./middleware.tsx\";\nimport { HttpError } from \"../../../error.ts\";\n\nDeno.test(\"error overlay - show when error is thrown\", async () => {\n  const app = new App();\n  app.use(devErrorOverlay());\n  app.config.mode = \"development\";\n\n  app.get(\"/\", () => {\n    throw new Error(\"fail\");\n  });\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\", {\n    headers: {\n      accept: \"text/html\",\n    },\n  });\n  const content = await res.text();\n  expect(content).toContain(\"fresh-error-overlay\");\n});\n\nDeno.test(\"error overlay - should not be visible for HttpError <500\", async () => {\n  const app = new App();\n  app.use(devErrorOverlay());\n  app.config.mode = \"development\";\n\n  app\n    .get(\"/\", () => {\n      throw new HttpError(404);\n    })\n    .get(\"/500\", () => {\n      throw new HttpError(500);\n    });\n\n  const server = new FakeServer(app.handler());\n  let res = await server.get(\"/\", {\n    headers: {\n      accept: \"text/html\",\n    },\n  });\n  let content = await res.text();\n  expect(content).not.toContain(\"fresh-error-overlay\");\n  expect(res.status).toEqual(404);\n\n  res = await server.get(\"/500\", {\n    headers: {\n      accept: \"text/html\",\n    },\n  });\n  content = await res.text();\n  expect(content).toContain(\"fresh-error-overlay\");\n  expect(res.status).toEqual(500);\n});\n\nDeno.test(\n  \"error overlay - should not be visible for HttpError <500 #2\",\n  async () => {\n    const app = new App();\n    app.use(devErrorOverlay());\n    app.config.mode = \"development\";\n    app\n      .use(async (ctx) => {\n        try {\n          return await ctx.next();\n        } catch {\n          return ctx.render(<p>ok</p>);\n        }\n      })\n      .get(\"/\", () => {\n        throw new HttpError(404);\n      });\n\n    const server = new FakeServer(app.handler());\n    const res = await server.get(\"/\", {\n      headers: {\n        accept: \"text/html\",\n      },\n    });\n    const content = await res.text();\n    expect(content).not.toContain(\"fresh-error-overlay\");\n  },\n);\n\nDeno.test(\"error overlay - should not be visible in prod\", async () => {\n  const app = new App({ mode: \"production\" });\n  app.use(devErrorOverlay());\n\n  app.get(\"/\", () => {\n    throw new HttpError(500);\n  });\n\n  const server = new FakeServer(app.handler());\n  const res = await server.get(\"/\", {\n    headers: {\n      accept: \"text/html\",\n    },\n  });\n  const content = await res.text();\n  expect(content).not.toContain(\"fresh-error-overlay\");\n  expect(res.status).toEqual(500);\n});\n"
  },
  {
    "path": "packages/fresh/src/dev/middlewares/error_overlay/overlay.tsx",
    "content": "import type { ComponentChildren } from \"preact\";\n\n// Just to get some syntax highlighting\nconst css = (arr: TemplateStringsArray, ...exts: never[]) => {\n  if (exts.length) throw new Error(\"Not allowed\");\n  return arr[0];\n};\n\nexport const errorCss = css`\n  :root {\n    --bg: #fff;\n    --bg-code-frame: rgb(255, 0, 32, 0.1);\n    --bg-active-line: #fbcecc;\n    --text: #222;\n    --text2: #444;\n    --title: #e84644;\n    --code: #333;\n    font-family: sans-serif;\n    line-height: 1.4;\n    color: var(--text);\n    background: var(--bg);\n  }\n\n  * {\n    box-sizing: border-box;\n    padding: 0;\n    margin: 0;\n  }\n\n  @media (prefers-color-scheme: dark) {\n    :root {\n      --bg-code-frame: rgba(251, 93, 113, 0.2);\n      --bg-active-line: #4f1919;\n      --bg: #353535;\n      --text: #f7f7f7;\n      --text2: #ddd;\n      --code: #fdd1d1;\n    }\n  }\n\n  .inner {\n    max-width: 48rem;\n    padding: 4rem 1rem;\n    margin: 0 auto;\n  }\n\n  .title {\n    color: var(--title);\n    font-weight: normal;\n    font-size: 1.5rem;\n    margin-bottom: 1rem;\n  }\n\n  .code-frame {\n    overflow: auto;\n    padding: 0.5rem;\n    margin-bottom: 0.5rem;\n    background: var(--bg-code-frame);\n    color: var(--code);\n  }\n  .line {\n    padding: 0.25rem 0.5rem;\n  }\n  .active-line {\n    display: inline-block;\n    width: 100%;\n    background: var(--bg-active-line);\n  }\n\n  .stack {\n    overflow-x: auto;\n  }\n\n  .close-btn {\n    position: absolute;\n    top: 1rem;\n    right: 1rem;\n    color: var(--title);\n    display: block;\n    width: 3rem;\n    height: 3rem;\n    background: none;\n    border: none;\n    transform: translate3d(0, 0, 0);\n  }\n  .close-btn:active {\n    transform: translate3d(0, 2px, 0);\n  }\n  .close-btn:hover {\n    cursor: pointer;\n    filter: drop-shadow(0 0 0.75rem crimson);\n  }\n`;\n\nfunction CodeFrame(props: { codeFrame: string }) {\n  const lines: ComponentChildren[] = [];\n\n  props.codeFrame.trimEnd().split(\"\\n\").forEach(\n    (line, i, arr) => {\n      const vnode = (\n        <span\n          class={\"line\" + (line.startsWith(\">\") ? \" active-line\" : \"\")}\n        >\n          {line}\n        </span>\n      );\n\n      lines.push(vnode);\n      if (i < arr.length - 1) lines.push(\"\\n\");\n    },\n  );\n  return (\n    <pre class=\"code-frame\">\n      <code>{lines}</code>\n    </pre>\n  );\n}\n\nconst DEFAULT_MESSAGE = \"Internal Server Error\";\n\nexport function ErrorOverlay(props: { url: URL }) {\n  const url = props.url;\n  const title = url.searchParams.get(\"message\") || DEFAULT_MESSAGE;\n  const stack = url.searchParams.get(\"stack\");\n  const codeFrame = url.searchParams.get(\"code-frame\");\n\n  if (title === DEFAULT_MESSAGE && !stack && !codeFrame) {\n    return null;\n  }\n\n  return (\n    <>\n      <div class=\"frsh-error-page\">\n        <style\n          // deno-lint-ignore react-no-danger\n          dangerouslySetInnerHTML={{ __html: errorCss }}\n        />\n        <div class=\"inner\">\n          <div class=\"header\">\n            <button\n              type=\"button\"\n              class=\"close-btn\"\n              aria-label=\"close\"\n              id=\"close-btn\"\n            >\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                fill=\"none\"\n                viewBox=\"0 0 24 24\"\n                strokeWidth={1.5}\n                stroke=\"currentColor\"\n              >\n                <path\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                  d=\"M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n                />\n              </svg>\n            </button>\n          </div>\n          <div>\n            <h1 class=\"title\">{title}</h1>\n            {codeFrame ? <CodeFrame codeFrame={codeFrame} /> : null}\n            {stack ? <pre class=\"stack\">{stack}</pre> : null}\n          </div>\n        </div>\n      </div>\n      <script\n        // deno-lint-ignore react-no-danger\n        dangerouslySetInnerHTML={{\n          __html:\n            `document.querySelector(\"#close-btn\").addEventListener(\"click\", () => parent.postMessage(\"close-error-overlay\"));`,\n        }}\n      />\n    </>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/middlewares/live_reload.ts",
    "content": "import type { Middleware } from \"../../middlewares/mod.ts\";\nimport { ALIVE_URL } from \"../../constants.ts\";\n\n// Live reload: Send updates to browser\nexport function liveReload<T>(): Middleware<T> {\n  const revision = Date.now();\n\n  return (ctx) => {\n    const { config, req, url } = ctx;\n\n    const aliveUrl = config.basePath + ALIVE_URL;\n\n    if (url.pathname === aliveUrl) {\n      if (req.headers.get(\"upgrade\") !== \"websocket\") {\n        return new Response(null, { status: 501 });\n      }\n\n      // TODO: When a change is made the Deno server restarts,\n      // so for now the WebSocket connection is only used for\n      // the client to know when the server is back up. Once we\n      // have HMR we'll actively start sending messages back\n      // and forth.\n      const { response, socket } = Deno.upgradeWebSocket(req);\n\n      socket.addEventListener(\"open\", () => {\n        socket.send(\n          JSON.stringify({\n            type: \"initial-state\",\n            revision,\n          }),\n        );\n      });\n\n      return response;\n    }\n\n    return ctx.next();\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/mod.ts",
    "content": "export {\n  Builder,\n  type BuildOptions,\n  type ResolvedBuildConfig,\n} from \"./builder.ts\";\nexport {\n  type OnTransformArgs,\n  type OnTransformOptions,\n  type TransformFn,\n} from \"./file_transformer.ts\";\n"
  },
  {
    "path": "packages/fresh/src/dev/update_check.ts",
    "content": "import * as semver from \"@std/semver\";\nimport * as colors from \"@std/fmt/colors\";\nimport * as path from \"@std/path\";\nimport { CURRENT_FRESH_VERSION } from \"../otel.ts\";\n\nexport interface CheckFile {\n  last_checked: string;\n  last_shown?: string;\n  latest_version: string;\n  current_version: string;\n}\n\nfunction getHomeDir(): string | null {\n  switch (Deno.build.os) {\n    case \"linux\": {\n      const xdg = Deno.env.get(\"XDG_CACHE_HOME\");\n      if (xdg) return xdg;\n\n      const home = Deno.env.get(\"HOME\");\n      if (home) return `${home}/.cache`;\n      break;\n    }\n\n    case \"darwin\": {\n      const home = Deno.env.get(\"HOME\");\n      if (home) return `${home}/Library/Caches`;\n      break;\n    }\n\n    case \"windows\":\n      return Deno.env.get(\"LOCALAPPDATA\") ?? null;\n  }\n\n  return null;\n}\n\nfunction getFreshCacheDir(): string | null {\n  const home = getHomeDir();\n  if (home) return path.join(home, \"fresh\");\n  return null;\n}\n\nasync function fetchLatestVersion(): Promise<string> {\n  const res = await fetch(\"https://dl.deno.land/fresh/release-latest.txt\");\n  if (res.ok) {\n    return (await res.text()).trim().replace(/^v/, \"\");\n  }\n\n  throw new Error(`Could not fetch latest version.`);\n}\n\nfunction readCurrentVersion(): string {\n  return CURRENT_FRESH_VERSION;\n}\n\nexport async function updateCheck(\n  interval: number,\n  getCacheDir = getFreshCacheDir,\n  getLatestVersion = fetchLatestVersion,\n  getCurrentVersion = readCurrentVersion,\n) {\n  // Skip update checks on CI or Deno Deploy\n  if (\n    Deno.env.get(\"CI\") === \"true\" ||\n    Deno.env.get(\"FRESH_NO_UPDATE_CHECK\") === \"true\" ||\n    Deno.env.get(\"DENO_DEPLOYMENT_ID\")\n  ) {\n    return;\n  }\n\n  const home = getCacheDir();\n  if (!home) return;\n  const filePath = path.join(home, \"latest.json\");\n  try {\n    await Deno.mkdir(home, { recursive: true });\n  } catch (err) {\n    if (!(err instanceof Deno.errors.AlreadyExists)) {\n      throw err;\n    }\n  }\n\n  const version = await getCurrentVersion();\n\n  let checkFile: CheckFile = {\n    current_version: version,\n    latest_version: version,\n    last_checked: new Date(0).toISOString(),\n  };\n  try {\n    const text = await Deno.readTextFile(filePath);\n    checkFile = JSON.parse(text);\n  } catch (err) {\n    if (!(err instanceof Deno.errors.NotFound)) {\n      throw err;\n    }\n  }\n\n  // Update current version\n  checkFile.current_version = version;\n\n  // Only check in the specified interval\n  if (Date.now() >= new Date(checkFile.last_checked).getTime() + interval) {\n    try {\n      checkFile.latest_version = await getLatestVersion();\n      checkFile.last_checked = new Date().toISOString();\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      // Update check is optional and shouldn't abort the program.\n      // deno-lint-ignore no-console\n      console.error(\n        colors.red(`    Update check failed: `) + message,\n      );\n      return;\n    }\n  }\n\n  // Only show update message if current version is smaller than latest\n  const currentVersion = semver.parse(checkFile.current_version);\n  const latestVersion = semver.parse(checkFile.latest_version);\n  if (\n    (!checkFile.last_shown ||\n      Date.now() >= new Date(checkFile.last_shown).getTime() + interval) &&\n    semver.lessThan(currentVersion, latestVersion)\n  ) {\n    checkFile.last_shown = new Date().toISOString();\n\n    const current = colors.bold(colors.rgb8(checkFile.current_version, 208));\n    const latest = colors.bold(colors.rgb8(checkFile.latest_version, 121));\n    // deno-lint-ignore no-console\n    console.log(\n      `    Fresh ${latest} is available. You're on ${current}`,\n    );\n    // deno-lint-ignore no-console\n    console.log(\n      `    To upgrade, run: deno run -A -r jsr:@fresh/update`,\n    );\n    // deno-lint-ignore no-console\n    console.log();\n  }\n\n  // Migrate old format to current\n  if (!checkFile.last_shown) {\n    checkFile.last_shown = new Date().toISOString();\n  }\n\n  const raw = JSON.stringify(checkFile, null, 2);\n  await Deno.writeTextFile(filePath, raw);\n}\n"
  },
  {
    "path": "packages/fresh/src/dev/update_check_test.ts",
    "content": "import * as path from \"@std/path\";\nimport denoJson from \"../../deno.json\" with { type: \"json\" };\nimport { getStdOutput } from \"../../tests/test_utils.tsx\";\nimport { expect } from \"@std/expect\";\nimport { withTmpDir } from \"../test_utils.ts\";\nimport type { CheckFile } from \"./update_check.ts\";\nimport { WEEK } from \"../constants.ts\";\nimport { retry } from \"@std/async/retry\";\n\nconst CURRENT_VERSION = denoJson.version;\n\nconst cwd = import.meta.dirname!;\n\nDeno.test(\"stores update check file in $HOME/fresh\", async () => {\n  await using tmp = await withTmpDir();\n  const filePath = path.join(tmp.dir, \"latest.json\");\n\n  await new Deno.Command(Deno.execPath(), {\n    args: [\n      \"run\",\n      \"-A\",\n      path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n    ],\n    cwd,\n    env: {\n      CI: \"false\",\n      TEST_HOME: tmp.dir,\n    },\n  }).output();\n\n  const text = JSON.parse(await Deno.readTextFile(filePath));\n  expect(text).toEqual({\n    current_version: CURRENT_VERSION,\n    latest_version: \"99.99.999\",\n    last_checked: text.last_checked,\n    last_shown: text.last_shown,\n  });\n});\n\nDeno.test(\"skips update check on specific environment variables\", async (t) => {\n  const envs = [\"FRESH_NO_UPDATE_CHECK\", \"CI\", \"DENO_DEPLOYMENT_ID\"];\n\n  for (const env of envs) {\n    await t.step(`checking ${env}`, async () => {\n      await using tmp = await withTmpDir();\n      const out = await new Deno.Command(Deno.execPath(), {\n        args: [\n          \"run\",\n          \"-A\",\n          path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n        ],\n        cwd,\n        env: {\n          [env]: \"true\",\n          TEST_HOME: tmp.dir,\n          LATEST_VERSION: \"1.30.0\",\n        },\n        stderr: \"piped\",\n        stdout: \"piped\",\n      }).output();\n\n      const { stdout } = getStdOutput(out);\n      expect(stdout).not.toMatch(/Fresh 1\\.30\\.0 is available/);\n    });\n  }\n});\n\nDeno.test(\"shows update message on version mismatch\", async () => {\n  await using tmp = await withTmpDir();\n  const filePath = path.join(tmp.dir, \"latest.json\");\n\n  await Deno.writeTextFile(\n    filePath,\n    JSON.stringify({\n      current_version: \"1.1.0\",\n      latest_version: \"1.1.0\",\n      last_checked: new Date(0).toISOString(),\n    }),\n  );\n\n  const out = await new Deno.Command(Deno.execPath(), {\n    args: [\n      \"run\",\n      \"-A\",\n      path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n    ],\n    cwd,\n    env: {\n      CI: \"false\",\n      TEST_HOME: tmp.dir,\n      LATEST_VERSION: \"999.999.0\",\n    },\n    stderr: \"piped\",\n    stdout: \"piped\",\n  }).output();\n\n  const { stdout } = getStdOutput(out);\n  expect(stdout).toMatch(/Fresh 999\\.999\\.0 is available/);\n\n  // Updates check file\n  const text = JSON.parse(await Deno.readTextFile(filePath));\n  expect(text).toEqual({\n    current_version: CURRENT_VERSION,\n    latest_version: \"999.999.0\",\n    last_checked: text.last_checked,\n    last_shown: text.last_shown,\n  });\n});\n\nDeno.test(\"only fetch new version defined by interval\", async (t) => {\n  await using tmp = await withTmpDir();\n\n  await t.step(\"fetches latest version initially\", async () => {\n    const out = await new Deno.Command(Deno.execPath(), {\n      args: [\n        \"run\",\n        \"-A\",\n        path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n      ],\n      cwd,\n      env: {\n        CI: \"false\",\n        UPDATE_INTERVAL: \"100000\",\n        TEST_HOME: tmp.dir,\n        LATEST_VERSION: \"1.30.0\",\n      },\n      stderr: \"piped\",\n      stdout: \"piped\",\n    }).output();\n\n    const { stdout } = getStdOutput(out);\n    expect(stdout).toMatch(/fetching latest version/);\n  });\n\n  await t.step(\"should not fetch if interval has not passed\", async () => {\n    const out = await new Deno.Command(Deno.execPath(), {\n      args: [\n        \"run\",\n        \"-A\",\n        path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n      ],\n      cwd,\n      env: {\n        CI: \"false\",\n        UPDATE_INTERVAL: \"100000\",\n        TEST_HOME: tmp.dir,\n        LATEST_VERSION: \"1.30.0\",\n      },\n      stderr: \"piped\",\n      stdout: \"piped\",\n    }).output();\n\n    const { stdout } = getStdOutput(out);\n    expect(stdout).not.toMatch(/fetching latest version/);\n  });\n\n  await t.step(\"fetches if interval has passed\", async () => {\n    const out = await new Deno.Command(Deno.execPath(), {\n      args: [\n        \"run\",\n        \"-A\",\n        path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n      ],\n      cwd,\n      env: {\n        CI: \"false\",\n        UPDATE_INTERVAL: \"1 \",\n        TEST_HOME: tmp.dir,\n        LATEST_VERSION: \"1.30.0\",\n      },\n    }).output();\n\n    const { stdout } = getStdOutput(out);\n    expect(stdout).toMatch(/fetching latest version/);\n  });\n});\n\nDeno.test(\"updates current version in cache file\", async () => {\n  await using tmp = await withTmpDir();\n\n  const checkFile: CheckFile = {\n    current_version: \"1.2.0\",\n    latest_version: \"1.2.0\",\n    last_checked: new Date(Date.now() - WEEK).toISOString(),\n  };\n\n  await Deno.writeTextFile(\n    path.join(tmp.dir, \"latest.json\"),\n    JSON.stringify(checkFile, null, 2),\n  );\n\n  const out = await new Deno.Command(Deno.execPath(), {\n    args: [\n      \"run\",\n      \"-A\",\n      path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n    ],\n    cwd,\n    env: {\n      CI: \"false\",\n      TEST_HOME: tmp.dir,\n      LATEST_VERSION: CURRENT_VERSION,\n    },\n    stderr: \"piped\",\n    stdout: \"piped\",\n  }).output();\n\n  const { stdout } = getStdOutput(out);\n  expect(stdout).not.toMatch(/Fresh .* is available/);\n});\n\nDeno.test(\"only shows update message when current < latest\", async () => {\n  await using tmp = await withTmpDir();\n\n  const checkFile: CheckFile = {\n    current_version: \"9999.999.0\",\n    latest_version: \"1.2.0\",\n    last_checked: new Date().toISOString(),\n  };\n\n  await Deno.writeTextFile(\n    path.join(tmp.dir, \"latest.json\"),\n    JSON.stringify(checkFile, null, 2),\n  );\n\n  const out = await new Deno.Command(Deno.execPath(), {\n    args: [\n      \"run\",\n      \"-A\",\n      path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n    ],\n    cwd,\n    env: {\n      CI: \"false\",\n      TEST_HOME: tmp.dir,\n      LATEST_VERSION: CURRENT_VERSION,\n      CURRENT_VERSION: \"99999.9999.0\",\n    },\n    stderr: \"piped\",\n    stdout: \"piped\",\n  }).output();\n\n  const { stdout } = getStdOutput(out);\n  expect(stdout).not.toMatch(/Fresh .* is available/);\n});\n\nDeno.test(\"migrates to last_shown property\", async () => {\n  await using tmp = await withTmpDir();\n\n  const checkFile: CheckFile = {\n    latest_version: \"1.4.0\",\n    current_version: \"1.2.0\",\n    last_checked: new Date().toISOString(),\n  };\n\n  await Deno.writeTextFile(\n    path.join(tmp.dir, \"latest.json\"),\n    JSON.stringify(checkFile, null, 2),\n  );\n\n  const out = await new Deno.Command(Deno.execPath(), {\n    args: [\n      \"run\",\n      \"-A\",\n      path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n    ],\n    cwd,\n    env: {\n      CI: \"false\",\n      TEST_HOME: tmp.dir,\n      CURRENT_VERSION: \"1.2.0\",\n      LATEST_VERSION: \"99999.9999.0\",\n    },\n    stderr: \"piped\",\n    stdout: \"piped\",\n  }).output();\n\n  const { stdout, stderr } = getStdOutput(out);\n  if (stdout === \"\") {\n    // deno-lint-ignore no-console\n    console.log(stderr);\n  }\n  expect(stdout).toMatch(/Fresh .* is available/);\n\n  const checkFileAfter = JSON.parse(\n    await Deno.readTextFile(\n      path.join(tmp.dir, \"latest.json\"),\n    ),\n  );\n\n  // Check if last version was written\n  expect(typeof checkFileAfter.last_shown).toEqual(\"string\");\n});\n\nDeno.test(\"doesn't show update if last_shown + interval >= today\", async () => {\n  await using tmp = await withTmpDir();\n\n  const todayMinus1Hour = new Date();\n  todayMinus1Hour.setHours(todayMinus1Hour.getHours() - 1);\n\n  const checkFile: CheckFile = {\n    current_version: \"1.2.0\",\n    latest_version: \"1.6.0\",\n    last_checked: new Date().toISOString(),\n    last_shown: todayMinus1Hour.toISOString(),\n  };\n\n  await Deno.writeTextFile(\n    path.join(tmp.dir, \"latest.json\"),\n    JSON.stringify(checkFile, null, 2),\n  );\n\n  const out = await new Deno.Command(Deno.execPath(), {\n    args: [\n      \"run\",\n      \"-A\",\n      path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n    ],\n    cwd,\n    env: {\n      CI: \"false\",\n      TEST_HOME: tmp.dir,\n      CURRENT_VERSION: \"1.2.0\",\n      LATEST_VERSION: \"99999.9999.0\",\n    },\n    stderr: \"piped\",\n    stdout: \"piped\",\n  }).output();\n\n  const { stdout } = getStdOutput(out);\n  expect(stdout).not.toMatch(/Fresh .* is available/);\n});\n\nDeno.test(\n  \"shows update if last_shown + interval < today\",\n  async () => {\n    await retry(async () => {\n      await using tmp = await withTmpDir();\n\n      const yesterday = new Date();\n      yesterday.setDate(yesterday.getDate() - 1);\n\n      const checkFile: CheckFile = {\n        current_version: \"1.2.0\",\n        latest_version: \"99.999.99\",\n        last_checked: new Date().toISOString(),\n        last_shown: yesterday.toISOString(),\n      };\n\n      await Deno.writeTextFile(\n        path.join(tmp.dir, \"latest.json\"),\n        JSON.stringify(checkFile, null, 2),\n      );\n\n      const out = await new Deno.Command(Deno.execPath(), {\n        args: [\n          \"run\",\n          \"-A\",\n          path.join(cwd, \"../../tests/fixture_update_check/mod.ts\"),\n        ],\n        cwd,\n        env: {\n          CI: \"false\",\n          TEST_HOME: tmp.dir,\n          CURRENT_VERSION: CURRENT_VERSION,\n          LATEST_VERSION: \"99999.9999.0\",\n        },\n        stderr: \"piped\",\n        stdout: \"piped\",\n      }).output();\n\n      const { stdout } = getStdOutput(out);\n      expect(stdout).toMatch(/Fresh .* is available/);\n\n      const checkFileAfter = JSON.parse(\n        await Deno.readTextFile(\n          path.join(tmp.dir, \"latest.json\"),\n        ),\n      );\n\n      expect(checkFileAfter.last_shown).not.toEqual(yesterday.toISOString());\n    }, { maxAttempts: 5 });\n  },\n);\n"
  },
  {
    "path": "packages/fresh/src/error.ts",
    "content": "import { type ErrorStatus, STATUS_TEXT } from \"@std/http/status\";\n\nexport type { ErrorStatus };\n\n/**\n * Error that's thrown when a request fails. Correlates to a\n * {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status | HTTP status}.\n *\n * @property status The HTTP status code.\n *\n * @example Basic usage\n * ```ts\n * import { App, HttpError } from \"fresh\";\n * import { expect } from \"@std/expect\";\n *\n * const app = new App()\n *   .get(\"/\", () => new Response(\"ok\"))\n *   .get(\"/not-found\", () => {\n *      throw new HttpError(404, \"Nothing here\");\n *    });\n *\n * const handler = app.handler();\n *\n * try {\n *   await handler(new Request(\"http://localhost/not-found\"))\n * } catch (error) {\n *   expect(error).toBeInstanceOf(HttpError);\n *   expect(error.status).toBe(404);\n *   expect(error.message).toBe(\"Nothing here\");\n * }\n * ```\n */\nexport class HttpError extends Error {\n  /**\n   * The HTTP status code.\n   *\n   * @example Basic usage\n   * ```ts\n   * import { App, HttpError } from \"fresh\";\n   * import { expect } from \"@std/expect\";\n   *\n   * const app = new App()\n   *   .get(\"/\", () => new Response(\"ok\"))\n   *   .get(\"/not-found\", () => {\n   *      throw new HttpError(404, \"Nothing here\");\n   *    });\n   *\n   * const handler = app.handler();\n   *\n   * try {\n   *   await handler(new Request(\"http://localhost/not-found\"))\n   * } catch (error) {\n   *   expect(error).toBeInstanceOf(HttpError);\n   *   expect(error.status).toBe(404);\n   *   expect(error.message).toBe(\"Nothing here\");\n   * }\n   * ```\n   */\n  status: ErrorStatus;\n\n  /**\n   * Constructs a new instance.\n   *\n   * @param status The HTTP status code.\n   * @param message The error message. Defaults to the status text of the given\n   * status code.\n   * @param options Optional error options.\n   */\n  constructor(\n    status: ErrorStatus,\n    message: string = STATUS_TEXT[status],\n    options?: ErrorOptions,\n  ) {\n    super(message, options);\n    this.name = this.constructor.name;\n    this.status = status;\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/error_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport { HttpError } from \"./error.ts\";\n\nDeno.test(\"HttpError\", () => {\n  const err = new HttpError(404);\n  expect(err.status).toEqual(404);\n  expect(err.message).toEqual(\"Not Found\");\n  expect(typeof err.stack).toEqual(\"string\");\n\n  const err2 = new HttpError(500);\n  expect(err2.status).toEqual(500);\n  expect(err2.message).toEqual(\"Internal Server Error\");\n  expect(typeof err2.stack).toEqual(\"string\");\n});\n"
  },
  {
    "path": "packages/fresh/src/file_url.ts",
    "content": "import * as pathPosix from \"@std/path/posix\";\nimport * as path from \"@std/path\";\n\nexport type FileUrl = string & { readonly __brand: unique symbol };\n\nexport function pathToFileUrl(str: string): FileUrl {\n  if (str.startsWith(\"file://\")) {\n    // all good\n  } else if (path.isAbsolute(str)) {\n    str = path.toFileUrl(str).href;\n  } else {\n    throw new Error(`Cannot convert \"${str}\" to file:// url`);\n  }\n\n  return str as FileUrl;\n}\n\nexport function relativeUrl(from: FileUrl, to: FileUrl): string {\n  return pathPosix.relative(from, to);\n}\n"
  },
  {
    "path": "packages/fresh/src/file_url_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport { pathToFileUrl, relativeUrl } from \"./file_url.ts\";\n\nDeno.test(\"pathToFileUrl\", () => {\n  expect(pathToFileUrl(\"file:///foo/bar\")).toEqual(\"file:///foo/bar\");\n  expect(pathToFileUrl(\"/foo/bar\")).toEqual(\"file:///foo/bar\");\n});\n\nDeno.test(\"relativeUrl\", () => {\n  const a = pathToFileUrl(\"file://foo/bar\");\n  const b = pathToFileUrl(\"file://foo/baz\");\n\n  expect(relativeUrl(a, b)).toEqual(\"../baz\");\n});\n"
  },
  {
    "path": "packages/fresh/src/fs.ts",
    "content": "import { walk, type WalkEntry, type WalkOptions } from \"@std/fs/walk\";\n\nexport interface FsAdapter {\n  cwd(): string;\n  walk(\n    root: string | URL,\n    options?: WalkOptions,\n  ): AsyncIterableIterator<WalkEntry>;\n  isDirectory(path: string | URL): Promise<boolean>;\n  mkdirp(dir: string): Promise<void>;\n  readFile(path: string | URL): Promise<Uint8Array>;\n  readTextFile(path: string | URL): Promise<string>;\n}\n\nexport const fsAdapter: FsAdapter = {\n  walk,\n  cwd: Deno.cwd,\n  async isDirectory(path) {\n    try {\n      const stat = await Deno.stat(path);\n      return stat.isDirectory;\n    } catch (err) {\n      if (err instanceof Deno.errors.NotFound) return false;\n      throw err;\n    }\n  },\n  async mkdirp(dir: string) {\n    try {\n      await Deno.mkdir(dir, { recursive: true });\n    } catch (err) {\n      if (!(err instanceof Deno.errors.AlreadyExists)) {\n        throw err;\n      }\n    }\n  },\n  readFile: Deno.readFile,\n  readTextFile: Deno.readTextFile,\n};\n"
  },
  {
    "path": "packages/fresh/src/fs_routes.ts",
    "content": "import type { AnyComponent } from \"preact\";\nimport type { MaybeLazy, Route, RouteConfig } from \"./types.ts\";\nimport type { HandlerByMethod, RouteHandler } from \"./handlers.ts\";\nimport type { Middleware } from \"./middlewares/mod.ts\";\nimport type { AsyncAnyComponent, PageProps } from \"./render.ts\";\nimport { type HandlerFn, isHandlerByMethod } from \"./handlers.ts\";\nimport {\n  type Command,\n  CommandType,\n  newAppCmd,\n  newErrorCmd,\n  newLayoutCmd,\n  newMiddlewareCmd,\n  newNotFoundCmd,\n  newRouteCmd,\n} from \"./commands.ts\";\nimport { isLazy } from \"./utils.ts\";\nimport { recordSpanError, tracer } from \"./otel.ts\";\n\nexport interface FreshFsMod<State> {\n  config?: RouteConfig;\n  handler?: RouteHandler<unknown, State> | HandlerFn<unknown, State>[];\n  handlers?: RouteHandler<unknown, State>;\n  default?:\n    | AnyComponent<PageProps<unknown, State>>\n    | AsyncAnyComponent<PageProps<unknown, State>>;\n  css?: string[];\n}\n\nexport interface FsRouteFile<State> {\n  id: string;\n  filePath: string;\n  mod: MaybeLazy<FreshFsMod<State>>;\n  pattern: string;\n  type: CommandType;\n  routePattern: string;\n  overrideConfig: RouteConfig | undefined;\n  css: string[];\n}\n\nfunction isFreshFile<State>(\n  // deno-lint-ignore no-explicit-any\n  mod: any,\n  commandType: CommandType,\n): mod is FreshFsMod<State> {\n  if (mod === null || typeof mod !== \"object\") return false;\n\n  return typeof mod.default === \"function\" ||\n    commandType === CommandType.Middleware && Array.isArray(mod.default) ||\n    typeof mod.config === \"object\" || typeof mod.handlers === \"object\" ||\n    typeof mod.handlers === \"function\" || typeof mod.handler === \"object\" ||\n    typeof mod.handler === \"function\";\n}\n\nexport interface FsRoutesOptions {\n  /**\n   * Parent directory for the `/routes` and `/islands` folders.\n   *\n   * By default, the `root` config option of the provided app is used.\n   * @default app.config.root\n   */\n  dir?: string;\n  ignoreFilePattern?: RegExp[];\n  loadRoute: (path: string) => Promise<unknown>;\n}\n\nexport function fsItemsToCommands<State>(\n  items: FsRouteFile<State>[],\n): Command<State>[] {\n  const commands: Command<State>[] = [];\n\n  for (let i = 0; i < items.length; i++) {\n    const item = items[i];\n    const { filePath, type, mod: rawMod, pattern, routePattern } = item;\n\n    switch (type) {\n      case CommandType.Middleware: {\n        if (isLazy(rawMod)) continue;\n        const { handlers, mod } = validateFsMod(filePath, rawMod, type);\n\n        let middlewares = (handlers ?? mod.default) as unknown as\n          | Middleware<State>\n          | Middleware<State>[]\n          | HandlerByMethod<unknown, State> ??\n          null;\n        if (middlewares === null) continue;\n\n        if (isHandlerByMethod(middlewares)) {\n          warnInvalidRoute(\n            `Middleware does not support object handlers with GET, POST, etc. in ${filePath}`,\n          );\n          continue;\n        }\n\n        if (!Array.isArray(middlewares)) {\n          middlewares = [middlewares];\n        }\n\n        commands.push(newMiddlewareCmd(pattern, middlewares, true));\n        continue;\n      }\n      case CommandType.Layout: {\n        const { handlers, mod } = validateFsMod<State>(filePath, rawMod, type);\n        if (handlers !== null) {\n          warnInvalidRoute(\"Layout does not support handlers\");\n        }\n        if (!mod.default) continue;\n\n        commands.push(newLayoutCmd(pattern, mod.default, mod.config, true));\n        continue;\n      }\n      case CommandType.Error: {\n        const { handlers, mod } = validateFsMod<State>(filePath, rawMod, type);\n        commands.push(newErrorCmd(\n          pattern,\n          {\n            component: mod.default ?? undefined,\n            config: mod.config ?? undefined,\n            // deno-lint-ignore no-explicit-any\n            handler: (handlers as any) ?? undefined,\n          },\n          true,\n        ));\n        continue;\n      }\n      case CommandType.NotFound: {\n        const { handlers, mod } = validateFsMod<State>(filePath, rawMod, type);\n        commands.push(newNotFoundCmd({\n          config: mod.config,\n          component: mod.default,\n          // deno-lint-ignore no-explicit-any\n          handler: handlers as any ?? undefined,\n        }));\n        continue;\n      }\n      case CommandType.App: {\n        const { mod } = validateFsMod<State>(filePath, rawMod, type);\n        if (mod.default === undefined) continue;\n\n        commands.push(newAppCmd(mod.default));\n        continue;\n      }\n      case CommandType.Route: {\n        let normalized;\n        // Merge configs\n        let config: RouteConfig = {};\n        if (isLazy(rawMod)) {\n          normalized = async () => {\n            return await tracer.startActiveSpan(\"lazy-route\", {\n              attributes: { \"fresh.route_name\": rawMod.name ?? \"anonymous\" },\n            }, async (span) => {\n              try {\n                const result = await rawMod();\n                return normalizeRoute(filePath, result, routePattern, type);\n              } catch (err) {\n                recordSpanError(span, err);\n                throw err;\n              } finally {\n                span.end();\n              }\n            });\n          };\n\n          config.methods = item.overrideConfig?.methods ?? \"ALL\";\n          config.routeOverride = item.overrideConfig?.routeOverride ??\n            routePattern;\n        } else {\n          normalized = normalizeRoute(filePath, rawMod, routePattern, type);\n          if (rawMod.config) {\n            config = rawMod.config;\n          }\n        }\n\n        commands.push(\n          newRouteCmd(pattern, normalized, config, false),\n        );\n        continue;\n      }\n      case CommandType.Handler:\n        throw new Error(`Not supported`);\n      case CommandType.FsRoute:\n        throw new Error(`Nested FsRoutes are not supported`);\n      default:\n        throw new Error(`Unknown command type: ${type}`);\n    }\n  }\n\n  return commands;\n}\n\nfunction warnInvalidRoute(message: string) {\n  // deno-lint-ignore no-console\n  console.warn(\n    `🍋 %c[WARNING] Unsupported route config: ${message}`,\n    \"color:rgb(251, 184, 0)\",\n  );\n}\n\nconst APP_REG = /_app(?!\\.[tj]sx?)?$/;\n\n/**\n * Sort route paths where special Fresh files like `_app`,\n * `_layout` and `_middleware` are sorted in front.\n */\nexport function sortRoutePaths(a: string, b: string) {\n  // The `_app` route should always be the first\n  if (APP_REG.test(a)) return -1;\n  else if (APP_REG.test(b)) return 1;\n\n  const aLen = a.length;\n  const bLen = b.length;\n\n  let segment = false;\n  let aIdx = 0;\n  let bIdx = 0;\n  for (; aIdx < aLen && bIdx < bLen; aIdx++, bIdx++) {\n    const charA = a.charAt(aIdx);\n    const charB = b.charAt(bIdx);\n\n    // When comparing a grouped route with a non-grouped one, we\n    // need to skip over the group name to effectively compare the\n    // actual route.\n    if (charA === \"(\" && charB !== \"(\") {\n      if (charB == \"[\") return -1;\n      return 1;\n    } else if (charB === \"(\" && charA !== \"(\") {\n      if (charA == \"[\") return 1;\n      return -1;\n    }\n\n    if (charA === \"/\" || charB === \"/\") {\n      segment = true;\n\n      // If the other path doesn't close the segment\n      // then we don't need to continue\n      if (charA !== \"/\") return 1;\n      if (charB !== \"/\") return -1;\n\n      continue;\n    }\n\n    if (segment) {\n      segment = false;\n\n      const scoreA = getRoutePathScore(charA, a, aIdx);\n      const scoreB = getRoutePathScore(charB, b, bIdx);\n      if (scoreA === scoreB) {\n        if (charA !== charB) {\n          // TODO: Do we need localeSort here or is this good enough?\n          return charA < charB ? 0 : 1;\n        }\n        continue;\n      }\n\n      return scoreA > scoreB ? -1 : 1;\n    }\n\n    if (charA !== charB) {\n      // TODO: Do we need localeSort here or is this good enough?\n      return charA < charB ? 0 : 1;\n    }\n\n    // If we're at the end of A or B, then we assume that the longer\n    // path is more specific\n    if (aIdx === aLen - 1 && bIdx < bLen - 1) {\n      return 1;\n    } else if (bIdx === bLen - 1 && aIdx < aLen - 1) {\n      return -1;\n    }\n  }\n\n  return 0;\n}\n\n/**\n * Assign a score based on the first two characters of a path segment.\n * The goal is to sort `_middleware` and `_layout` in front of everything\n * and `[` or `[...` last respectively.\n */\nfunction getRoutePathScore(char: string, s: string, i: number): number {\n  if (char === \"_\") {\n    if (i + 1 < s.length) {\n      if (s[i + 1] === \"e\") return 4;\n      if (s[i + 1] === \"m\") return 6;\n    }\n    return 5;\n  } else if (char === \"[\") {\n    if (i + 1 < s.length && s[i + 1] === \".\") {\n      return 0;\n    }\n    return 1;\n  }\n\n  if (\n    i + 4 === s.length - 1 && char === \"i\" && s[i + 1] === \"n\" &&\n    s[i + 2] === \"d\" && s[i + 3] === \"e\" && s[i + 4] === \"x\"\n  ) {\n    return 3;\n  }\n\n  return 2;\n}\n\nexport function validateFsMod<State>(\n  filePath: string,\n  mod: unknown,\n  commandType: CommandType,\n): {\n  handlers: RouteHandler<unknown, State> | HandlerFn<unknown, State>[] | null;\n  mod: FreshFsMod<State>;\n} {\n  if (!isFreshFile<State>(mod, commandType)) {\n    throw new Error(\n      `Expected a route, middleware, layout or error template, but couldn't find relevant exports in: ${filePath}`,\n    );\n  }\n\n  const handlers = mod.handlers ?? mod.handler ?? null;\n  if (typeof handlers === \"function\" && handlers.length > 1) {\n    throw new Error(\n      `Handlers must only have one argument but found more than one. Check the function signature in: ${filePath}`,\n    );\n  }\n\n  return { handlers, mod };\n}\n\nfunction normalizeRoute<State>(\n  filePath: string,\n  rawMod: FreshFsMod<State>,\n  routePattern: string,\n  commandType: CommandType,\n): Route<State> {\n  const { handlers, mod } = validateFsMod<State>(filePath, rawMod, commandType);\n\n  return {\n    config: {\n      ...mod.config,\n      routeOverride: mod.config?.routeOverride ?? routePattern,\n    },\n    // deno-lint-ignore no-explicit-any\n    handler: (handlers as any) ?? undefined,\n    component: mod.default,\n    css: rawMod.css,\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/fs_routes_test.tsx",
    "content": "import { App, setBuildCache } from \"./app.ts\";\nimport { type FreshFsMod, sortRoutePaths } from \"./fs_routes.ts\";\nimport { delay, FakeServer, MockBuildCache } from \"./test_utils.ts\";\nimport { createFakeFs } from \"./test_utils.ts\";\nimport { expect, fn } from \"@std/expect\";\nimport { stub } from \"@std/testing/mock\";\nimport { type HandlerByMethod, type HandlerFn, page } from \"./handlers.ts\";\nimport type { Method } from \"./router.ts\";\nimport { parseHtml } from \"../tests/test_utils.tsx\";\nimport type { Context } from \"./context.ts\";\nimport { HttpError } from \"./error.ts\";\nimport { crawlRouteDir } from \"./dev/fs_crawl.ts\";\nimport * as path from \"@std/path\";\n\nasync function createServer<T>(\n  files: Record<string, string | Uint8Array | FreshFsMod<T>>,\n  mountPath?: string,\n): Promise<FakeServer> {\n  const fs = createFakeFs(files);\n\n  const routeDir = path.join(fs.cwd(), \"routes\");\n  const rawFiles = await crawlRouteDir(fs, routeDir, [], () => {});\n\n  const fsFiles = rawFiles.map((file) => {\n    // deno-lint-ignore no-explicit-any\n    return { ...file, mod: files[file.filePath] as any };\n  });\n\n  const app = new App<T>()\n    .fsRoutes(mountPath);\n\n  const buildCache = new MockBuildCache<T>(fsFiles, \"development\");\n  setBuildCache<T>(app, buildCache, \"development\");\n\n  return new FakeServer(app.handler());\n}\n\nDeno.test(\"fsRoutes - throws error when file has no exports\", async () => {\n  const p = createServer({ \"routes/index.tsx\": {} });\n  await expect(p).rejects.toMatch(/relevant exports/);\n});\n\nDeno.test(\"fsRoutes - registers HTTP methods on router\", async () => {\n  const methodHandler: HandlerByMethod<unknown, unknown> = {\n    GET: () => new Response(\"GET\"),\n    POST: () => new Response(\"POST\"),\n    PATCH: () => new Response(\"PATCH\"),\n    PUT: () => new Response(\"PUT\"),\n    DELETE: () => new Response(\"DELETE\"),\n    HEAD: () => new Response(\"HEAD\"),\n  };\n  const server = await createServer({\n    \"routes/all.ts\": { handlers: methodHandler },\n    \"routes/get.ts\": { handlers: { GET: methodHandler.GET } },\n    \"routes/post.ts\": { handlers: { POST: methodHandler.POST } },\n    \"routes/patch.ts\": { handlers: { PATCH: methodHandler.PATCH } },\n    \"routes/put.ts\": { handlers: { PUT: methodHandler.PUT } },\n    \"routes/delete.ts\": { handlers: { DELETE: methodHandler.DELETE } },\n    \"routes/head.ts\": { handlers: { HEAD: methodHandler.HEAD } },\n  });\n\n  const methods: Method[] = [\"GET\", \"POST\", \"PATCH\", \"PUT\", \"DELETE\", \"HEAD\"];\n  for (const method of methods) {\n    const name = method.toLowerCase() as Lowercase<Method>;\n    const res = await server[name](\"/all\");\n    expect(res.status).toEqual(200);\n\n    if (method === \"HEAD\") {\n      expect(res.body).toEqual(null);\n    } else {\n      expect(await res.text()).toEqual(method);\n    }\n  }\n\n  // Check individual routes\n  for (const method of methods) {\n    const lower = method.toLowerCase() as Lowercase<Method>;\n    const res = await server[lower](`/${lower}`);\n\n    expect(res.status).toEqual(200);\n    if (method === \"HEAD\") {\n      expect(res.body).toEqual(null);\n    } else {\n      expect(await res.text()).toEqual(method);\n    }\n\n    // Check that all other methods are forbidden\n    for (const other of methods) {\n      if (other === method) continue;\n\n      const name = other.toLowerCase() as Lowercase<Method>;\n      const res = await server[name](`/${lower}`);\n\n      await res.body?.cancel();\n      if (method === \"GET\" && other === \"HEAD\") {\n        // GET route + HEAD request should return 200\n        expect(res.status).toEqual(200);\n      } else {\n        expect(res.status).toEqual(405);\n      }\n    }\n  }\n});\n\nDeno.test(\"fsRoutes - registers fn handler for every method\", async () => {\n  const handler: HandlerFn<unknown, unknown> = () => new Response(\"ok\");\n  const server = await createServer({\n    \"routes/all.ts\": { handlers: handler },\n  });\n\n  const methods: Method[] = [\"GET\", \"POST\", \"PATCH\", \"PUT\", \"DELETE\", \"HEAD\"];\n  for (const method of methods) {\n    const name = method.toLowerCase() as Lowercase<Method>;\n    const res = await server[name](\"/all\");\n    expect(res.status).toEqual(200);\n\n    if (method === \"HEAD\") {\n      expect(res.body).toEqual(null);\n    } else {\n      expect(await res.text()).toEqual(\"ok\");\n    }\n  }\n\n  // Check individual routes\n  for (const method of methods) {\n    const lower = method.toLowerCase() as Lowercase<Method>;\n    const res = await server[lower](\"/all\");\n    expect(res.status).toEqual(200);\n\n    if (method === \"HEAD\") {\n      expect(res.body).toEqual(null);\n    } else {\n      expect(await res.text()).toEqual(\"ok\");\n    }\n  }\n});\n\nDeno.test(\"fsRoutes - renders component without handler\", async () => {\n  const server = await createServer({\n    \"routes/all.ts\": { default: () => <h1>foo</h1> },\n  });\n\n  const res = await server.get(\"/all\");\n  expect(res.status).toEqual(200);\n  expect(res.headers.get(\"Content-Type\")).toEqual(\"text/html; charset=utf-8\");\n\n  const doc = parseHtml(await res.text());\n  // deno-lint-ignore no-explicit-any\n  expect((doc.body.firstChild as any).outerHTML).toEqual(\n    \"<h1>foo</h1>\",\n  );\n});\n\nDeno.test(\"fsRoutes - sorts routes\", async () => {\n  const server = await createServer({\n    \"routes/[id].ts\": { handler: () => new Response(\"fail\") },\n    \"routes/all.ts\": { handler: () => new Response(\"ok\") },\n  });\n\n  const res = await server.get(\"/all\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - serve index\", async () => {\n  const server = await createServer({\n    \"routes/[id].ts\": { handler: () => new Response(\"fail\") },\n    \"routes/index.ts\": { handler: () => new Response(\"ok\") },\n  });\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - add middleware for function handler\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/[id].ts\": { handler: (ctx) => new Response(ctx.state.text) },\n    \"routes/index.ts\": { handler: (ctx) => new Response(ctx.state.text) },\n    \"routes/none.ts\": { default: (ctx) => <div>{ctx.state.text}</div> },\n    \"routes/_middleware.ts\": {\n      handler(ctx) {\n        ctx.state.text = \"ok\";\n        return ctx.next();\n      },\n    },\n  });\n\n  let res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.get(\"/foo\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.get(\"/none\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - middleware\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/index.ts\": { handler: (ctx) => new Response(ctx.state.text) },\n    \"routes/_middleware.ts\": {\n      default: ((ctx: Context<{ text: string }>) => {\n        ctx.state.text = \"ok\";\n        return ctx.next();\n        // deno-lint-ignore no-explicit-any\n      }) as any,\n    },\n  });\n\n  const res = await server.get(\"/\");\n  expect(res.status).toEqual(200);\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - nested middlewares\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/_middleware.ts\": {\n      handler: function A(ctx) {\n        ctx.state.text = \"A\";\n        return ctx.next();\n      },\n    },\n    \"routes/foo/_middleware.ts\": {\n      handler: function B(ctx) {\n        ctx.state.text += \"B\";\n        return ctx.next();\n      },\n    },\n    \"routes/foo/index.ts\": { default: (ctx) => <div>{ctx.state.text}</div> },\n  });\n\n  const res = await server.get(\"/foo\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"AB\");\n});\n\nDeno.test(\"fsRoutes - middleware array\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/_middleware.ts\": {\n      handler: [\n        (ctx) => {\n          ctx.state.text = \"A\";\n          return ctx.next();\n        },\n        (ctx) => {\n          ctx.state.text += \"B\";\n          return ctx.next();\n        },\n      ],\n    },\n    \"routes/foo/_middleware.ts\": {\n      handler: (ctx) => {\n        ctx.state.text += \"C\";\n        return ctx.next();\n      },\n    },\n    \"routes/foo/index.ts\": {\n      default: (ctx) => <div class=\"full-\">{ctx.state.text}</div>,\n    },\n  });\n\n  const res = await server.get(\"/foo\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ABC\");\n});\n\nDeno.test(\"fsRoutes - combined\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/foo/bar.ts\": {\n      default: (ctx) => <div>{ctx.state.text}</div>,\n    },\n    \"routes/foo/_middleware.ts\": {\n      handler: (ctx) => {\n        ctx.state.text = \"ok\";\n        return ctx.next();\n      },\n    },\n    \"routes/_middleware.ts\": {\n      handler: (ctx) => {\n        ctx.state.text = \"ok\";\n        return ctx.next();\n      },\n    },\n  });\n\n  const res = await server.get(\"/foo/bar\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - prepend _app\", async () => {\n  const server = await createServer({\n    \"routes/foo/bar.ts\": {\n      default: () => <>foo_bar</>,\n    },\n    \"routes/foo.ts\": {\n      default: () => <>foo</>,\n    },\n    \"routes/_app.tsx\": {\n      default: (ctx) => (\n        <div>\n          app/<ctx.Component />\n        </div>\n      ),\n    },\n  });\n\n  let res = await server.get(\"/foo/bar\");\n  let doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"app/foo_bar\");\n\n  res = await server.get(\"/foo\");\n  doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"app/foo\");\n});\n\nDeno.test(\"fsRoutes - prepend _layout\", async () => {\n  const server = await createServer({\n    \"routes/foo/bar.ts\": {\n      default: () => <>foo_bar</>,\n    },\n    \"routes/foo.ts\": {\n      default: () => <>foo</>,\n    },\n    \"routes/_layout.tsx\": {\n      default: (ctx) => (\n        <>\n          layout/<ctx.Component />\n        </>\n      ),\n    },\n    \"routes/_app.tsx\": {\n      default: (ctx) => (\n        <div>\n          app/<ctx.Component />\n        </div>\n      ),\n    },\n  });\n\n  let res = await server.get(\"/foo/bar\");\n  let doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"app/layout/foo_bar\");\n\n  res = await server.get(\"/foo\");\n  doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"app/layout/foo\");\n});\n\nDeno.test(\"fsRoutes - nested _layout\", async () => {\n  const server = await createServer({\n    \"routes/foo/bar.ts\": {\n      default: () => <>foo_bar</>,\n    },\n    \"routes/foo.ts\": {\n      default: () => <>foo</>,\n    },\n    \"routes/foo/_layout.tsx\": {\n      default: function fooBar(ctx) {\n        return (\n          <>\n            layout_foo_bar/<ctx.Component />\n          </>\n        );\n      },\n    },\n    \"routes/_layout.tsx\": {\n      default: function rootLayout(ctx) {\n        return (\n          <>\n            layout/<ctx.Component />\n          </>\n        );\n      },\n    },\n    \"routes/_app.tsx\": {\n      default: (ctx) => (\n        <div>\n          app/<ctx.Component />\n        </div>\n      ),\n    },\n  });\n\n  let res = await server.get(\"/foo/bar\");\n  let doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\n    \"app/layout/layout_foo_bar/foo_bar\",\n  );\n\n  res = await server.get(\"/foo\");\n  doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"app/layout/foo\");\n});\n\nDeno.test(\"fsRoutes - _layout skip if not present\", async () => {\n  const server = await createServer({\n    \"routes/foo/bar/baz.ts\": {\n      default: () => <>foo_bar_baz</>,\n    },\n    \"routes/foo/_layout.tsx\": {\n      default: (ctx) => (\n        <div>\n          layout_foo/<ctx.Component />\n        </div>\n      ),\n    },\n  });\n\n  const res = await server.get(\"/foo/bar/baz\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"layout_foo/foo_bar_baz\");\n});\n\nDeno.test(\"fsRoutes - _layout file types\", async () => {\n  const server = await createServer({\n    \"routes/js/index.js\": {\n      default: () => <>js</>,\n    },\n    \"routes/js/_layout.js\": {\n      default: (ctx) => (\n        <div>\n          layout_js/<ctx.Component />\n        </div>\n      ),\n    },\n    \"routes/jsx/index.jsx\": {\n      default: () => <>jsx</>,\n    },\n    \"routes/jsx/_layout.jsx\": {\n      default: (ctx) => (\n        <div>\n          layout_jsx/<ctx.Component />\n        </div>\n      ),\n    },\n    \"routes/ts/index.ts\": {\n      default: () => <>ts</>,\n    },\n    \"routes/ts/_layout.tsx\": {\n      default: (ctx) => (\n        <div>\n          layout_ts/<ctx.Component />\n        </div>\n      ),\n    },\n    \"routes/tsx/index.tsx\": {\n      default: () => <>tsx</>,\n    },\n    \"routes/tsx/_layout.tsx\": {\n      default: (ctx) => (\n        <div>\n          layout_tsx/<ctx.Component />\n        </div>\n      ),\n    },\n  });\n\n  const res = await server.get(\"/js\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"layout_js/js\");\n});\n\nDeno.test(\"fsRoutes - _layout disable _app\", async () => {\n  const server = await createServer({\n    \"routes/index.tsx\": {\n      default: () => <>route</>,\n    },\n    \"routes/_layout.tsx\": {\n      config: {\n        skipAppWrapper: true,\n      },\n      default: (ctx) => (\n        <>\n          layout/<ctx.Component />\n        </>\n      ),\n    },\n    \"routes/_app.tsx\": {\n      default: (ctx) => (\n        <div>\n          app/<ctx.Component />\n        </div>\n      ),\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"layout/route\");\n});\n\nDeno.test(\n  \"fsRoutes - _layout disable _app + inherited _layouts\",\n  async () => {\n    const server = await createServer({\n      \"routes/sub/sub2/index.tsx\": {\n        default: () => <>sub_sub2</>,\n      },\n      \"routes/sub/sub2/_layout.tsx\": {\n        default: (ctx) => (\n          <>\n            layout_sub_sub2/<ctx.Component />\n          </>\n        ),\n      },\n      \"routes/sub/_layout.tsx\": {\n        config: {\n          skipAppWrapper: true,\n          skipInheritedLayouts: true,\n        },\n        default: (ctx) => (\n          <>\n            layout_sub/<ctx.Component />\n          </>\n        ),\n      },\n      \"routes/_layout.tsx\": {\n        default: (ctx) => (\n          <>\n            layout/<ctx.Component />\n          </>\n        ),\n      },\n      \"routes/_app.tsx\": {\n        default: (ctx) => (\n          <div>\n            app/<ctx.Component />\n          </div>\n        ),\n      },\n    });\n\n    const res = await server.get(\"/sub/sub2\");\n    const doc = parseHtml(await res.text());\n    expect(doc.body.firstChild?.textContent).toEqual(\n      \"layout_sub/layout_sub_sub2/sub_sub2\",\n    );\n  },\n);\n\nDeno.test(\"fsRoutes - route overrides _layout\", async () => {\n  const server = await createServer({\n    \"routes/index.tsx\": {\n      config: {\n        skipInheritedLayouts: true,\n      },\n      default: () => <>route</>,\n    },\n    \"routes/_layout.tsx\": {\n      default: (ctx) => (\n        <div>\n          layout/<ctx.Component />\n        </div>\n      ),\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"route\");\n});\n\nDeno.test(\"fsRoutes - route overrides _app\", async () => {\n  const server = await createServer({\n    \"routes/index.tsx\": {\n      config: {\n        skipAppWrapper: true,\n      },\n      default: () => <>route</>,\n    },\n    \"routes/_app.tsx\": {\n      default: (ctx) => (\n        <div>\n          app/<ctx.Component />\n        </div>\n      ),\n    },\n    // Add some more routes on same level\n    \"routes/a.tsx\": { default: () => <>a</> },\n    \"routes/b.tsx\": { default: () => <>b</> },\n    \"routes/c.tsx\": { default: () => <>c</> },\n  });\n\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"route\");\n});\n\nDeno.test(\"fsRoutes - route overrides _app does not affect other routes\", async () => {\n  const server = await createServer({\n    \"routes/_500.tsx\": {\n      config: { skipInheritedLayouts: true },\n      default: () => <>error!</>,\n    },\n    \"routes/_app.tsx\": {\n      default: (ctx) => (\n        <div>\n          app/<ctx.Component />\n        </div>\n      ),\n    },\n    \"routes/_layout.tsx\": {\n      default: (ctx) => (\n        <>\n          layout/<ctx.Component />\n        </>\n      ),\n    },\n    // By having \"bar\" come before \"foo\", this results in a bug\n    // where the app wrapper is missing from \"foo\".\n    \"routes/bar.tsx\": {\n      config: {\n        skipAppWrapper: true,\n        skipInheritedLayouts: true,\n      },\n      default: () => <>bar</>,\n    },\n    \"routes/index.tsx\": { default: () => <>index</> },\n    \"routes/foo.tsx\": { default: () => <>foo</> },\n  });\n\n  const [indexRes, fooRes, barRes] = await Promise.all([\n    server.get(\"/\"),\n    server.get(\"/foo\"),\n    server.get(\"/bar\"),\n  ]);\n  const [index, foo, bar] = await Promise.all([\n    indexRes.text(),\n    fooRes.text(),\n    barRes.text(),\n  ]);\n  expect(parseHtml(index).body.firstChild?.textContent).toEqual(\n    \"app/layout/index\",\n  );\n  expect(parseHtml(foo).body.firstChild?.textContent).toEqual(\"app/layout/foo\");\n  expect(parseHtml(bar).body.firstChild?.textContent).toEqual(\"bar\");\n});\n\nDeno.test(\"fsRoutes - handler return data\", async () => {\n  const server = await createServer({\n    \"routes/index.tsx\": {\n      handler: () => {\n        return page(\"foo\", { status: 404 });\n      },\n      default: (ctx) => {\n        // deno-lint-ignore no-explicit-any\n        return <p>{ctx.data as any}</p>;\n      },\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"foo\");\n});\n\nDeno.test(\"fsRoutes - _404\", async () => {\n  const server = await createServer({\n    \"routes/_404.tsx\": {\n      default: () => {\n        return <div>Custom 404 - Not Found</div>;\n      },\n    },\n    \"routes/index.tsx\": {\n      handlers: () => {\n        throw new Error(\"ok\");\n      },\n    },\n  });\n\n  const res = await server.get(\"/invalid\");\n  const content = await res.text();\n  expect(content).toContain(\"Custom 404 - Not Found\");\n});\n\nDeno.test(\"fsRoutes - _404 method handler\", async () => {\n  const server = await createServer({\n    \"routes/_404.tsx\": {\n      handlers: {\n        GET() {\n          return new Response(\"ok\");\n        },\n      },\n    },\n    \"routes/index.tsx\": {\n      handlers: {\n        GET() {\n          return new Response(\"fail\");\n        },\n      },\n    },\n  });\n\n  const res = await server.get(\"/invalid\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - _500\", async () => {\n  const server = await createServer({\n    \"routes/_500.tsx\": {\n      default: () => {\n        return <div>Custom Error Page</div>;\n      },\n    },\n    \"routes/index.tsx\": {\n      handlers: () => {\n        throw new Error(\"ok\");\n      },\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const content = await res.text();\n  expect(content).toContain(\"Custom Error Page\");\n});\n\nDeno.test(\"fsRoutes - _500 method handler\", async () => {\n  const server = await createServer({\n    \"routes/_500.tsx\": {\n      handlers: {\n        GET() {\n          return new Response(\"ok\");\n        },\n      },\n    },\n    \"routes/index.tsx\": {\n      handlers: () => {\n        throw new Error(\"fail\");\n      },\n    },\n  });\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - _error\", async () => {\n  const server = await createServer({\n    \"routes/_error.tsx\": {\n      default: () => {\n        return <div>Custom Error Page</div>;\n      },\n    },\n    \"routes/index.tsx\": {\n      handlers: () => {\n        throw new Error(\"ok\");\n      },\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const content = await res.text();\n  expect(content).toContain(\"Custom Error Page\");\n});\n\nDeno.test(\"fsRoutes - _error nested\", async () => {\n  const server = await createServer({\n    \"routes/_error.tsx\": {\n      handlers: () => {\n        throw new Error(\"fail\");\n      },\n    },\n    \"routes/foo/_error.tsx\": {\n      handlers: (ctx) => {\n        return new Response((ctx.error as Error).message);\n      },\n    },\n    \"routes/foo/index.tsx\": {\n      handlers: () => {\n        throw new Error(\"ok\");\n      },\n    },\n  });\n\n  const res = await server.get(\"/foo\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - _error nested throw\", async () => {\n  const server = await createServer({\n    \"routes/_error.tsx\": {\n      handlers: (ctx) => {\n        return new Response((ctx.error as Error).message);\n      },\n    },\n    \"routes/foo/_error.tsx\": {\n      handlers: () => {\n        throw new Error(\"ok\");\n      },\n    },\n    \"routes/foo/index.tsx\": {\n      handlers: () => {\n        throw new Error(\"ok\");\n      },\n    },\n  });\n\n  const res = await server.get(\"/foo\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - _error method handler\", async () => {\n  const server = await createServer({\n    \"routes/_error.tsx\": {\n      handlers: {\n        GET() {\n          return new Response(\"ok\");\n        },\n      },\n    },\n    \"routes/index.tsx\": {\n      handlers: () => {\n        throw new Error(\"fail\");\n      },\n    },\n  });\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - _error render component\", async () => {\n  const server = await createServer({\n    \"routes/_error.tsx\": {\n      default: (ctx) => {\n        return <div>{(ctx.error as Error).message}</div>;\n      },\n    },\n    \"routes/foo/_error.tsx\": {\n      handlers: () => {\n        throw new Error(\"ok\");\n      },\n    },\n    \"routes/foo/index.tsx\": {\n      handlers: () => {\n        throw new Error(\"failing\");\n      },\n    },\n  });\n\n  const res = await server.get(\"/foo\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ok\");\n});\n\n// 404 pages go to the root error page if available\nDeno.test.ignore(\"fsRoutes - _error render on 404\", async () => {\n  // deno-lint-ignore no-explicit-any\n  let error: any = null;\n  const server = await createServer({\n    \"routes/_error.tsx\": {\n      default: (ctx) => {\n        // deno-lint-ignore no-explicit-any\n        error = ctx.error as any;\n        return <p>ok</p>;\n      },\n    },\n    \"routes/foo/_error.tsx\": {\n      default: function foo2(ctx) {\n        // deno-lint-ignore no-explicit-any\n        error = ctx.error as any;\n        return <p>ok foo</p>;\n      },\n    },\n    \"routes/foo/index.tsx\": {\n      default: () => {\n        return <p>ignore</p>;\n      },\n    },\n  });\n\n  let res = await server.get(\"/foo/a\");\n  let doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ok foo\");\n  expect(error?.status).toEqual(404);\n\n  res = await server.get(\"/\");\n  doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ok\");\n  expect(error?.status).toEqual(404);\n});\n\nDeno.test(\"fsRoutes - skip _error component in non-error\", async () => {\n  const server = await createServer({\n    \"routes/_error.tsx\": {\n      default: function errorComp() {\n        return <div>fail</div>;\n      },\n    },\n    \"routes/index.tsx\": {\n      default: () => <div>ok</div>,\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - route group resolve index\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/(foo)/_layout.tsx\": {\n      default: (ctx) => (\n        <div>\n          layout/<ctx.Component />\n        </div>\n      ),\n    },\n    \"routes/(foo)/index.tsx\": {\n      default: () => <>ok</>,\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"layout/ok\");\n});\n\nDeno.test(\"fsRoutes - route group ignores (_...) folders\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/(_foo)/index.tsx\": {\n      default: () => <div>fail</div>,\n    },\n    \"routes/(foo)/index.tsx\": {\n      default: () => <div>ok</div>,\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - route group specific templates\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/(foo)/_error.tsx\": {\n      default: () => <div>fail foo</div>,\n    },\n    \"routes/(foo)/_layout.tsx\": {\n      default: (ctx) => (\n        <div>\n          {ctx.state.text}/(foo)_layout/<ctx.Component />\n        </div>\n      ),\n    },\n    \"routes/(foo)/_middleware.tsx\": {\n      handlers: (ctx) => {\n        ctx.state.text = \"(foo)_middleware\";\n        return ctx.next();\n      },\n    },\n    \"routes/(foo)/foo.tsx\": {\n      default: () => <div>foo</div>,\n    },\n    \"routes/(foo)/foo_error.tsx\": {\n      default: () => {\n        throw new Error(\"fail\");\n      },\n    },\n    \"routes/(bar)/_error.tsx\": {\n      default: () => <div>fail bar</div>,\n    },\n    \"routes/(bar)/_layout.tsx\": {\n      default: (ctx) => (\n        <div>\n          {ctx.state.text}/(bar)_layout/<ctx.Component />\n        </div>\n      ),\n    },\n    \"routes/(bar)/_middleware.tsx\": {\n      handlers: (ctx) => {\n        ctx.state.text = \"(bar)_middleware\";\n        return ctx.next();\n      },\n    },\n    \"routes/(bar)/bar.tsx\": {\n      default: () => <div>bar</div>,\n    },\n    \"routes/(bar)/bar_error.tsx\": {\n      default: () => {\n        throw new Error(\"fail\");\n      },\n    },\n  });\n\n  let res = await server.get(\"/foo\");\n  let doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\n    \"(foo)_middleware/(foo)_layout/foo\",\n  );\n  res = await server.get(\"/foo_error\");\n  doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\n    \"(foo)_middleware/(foo)_layout/fail foo\",\n  );\n\n  res = await server.get(\"/bar\");\n  doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\n    \"(bar)_middleware/(bar)_layout/bar\",\n  );\n\n  res = await server.get(\"/bar_error\");\n  doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\n    \"(bar)_middleware/(bar)_layout/fail bar\",\n  );\n});\n\nDeno.test(\"fsRoutes - route group order #2\", async () => {\n  const server = await createServer({\n    \"routes/(app)/[org]/[app]/index.tsx\": {\n      default: () => <div>fail</div>,\n    },\n    \"routes/auth/login.tsx\": {\n      default: () => <div>ok</div>,\n    },\n  });\n\n  const res = await server.get(\"/auth/login\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - route group order #3\", async () => {\n  const server = await createServer({\n    \"routes/(app)/(foo)/foo/index.tsx\": {\n      default: () => <div>fail</div>,\n    },\n    \"routes/(foo)/foo/bar.tsx\": {\n      default: () => <div>ok</div>,\n    },\n  });\n\n  const res = await server.get(\"/foo/bar\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"ok\");\n});\n\nDeno.test(\"fsRoutes - async route components\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/_error.tsx\": {\n      default: async () => {\n        await delay(1);\n        return <div>fail foo</div>;\n      },\n    },\n    \"routes/_layout.tsx\": {\n      default: async (ctx) => {\n        await delay(1);\n        return (\n          <div>\n            {ctx.state.text}/_layout/<ctx.Component />\n          </div>\n        );\n      },\n    },\n    \"routes/foo.tsx\": {\n      default: async () => {\n        await delay(1);\n        return <div>foo</div>;\n      },\n    },\n    \"routes/foo_error.tsx\": {\n      default: async () => {\n        await delay(1);\n        throw new Error(\"fail\");\n      },\n    },\n  });\n\n  let res = await server.get(\"/foo\");\n  let doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"/_layout/foo\");\n\n  res = await server.get(\"/foo_error\");\n  doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"/_layout/fail foo\");\n});\n\nDeno.test(\"fsRoutes - async route components returning response\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/_app.tsx\": {\n      default: async (ctx) => {\n        await delay(1);\n        if (ctx.url.searchParams.has(\"app\")) {\n          return new Response(\"_app\");\n        }\n        return (\n          <div>\n            _app/<ctx.Component />\n          </div>\n        );\n      },\n    },\n    \"routes/_layout.tsx\": {\n      default: async (ctx) => {\n        await delay(1);\n        if (ctx.url.searchParams.has(\"layout\")) {\n          return new Response(\"_layout\");\n        }\n        return (\n          <div>\n            _layout/<ctx.Component />\n          </div>\n        );\n      },\n    },\n    \"routes/index.tsx\": {\n      default: async (ctx) => {\n        await delay(1);\n        if (ctx.url.searchParams.has(\"index\")) {\n          return new Response(\"index\");\n        }\n        return <div>index</div>;\n      },\n    },\n  });\n\n  let res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"_app/_layout/index\");\n\n  res = await server.get(\"/?app\");\n  let text = await res.text();\n  expect(text).toEqual(\"_app\");\n\n  res = await server.get(\"/?layout\");\n  text = await res.text();\n  expect(text).toEqual(\"_layout\");\n\n  res = await server.get(\"/?index\");\n  text = await res.text();\n  expect(text).toEqual(\"index\");\n});\n\nDeno.test(\n  \"fsRoutes - returns response code from error route\",\n  async () => {\n    const server = await createServer<{ text: string }>({\n      \"routes/_error.tsx\": {\n        default: () => <div>fail</div>,\n      },\n      \"routes/fail.tsx\": {\n        default: () => {\n          throw new Error(\"fail\");\n        },\n      },\n      \"routes/foo/_error.tsx\": {\n        default: () => {\n          throw new HttpError(501);\n        },\n      },\n      \"routes/foo/fail.tsx\": {\n        default: () => {\n          throw new Error(\"fail\");\n        },\n      },\n    });\n\n    let res = await server.get(\"/fooa\");\n    await res.body?.cancel();\n    expect(res.status).toEqual(404);\n\n    res = await server.get(\"/fail\");\n    await res.body?.cancel();\n    expect(res.status).toEqual(500);\n\n    res = await server.get(\"/foo/fail\");\n    await res.body?.cancel();\n    expect(res.status).toEqual(501);\n  },\n);\n\nDeno.test(\n  \"fsRoutes - set headers from handler\",\n  async () => {\n    const server = await createServer<{ text: string }>({\n      \"routes/index.tsx\": {\n        handler: (ctx) => {\n          return ctx.render(<h1>hello</h1>, {\n            headers: { \"X-Foo\": \"123\" },\n            status: 418,\n            statusText: \"I'm a fresh teapot\",\n          });\n        },\n      },\n    });\n\n    const res = await server.get(\"/\");\n    await res.body?.cancel();\n    expect(res.status).toEqual(418);\n    expect(res.statusText).toEqual(\"I'm a fresh teapot\");\n    expect(res.headers.get(\"X-Foo\")).toEqual(\"123\");\n  },\n);\n\nDeno.test(\"fsRoutes - set request init from handler #2\", async () => {\n  const server = await createServer({\n    \"routes/index.tsx\": {\n      handler: () => {\n        return page(\"foo\", { status: 404, headers: { \"X-Foo\": \"123\" } });\n      },\n      default: (ctx) => {\n        // deno-lint-ignore no-explicit-any\n        return <p>{ctx.data as any}</p>;\n      },\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n  expect(doc.body.firstChild?.textContent).toEqual(\"foo\");\n  expect(res.status).toEqual(404);\n  expect(res.headers.get(\"X-Foo\")).toEqual(\"123\");\n});\n\nDeno.test(\"fsRoutes - sortRoutePaths\", () => {\n  let routes = [\n    \"/foo/[id]\",\n    \"/foo/[...slug]\",\n    \"/foo/bar\",\n    \"/foo/_layout\",\n    \"/foo/index\",\n    \"/foo/_middleware\",\n    \"/foo/bar/_middleware\",\n    \"/foo/_error\",\n    \"/foo/bar/index\",\n    \"/foo/bar/_error\",\n    \"/_error\",\n    \"/foo/bar/[...foo]\",\n    \"/foo/bar/baz\",\n    \"/foo/bar/_layout\",\n  ];\n  let sorted = [\n    \"/_error\",\n    \"/foo/_middleware\",\n    \"/foo/_layout\",\n    \"/foo/_error\",\n    \"/foo/index\",\n    \"/foo/bar/_middleware\",\n    \"/foo/bar/_layout\",\n    \"/foo/bar/_error\",\n    \"/foo/bar/index\",\n    \"/foo/bar/baz\",\n    \"/foo/bar/[...foo]\",\n    \"/foo/bar\",\n    \"/foo/[id]\",\n    \"/foo/[...slug]\",\n  ];\n  routes.sort(sortRoutePaths);\n  expect(routes).toEqual(sorted);\n\n  routes = [\n    \"/js/index.js\",\n    \"/js/_layout.js\",\n    \"/jsx/index.jsx\",\n    \"/jsx/_layout.jsx\",\n    \"/ts/index.ts\",\n    \"/ts/_layout.tsx\",\n    \"/tsx/index.tsx\",\n    \"/tsx/_layout.tsx\",\n  ];\n  routes.sort(sortRoutePaths);\n  sorted = [\n    \"/js/_layout.js\",\n    \"/js/index.js\",\n    \"/jsx/_layout.jsx\",\n    \"/jsx/index.jsx\",\n    \"/ts/_layout.tsx\",\n    \"/ts/index.ts\",\n    \"/tsx/_layout.tsx\",\n    \"/tsx/index.tsx\",\n  ];\n  expect(routes).toEqual(sorted);\n\n  // Skip over groups\n  routes = [\n    \"/(app)/[org]/[app]/index.ts\",\n    \"/auth/login.ts\",\n  ];\n  routes.sort(sortRoutePaths);\n  sorted = [\n    \"/auth/login.ts\",\n    \"/(app)/[org]/[app]/index.ts\",\n  ];\n  expect(routes).toEqual(sorted);\n\n  routes = [\n    \"/auth/login.ts\",\n    \"/(app)/[org]/[app]/index.ts\",\n  ];\n  routes.sort(sortRoutePaths);\n  sorted = [\n    \"/auth/login.ts\",\n    \"/(app)/[org]/[app]/index.ts\",\n  ];\n  expect(routes).toEqual(sorted);\n});\n\nDeno.test(\"fsRoutes - sortRoutePaths with groups\", () => {\n  let routes = [\n    \"/(authed)/_middleware.ts\",\n    \"/(authed)/index.ts\",\n    \"/about.tsx\",\n  ];\n  routes.sort(sortRoutePaths);\n  let sorted = [\n    \"/about.tsx\",\n    \"/(authed)/_middleware.ts\",\n    \"/(authed)/index.ts\",\n  ];\n  expect(routes).toEqual(sorted);\n\n  routes = [\n    \"/_app\",\n    \"/(authed)/_middleware\",\n    \"/(authed)/_layout\",\n    \"/_error\",\n    \"/(authed)/index\",\n    \"/login\",\n    \"/auth/login\",\n    \"/auth/logout\",\n    \"/(authed)/(account)/account\",\n    \"/(authed)/api/slug\",\n    \"/hooks/github\",\n    \"/(authed)/[org]/_middleware\",\n    \"/(authed)/[org]/index\",\n  ];\n  routes.sort(sortRoutePaths);\n  sorted = [\n    \"/_app\",\n    \"/_error\",\n    \"/login\",\n    \"/auth/login\",\n    \"/auth/logout\",\n    \"/hooks/github\",\n    \"/(authed)/_middleware\",\n    \"/(authed)/_layout\",\n    \"/(authed)/index\",\n    \"/(authed)/api/slug\",\n    \"/(authed)/(account)/account\",\n    \"/(authed)/[org]/_middleware\",\n    \"/(authed)/[org]/index\",\n  ];\n  expect(routes).toEqual(sorted);\n});\n\nDeno.test(\"fsRoutes - registers default GET route for component without GET handler\", async () => {\n  const server = await createServer<{ value: boolean }>({\n    \"routes/noGetHandler.tsx\": {\n      default: (ctx) => {\n        return <h1>{ctx.state.value ? \"true\" : \"false\"}</h1>;\n      },\n      handlers: {\n        POST: () => new Response(\"POST\"),\n      },\n    },\n  });\n\n  const postRes = await server.post(\"/noGetHandler\");\n  expect(postRes.status).toEqual(200);\n  expect(postRes.headers.get(\"Content-Type\")).toEqual(\n    \"text/plain;charset=UTF-8\",\n  );\n  expect(await postRes.text()).toEqual(\"POST\");\n\n  const getRes = await server.get(\"/noGetHandler\");\n  expect(getRes.status).toEqual(200);\n  expect(getRes.headers.get(\"Content-Type\")).toEqual(\n    \"text/html; charset=utf-8\",\n  );\n  expect(await getRes.text()).toContain(\n    \"<h1>false</h1>\",\n  );\n});\n\nDeno.test(\"fsRoutes - default GET route works with nested middleware\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/_middleware.ts\": {\n      handler: (ctx) => {\n        ctx.state.text = \"A\";\n        return ctx.next();\n      },\n    },\n    \"routes/foo/_middleware.ts\": {\n      handler: (ctx) => {\n        ctx.state.text += \"B\";\n        return ctx.next();\n      },\n    },\n    \"routes/foo/noGetHandler.tsx\": {\n      default: (ctx) => {\n        return <h1>{ctx.state.text}</h1>;\n      },\n      handlers: {\n        POST: () => new Response(\"POST\"),\n      },\n    },\n  });\n\n  const postRes = await server.post(\"/foo/noGetHandler\");\n  expect(postRes.status).toEqual(200);\n  expect(postRes.headers.get(\"Content-Type\")).toEqual(\n    \"text/plain;charset=UTF-8\",\n  );\n  expect(await postRes.text()).toEqual(\"POST\");\n\n  const getRes = await server.get(\"/foo/noGetHandler\");\n  expect(getRes.status).toEqual(200);\n  expect(getRes.headers.get(\"Content-Type\")).toEqual(\n    \"text/html; charset=utf-8\",\n  );\n  expect(await getRes.text()).toContain(\n    \"<h1>AB</h1>\",\n  );\n});\n\nDeno.test(\"fsRoutes - default GET route doesn't override existing handler\", async () => {\n  const server = await createServer<{ value: boolean }>({\n    \"routes/withGetHandler.tsx\": {\n      default: (ctx) => {\n        return <h1>{ctx.state.value ? \"true\" : \"false\"}</h1>;\n      },\n      handlers: {\n        POST: () => new Response(\"POST\"),\n        GET: (ctx) => {\n          ctx.state.value = true;\n          return page();\n        },\n      },\n    },\n  });\n\n  const postRes = await server.post(\"/withGetHandler\");\n  expect(postRes.status).toEqual(200);\n  expect(postRes.headers.get(\"Content-Type\")).toEqual(\n    \"text/plain;charset=UTF-8\",\n  );\n  expect(await postRes.text()).toEqual(\"POST\");\n\n  const getRes = await server.get(\"/withGetHandler\");\n  expect(getRes.status).toEqual(200);\n  expect(getRes.headers.get(\"Content-Type\")).toEqual(\n    \"text/html; charset=utf-8\",\n  );\n  expect(await getRes.text()).toContain(\n    \"<h1>true</h1>\",\n  );\n});\n\nDeno.test(\"support numeric keys\", async () => {\n  const TestComponent = () => <div>foo</div>;\n\n  const server = await createServer({\n    \"routes/index.tsx\": {\n      default: () => {\n        return (\n          <>\n            <TestComponent key={0} />\n            <TestComponent key={1} />\n            ok\n          </>\n        );\n      },\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const text = await res.text();\n  expect(text).toContain(\"ok\");\n});\n\n// Issue https://github.com/denoland/fresh/issues/2802\nDeno.test(\"support bigint keys\", async () => {\n  const TestComponent = () => <div>foo</div>;\n\n  const server = await createServer({\n    \"routes/index.tsx\": {\n      default: () => {\n        return (\n          <>\n            <TestComponent key={9007199254740991n} />\n            ok\n          </>\n        );\n      },\n    },\n  });\n\n  const res = await server.get(\"/\");\n  const text = await res.text();\n  expect(text).toContain(\"ok\");\n  expect(text).toContain(\"key:9007199254740991\");\n});\n\nDeno.test(\"fsRoutes - warn on _middleware with object handler\", async () => {\n  // deno-lint-ignore no-explicit-any\n  using warnSpy = stub(console, \"warn\", fn(() => {}) as any);\n  const server = await createServer({\n    \"routes/_middleware.ts\": { handler: { GET: () => new Response(\"ok\") } },\n    \"routes/index.ts\": { handler: () => new Response(\"ok\") },\n  });\n\n  await server.get(\"/\");\n\n  expect(warnSpy.fake).toHaveBeenCalledTimes(1);\n  expect(warnSpy.fake).toHaveBeenLastCalledWith(\n    \"🍋 %c[WARNING] Unsupported route config: Middleware does not support object handlers with GET, POST, etc. in routes/_middleware.ts\",\n    expect.any(String),\n  );\n});\n\nDeno.test(\"fsRoutes - warn on _layout handler\", async () => {\n  // deno-lint-ignore no-explicit-any\n  using warnSpy = stub(console, \"warn\", fn(() => {}) as any);\n  const server = await createServer({\n    \"routes/_layout.ts\": {\n      handler: () => new Response(\"ok\"),\n      default: (ctx) => (\n        <div>\n          <ctx.Component />\n        </div>\n      ),\n    },\n    \"routes/index.ts\": { default: () => <>ok</> },\n  });\n\n  await server.get(\"/\");\n\n  expect(warnSpy.fake).toHaveBeenCalledTimes(1);\n  expect(warnSpy.fake).toHaveBeenLastCalledWith(\n    \"🍋 %c[WARNING] Unsupported route config: Layout does not support handlers\",\n    expect.any(String),\n  );\n});\n\n// Middleware issue in 2.0.0-alpha.40\nDeno.test(\"fsRoutes - call correct middleware\", async () => {\n  const server = await createServer<{ text: string }>({\n    \"routes/_middleware.ts\": {\n      handler: async function middleware(ctx) {\n        ctx.state.text = \"_middleware\";\n        return await ctx.next();\n      },\n    },\n    \"routes/index.ts\": {\n      default: async (ctx) => await new Response(ctx.state.text),\n    },\n    \"routes/admin/_middleware.ts\": {\n      handler: async function adminMiddleware(ctx) {\n        ctx.state.text += \"admin/_middleware\";\n        return await ctx.next();\n      },\n    },\n    \"routes/foo/index.ts\": {\n      handler: (ctx) => {\n        return new Response(ctx.state.text);\n      },\n    },\n  });\n\n  let res = await server.get(\"/\");\n  let text = await res.text();\n  expect(text).toEqual(\"_middleware\");\n\n  res = await server.get(\"/foo\");\n  text = await res.text();\n  expect(text).toEqual(\"_middleware\");\n});\n\n// Issue: https://github.com/denoland/fresh/issues/2045\nDeno.test(\"fsRoutes - merge group methods\", async () => {\n  const server = await createServer({\n    \"routes/(foo)/bar/index.ts\": {\n      handler: {\n        POST: () => new Response(\"POST ok\"),\n      },\n    },\n    \"routes/bar/index.ts\": {\n      handler: {\n        GET: () => new Response(\"GET ok\"),\n      },\n    },\n  });\n\n  let res = await server.get(\"/bar\");\n  expect(await res.text()).toEqual(\"GET ok\");\n\n  res = await server.post(\"/bar\");\n  expect(await res.text()).toEqual(\"POST ok\");\n});\n\nDeno.test(\"fsRoutes - ignores test files in routes folder\", async () => {\n  const fs = createFakeFs({\n    \"routes/index.tsx\": {\n      default: () => <>index</>,\n    },\n    \"routes/index_test.tsx\": {\n      default: () => <>index_test</>,\n    },\n    \"routes/foo_test.ts\": {\n      handler: () => new Response(\"foo_test\"),\n    },\n    \"routes/bar.test.ts\": {\n      handler: () => new Response(\"bar_test\"),\n    },\n    \"routes/baz.test.tsx\": {\n      default: () => <>baz_test</>,\n    },\n    \"routes/qux_test.js\": {\n      handler: () => new Response(\"qux_test\"),\n    },\n    \"routes/valid.tsx\": {\n      default: () => <>valid</>,\n    },\n  });\n\n  const TEST_FILE_PATTERN = /[._]test\\.(?:[tj]sx?|[mc][tj]s)$/;\n  const routeDir = path.join(fs.cwd(), \"routes\");\n  const rawFiles = await crawlRouteDir(\n    fs,\n    routeDir,\n    [TEST_FILE_PATTERN],\n    () => {},\n  );\n\n  // Only index.tsx and valid.tsx should be found, test files should be filtered out\n  expect(rawFiles.length).toEqual(2);\n  expect(rawFiles.some((f) => f.filePath.includes(\"index.tsx\"))).toBe(true);\n  expect(rawFiles.some((f) => f.filePath.includes(\"valid.tsx\"))).toBe(true);\n\n  // Ensure none of the test files are included\n  expect(rawFiles.some((f) => f.filePath.includes(\"index_test.tsx\"))).toBe(\n    false,\n  );\n  expect(rawFiles.some((f) => f.filePath.includes(\"foo_test.ts\"))).toBe(false);\n  expect(rawFiles.some((f) => f.filePath.includes(\"bar.test.ts\"))).toBe(false);\n  expect(rawFiles.some((f) => f.filePath.includes(\"baz.test.tsx\"))).toBe(false);\n  expect(rawFiles.some((f) => f.filePath.includes(\"qux_test.js\"))).toBe(false);\n});\n\nDeno.test(\"fsRoutes - pattern argument\", async () => {\n  const server = await createServer({\n    \"routes/index.ts\": {\n      handler: {\n        GET: () => new Response(\"ok\"),\n      },\n    },\n    \"routes/foo.ts\": {\n      handler: {\n        GET: () => new Response(\"ok\"),\n      },\n    },\n  }, \"/mount\");\n\n  let res = await server.get(\"/mount\");\n  expect(await res.text()).toEqual(\"ok\");\n\n  res = await server.get(\"/mount/foo\");\n  expect(await res.text()).toEqual(\"ok\");\n});\n"
  },
  {
    "path": "packages/fresh/src/handlers.ts",
    "content": "import type { Context } from \"./context.ts\";\nimport type { Method } from \"./router.ts\";\n\nexport interface PageResponse<T> {\n  data: T;\n  headers?: HeadersInit;\n  status?: number;\n}\n\n/**\n * Create a {@link PageResponse} object that can be returned from a route\n * handler. This will cause the page component to be rendered with the specified\n * data and response options.\n *\n * ```ts\n * export const handlers = define.handlers({\n *   GET: (ctx) => {\n *     return page({ message: \"Hello, world!\" }, {\n *       headers: { \"Cache-Control\": \"public, max-age=3600\" },\n *       status: 201,\n *     });\n *   },\n * });\n *\n * export default define.page<typeof handlers>(({ data }) => {\n *   return <h1>{data.message}</h1>;\n * });\n * ```\n *\n * @param data The data to pass to the page component to be rendered.\n * @param options Additional options to use when constructing the response object.\n * @returns A {@link PageResponse} object that should be returned from a route handler.\n */\nexport function page<T>(data: T, options?: {\n  headers?: HeadersInit;\n  status?: number;\n}): PageResponse<T>;\nexport function page(): PageResponse<undefined>;\nexport function page<T>(data?: T, options?: {\n  headers?: HeadersInit;\n  status?: number;\n}): PageResponse<T> {\n  return {\n    data: data ?? undefined as T,\n    headers: options?.headers,\n    status: options?.status,\n  };\n}\n\n/**\n * A handler function that can be used to specify how a given route should\n * handle requests.\n *\n * The handler function can either return a {@link Response} object, or some\n * data that can be rendered by a page component. See {@link HandlerFn} for more\n * information.\n *\n * ### Per method handlers\n *\n * A route handler can be specific to a given HTTP method (GET, POST, PUT,\n * DELETE, etc). To define a method-specific handler, specify an object that\n * maps method names to functions that conform to the {@link HandlerFn}\n * signature.\n *\n * ```ts\n * export const handlers = define.handlers({\n *   GET: (ctx) => {\n *     return new Response(\"Hello from a GET request!\");\n *   },\n *   POST: (ctx) => {\n *     return new Response(\"Hello from a POST request!\");\n *   }\n * });\n * ```\n *\n * Any requests to methods not specified in the handler object will result in a\n * 405 Method Not Allowed response. If you want to handle these requests, you\n * can define a catch-all handler.\n *\n * If a GET handler is specified, but no HEAD handler is specified, a HEAD\n * handler will automatically be generated that calls the GET handler and\n * strips the response body.\n *\n * ### Catch-all handlers\n *\n * A route handler can also catch all requests in a route. To define a catch-all\n * handler, specify a function that conforms to the {@link HandlerFn} signature.\n * This function will be called for all requests, regardless of the method.\n *\n * ```ts\n * export const handlers = define.handlers((ctx) => {\n *   return new Response(`Hello from a ${ctx.req.method} request!`);\n * });\n * ```\n */\nexport type RouteHandler<Data, State> =\n  | HandlerFn<Data, State>\n  | HandlerByMethod<Data, State>;\n\nexport function isHandlerByMethod<D, S>(\n  handler: RouteHandler<D, S> | RouteHandler<D, S>[] | null,\n): handler is HandlerByMethod<D, S> {\n  return handler !== null && !Array.isArray(handler) &&\n    typeof handler === \"object\";\n}\n\n/**\n * A handler function that is invoked when a request is made to a route. The\n * handler function is passed a {@link Context} object that contains the\n * original request object, as well as any state related to the current request.\n *\n * The handler function can either return a {@link Response} object, which will\n * be sent back to the client, or some data that will be passed to the routes'\n * page component for rendering.\n *\n * ### Returning a Response\n *\n * If the handler function returns a {@link Response} object, the response will\n * be sent back to the client. This can be used to send back static content, or\n * to redirect the client to another URL.\n *\n * ```ts\n * export const handler = define.handlers((ctx) => {\n *   return new Response(\"Hello, world!\");\n * });\n * ```\n *\n * ### Returning data\n *\n * If the handler function returns an object with a `data` property, the data\n * will be passed to the page component, where it can be rendered into HTML.\n *\n * ```ts\n * export const handler = define.handlers((ctx) => {\n *   return { data: { message: \"Hello, world!\" } };\n * });\n *\n * export default definePage<typeof handler>(({ data }) => {\n *   return <h1>{data.message}</h1>;\n * });\n * ```\n *\n * When returning data, you can also specify additional properties that will be\n * used when constructing the response object from the HTML generated by the\n * page component. For example, you can specify custom headers, a custom status\n * code, or a list of elements to include in the `<head>`.\n *\n * ```tsx\n * export const handler = define.handlers((ctx) => {\n *   return {\n *     data: { message: \"Hello, world!\" },\n *     headers: { \"Cache-Control\": \"public, max-age=3600\" },\n *     status: 201,\n *     head: [<title>Hello, world!</title>],\n *   };\n * });\n * ```\n *\n * ### Asynchronous handlers\n *\n * The handler function can also be asynchronous. This can be useful if you need\n * to fetch data from an external source, or perform some other asynchronous\n * operation before returning a response.\n *\n * ```ts\n * export const handler = define.handlers(async (ctx) => {\n *   const resp = await fetch(\"https://api.example.com/data\").;\n *   if (!resp.ok) {\n *     throw new Error(\"Failed to fetch data\");\n *   }\n *   const data = await resp.json();\n *   return { data };\n * });\n * ```\n *\n * If you initiate multiple asynchronous operations in a handler, you can use\n * `Promise.all` to wait for all of them to complete at the same time. This can\n * speed up the response time of your handler, as it allows you to perform\n * multiple operations concurrently.\n *\n * ```ts\n * export const handler = define.handlers(async (ctx) => {\n *   const [resp1, resp2] = await Promise.all([\n *     fetch(\"https://api.example.com/data1\")\n *       .then((resp) => resp.json()),\n *     fetch(\"https://api.example.com/data2\")\n *       .then((resp) => resp.json()),\n *   ]);\n *   return { data: { data1, data2 } };\n * });\n * ```\n */\nexport interface HandlerFn<Data, State> {\n  (ctx: Context<State>):\n    | Response\n    | PageResponse<Data>\n    | Promise<Response | PageResponse<Data>>;\n}\n\n/**\n * A set of handler functions that routes requests based on the HTTP method.\n *\n * See {@link RouteHandler} for more information on how to use this type.\n */\nexport type HandlerByMethod<Data, State> = {\n  [M in Method]?: HandlerFn<Data, State>;\n};\n\nexport type RouteData<\n  Handler extends RouteHandler<unknown, unknown>,\n> = Handler extends (RouteHandler<infer Data, unknown>) ? Data\n  : never;\n"
  },
  {
    "path": "packages/fresh/src/internals.ts",
    "content": "import * as path from \"@std/path\";\n\nexport { setBuildCache, setErrorInterceptor } from \"./app.ts\";\nexport { IslandPreparer, ProdBuildCache } from \"./build_cache.ts\";\nexport { path };\nexport { ASSET_CACHE_BUST_KEY } from \"./constants.ts\";\n"
  },
  {
    "path": "packages/fresh/src/internals_dev.ts",
    "content": "export { crawlFsItem } from \"./dev/fs_crawl.ts\";\nexport { type FsAdapter, fsAdapter } from \"./fs.ts\";\nexport {\n  type FsRouteFileNoMod,\n  generateServerEntry,\n  generateSnapshotServer,\n  type IslandModChunk,\n  type PendingStaticFile,\n  prepareStaticFile,\n  writeCompiledEntry,\n} from \"./dev/dev_build_cache.ts\";\nexport { specToName } from \"./dev/builder.ts\";\nexport { pathToSpec, UniqueNamer } from \"./utils.ts\";\nexport { updateCheck } from \"./dev/update_check.ts\";\nexport { TEST_FILE_PATTERN, UPDATE_INTERVAL } from \"./constants.ts\";\n"
  },
  {
    "path": "packages/fresh/src/jsonify/__snapshots__/round_trip_test.ts.snap",
    "content": "export const snapshot = {};\n\nsnapshot[`round trip - undefined 1`] = `\"-1\"`;\n\nsnapshot[`round trip - null 1`] = `\"-2\"`;\n\nsnapshot[`round trip - 1 1`] = `\"[1]\"`;\n\nsnapshot[`round trip - 2 1`] = `\"[2]\"`;\n\nsnapshot[`round trip - -2 1`] = `\"[-2]\"`;\n\nsnapshot[`round trip - Infinity 1`] = `\"-4\"`;\n\nsnapshot[`round trip - -Infinity 1`] = `\"-5\"`;\n\nsnapshot[`round trip - 0 1`] = `\"[0]\"`;\n\nsnapshot[`round trip - -0 1`] = `\"-6\"`;\n\nsnapshot[`round trip - NaN 1`] = `\"-3\"`;\n\nsnapshot[`round trip - 0 2`] = `\"[0]\"`;\n\nsnapshot[`round trip - true 1`] = `\"[true]\"`;\n\nsnapshot[`round trip - false 1`] = `\"[false]\"`;\n\nsnapshot[`round trip - 'abc' 1`] = `'[\"abc\"]'`;\n\nsnapshot[`round trip - 1n 1`] = `'[[\"BigInt\",\"1\"]]'`;\n\nsnapshot[`round trip - -1n 1`] = `'[[\"BigInt\",\"-1\"]]'`;\n\nsnapshot[`round trip - [ 1, 2, 3 ] 1`] = `\"[[1,2,3],1,2,3]\"`;\n\nsnapshot[`round trip - [ null, undefined, -2 ] 1`] = `\"[[-2,-1,1],-2]\"`;\n\nsnapshot[`round trip - { a: 1, b: null, c: -2 } 1`] = `'[{\"a\":1,\"b\":-2,\"c\":2},1,-2]'`;\n\nsnapshot[`round trip - Uint8Array(3) [ 1, 2, 3 ] 1`] = `'[[\"Uint8Array\",\"AQID\"]]'`;\n\nsnapshot[`round trip - URL {\\\\n  href: 'https://fresh.deno.dev/',\\\\n  origin: 'https://fresh.deno.dev',\\\\n  protocol: 'https:',\\\\n  username: '',\\\\n  password: '',\\\\n  host: 'fresh.deno.dev',\\\\n  hostname: 'fresh.deno.dev',\\\\n  port: '',\\\\n  pathname: '/',\\\\n  hash: '',\\\\n  search: ''\\\\n} 1`] = `'[[\"URL\",\"https://fresh.deno.dev/\"]]'`;\n\nsnapshot[`round trip - 1990-05-31T00:00:00.000Z 1`] = `'[[\"Date\",\"1990-05-31T00:00:00.000Z\"]]'`;\n\nsnapshot[`round trip - Map(2) { 1 => null, undefined => -2 } 1`] = `'[[\"Map\",[1,-2,-1,2]],1,-2]'`;\n\nsnapshot[`round trip - Set(5) { 1, 2, null, -2, NaN } 1`] = `'[[\"Set\",[1,2,-2,3,-3]],1,2,-2]'`;\n\nsnapshot[`round trip - [ 1, <1 empty item>, 3 ] 1`] = `\"[[1,-7,2],1,3]\"`;\n\nsnapshot[`round trip - /foo[\"]/ 1`] = `'[[\"RegExp\",\"foo[\\\\\\\\\"]\", \"\"]]'`;\n\nsnapshot[`round trip - { a: { foo: 123 }, b: [ { foo: 123 }, { foo: 123 } ] } 1`] = `'[{\"a\":1,\"b\":3},{\"foo\":2},123,[1,1]]'`;\n\nsnapshot[`round trip - <ref *1> { a: 1, b: [Circular *1] } 1`] = `'[{\"a\":1,\"b\":0},1]'`;\n"
  },
  {
    "path": "packages/fresh/src/jsonify/constants.ts",
    "content": "export const UNDEFINED = -1;\nexport const NULL = -2;\nexport const NAN = -3;\nexport const INFINITY_POS = -4;\nexport const INFINITY_NEG = -5;\nexport const ZERO_NEG = -6;\nexport const HOLE = -7;\n"
  },
  {
    "path": "packages/fresh/src/jsonify/custom_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport { parse } from \"./parse.ts\";\nimport { stringify } from \"./stringify.ts\";\nimport { Signal, signal } from \"@preact/signals\";\n\nDeno.test(\"custom parse - Point\", () => {\n  class Point {\n    constructor(public x: number, public y: number) {\n      this.x = x;\n      this.y = y;\n    }\n  }\n\n  const str = stringify(new Point(30, 40), {\n    Point: (value) =>\n      value instanceof Point ? { value: [value.x, value.y] } : undefined,\n  });\n\n  expect(str).toEqual('[[\"Point\",1],[2,3],30,40]');\n\n  const point = parse(str, {\n    Point: ([x, y]: [number, number]) => new Point(x, y),\n  });\n  expect(point).toEqual(new Point(30, 40));\n});\n\nDeno.test(\"custom parse - Signals\", () => {\n  const res = parse<Signal>('[[\"Signal\",1],2]', {\n    Signal: (value) => signal(value),\n  });\n  expect(res).toBeInstanceOf(Signal);\n  expect(res.peek()).toEqual(2);\n});\n\nDeno.test(\"custom stringify - Signals\", () => {\n  const s = signal(2);\n  expect(stringify(s, {\n    Signal: (s2: unknown) => {\n      return s2 instanceof Signal ? { value: s2.peek() } : undefined;\n    },\n  })).toEqual(\n    '[[\"Signal\",1],2]',\n  );\n});\n\nDeno.test(\"custom parse - Signals with null value\", () => {\n  const res = parse<Signal>('[[\"Signal\",-2]]', {\n    Signal: (value) => signal(value),\n  });\n  expect(res).toBeInstanceOf(Signal);\n  expect(res.peek()).toEqual(null);\n});\n\nDeno.test(\"custom stringify - Signals with null value\", () => {\n  const s = signal(null);\n  expect(stringify(s, {\n    Signal: (s2: unknown) => {\n      return s2 instanceof Signal ? { value: s2.peek() } : undefined;\n    },\n  })).toEqual(\n    '[[\"Signal\",-2]]',\n  );\n});\n\nDeno.test(\"custom parse - Signals with undefined value\", () => {\n  const res = parse<Signal>('[[\"Signal\",-1]]', {\n    Signal: (value) => signal(value),\n  });\n  expect(res).toBeInstanceOf(Signal);\n  expect(res.peek()).toEqual(undefined);\n});\n\nDeno.test(\"custom stringify - Signals with undefined value\", () => {\n  const s = signal(undefined);\n  expect(stringify(s, {\n    Signal: (s2: unknown) => {\n      return s2 instanceof Signal ? { value: s2.peek() } : undefined;\n    },\n  })).toEqual(\n    '[[\"Signal\",-1]]',\n  );\n});\n\nDeno.test(\"custom stringify - referenced Signals\", () => {\n  const s = signal(2);\n  expect(stringify([s, s], {\n    Signal: (s2: unknown) => {\n      return s2 instanceof Signal ? { value: s2.peek() } : undefined;\n    },\n  })).toEqual(\n    '[[1,1],[\"Signal\",2],2]',\n  );\n});\n"
  },
  {
    "path": "packages/fresh/src/jsonify/parse.ts",
    "content": "import {\n  HOLE,\n  INFINITY_NEG,\n  INFINITY_POS,\n  NAN,\n  NULL,\n  UNDEFINED,\n  ZERO_NEG,\n} from \"./constants.ts\";\n\n// deno-lint-ignore no-explicit-any\nexport type CustomParser = Record<string, (value: any) => unknown>;\n\nexport function parse<T = unknown>(\n  value: string,\n  custom?: CustomParser | undefined,\n): T {\n  const data = JSON.parse(value);\n  if (!Array.isArray(data)) return unpack([], [], data, custom) as T;\n  const hydrated = new Array(data.length);\n  unpack(data, hydrated, 0, custom);\n  return hydrated[0];\n}\n\nfunction unpack(\n  arr: unknown[],\n  hydrated: unknown[],\n  idx: number,\n  custom: CustomParser | undefined,\n): unknown {\n  switch (idx) {\n    case UNDEFINED:\n      return undefined;\n    case NULL:\n      return null;\n    case NAN:\n      return NaN;\n    case INFINITY_POS:\n      return Infinity;\n    case INFINITY_NEG:\n      return -Infinity;\n    case ZERO_NEG:\n      return -0;\n  }\n\n  if (idx in hydrated) return hydrated[idx];\n\n  const current = arr[idx];\n  if (typeof current === \"number\") {\n    return hydrated[idx] = current;\n  } else if (\n    typeof current === \"string\" || typeof current === \"boolean\" ||\n    current === null\n  ) {\n    return hydrated[idx] = current;\n  } else if (Array.isArray(current)) {\n    if (current.length > 0 && typeof current[0] === \"string\") {\n      const name = current[0];\n      if (custom !== undefined && name in custom) {\n        const fn = custom[name];\n        const ref = current[1];\n        const value = unpack(arr, hydrated, ref, custom);\n        return hydrated[idx] = fn(value);\n      }\n      switch (name) {\n        case \"BigInt\":\n          return hydrated[idx] = BigInt(current[1]);\n        case \"URL\":\n          return hydrated[idx] = new URL(current[1]);\n        case \"Date\":\n          return hydrated[idx] = new Date(current[1]);\n        case \"RegExp\":\n          return hydrated[idx] = new RegExp(current[1], current[2]);\n        case \"Set\": {\n          const set = new Set();\n          for (let i = 0; i < current[1].length; i++) {\n            const ref = current[1][i];\n            set.add(unpack(arr, hydrated, ref, custom));\n          }\n          return hydrated[idx] = set;\n        }\n        case \"Map\": {\n          const set = new Map();\n          for (let i = 0; i < current[1].length; i++) {\n            const refKey = current[1][i++];\n            const key = unpack(arr, hydrated, refKey, custom);\n            const refValue = current[1][i];\n            const value = unpack(arr, hydrated, refValue, custom);\n\n            set.set(key, value);\n          }\n          return hydrated[idx] = set;\n        }\n        case \"Uint8Array\":\n          return hydrated[idx] = b64decode(current[1]);\n      }\n    } else {\n      const actual = new Array(current.length);\n      hydrated[idx] = actual;\n      for (let i = 0; i < current.length; i++) {\n        const ref = current[i];\n        if (ref === HOLE) {\n          continue;\n        } else {\n          actual[i] = unpack(arr, hydrated, ref, custom);\n        }\n      }\n      return actual;\n    }\n  } else if (typeof current === \"object\") {\n    const actual: Record<string, unknown> = {};\n    hydrated[idx] = actual;\n    const keys = Object.keys(current);\n    for (let i = 0; i < keys.length; i++) {\n      const key = keys[i];\n      // deno-lint-ignore no-explicit-any\n      const ref = (current as any)[key];\n      actual[key] = unpack(arr, hydrated, ref, custom);\n    }\n    return actual;\n  }\n}\n\nfunction b64decode(b64: string): Uint8Array {\n  const binString = atob(b64);\n  const size = binString.length;\n  const bytes = new Uint8Array(size);\n  for (let i = 0; i < size; i++) {\n    bytes[i] = binString.charCodeAt(i);\n  }\n  return bytes;\n}\n"
  },
  {
    "path": "packages/fresh/src/jsonify/round_trip_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport { parse } from \"./parse.ts\";\nimport { assertSnapshot } from \"@std/testing/snapshot\";\nimport { inspect } from \"node:util\";\nimport { stringify } from \"./stringify.ts\";\n\nconst inner = { foo: 123 };\nconst references = { a: inner, b: [inner, inner] };\n\nconst circular = { a: 1, b: null as unknown };\ncircular.b = circular;\n\nconst TESTS = [\n  undefined,\n  null,\n  1,\n  2,\n  -2,\n  Infinity,\n  -Infinity,\n  0,\n  -0,\n  NaN,\n  1.2 -\n  1.2,\n  true,\n  false,\n  \"abc\",\n  1n,\n  -1n,\n  [1, 2, 3],\n  [null, undefined, -2],\n  { a: 1, b: null, c: -2 },\n  new Uint8Array([1, 2, 3]),\n  new URL(\"https://fresh.deno.dev\"),\n  new Date(\"1990-05-31\"),\n  new Map([[1, null], [undefined, -2]]),\n  new Set([1, 2, null, -2, NaN]),\n  [1, , 3],\n  /foo[\"]/,\n  references,\n  circular,\n];\n\nfor (const value of TESTS) {\n  Deno.test(`round trip - ${inspect(value)}`, async (t) => {\n    const str = stringify(value);\n    await assertSnapshot(t, str);\n    const parsed = parse(str);\n    expect(parsed).toEqual(value);\n  });\n}\n"
  },
  {
    "path": "packages/fresh/src/jsonify/stringify.ts",
    "content": "import {\n  HOLE,\n  INFINITY_NEG,\n  INFINITY_POS,\n  NAN,\n  NULL,\n  UNDEFINED,\n  ZERO_NEG,\n} from \"./constants.ts\";\n\nexport type Stringifiers = Record<\n  string,\n  // deno-lint-ignore no-explicit-any\n  (value: any) => { value: any } | undefined\n>;\n\n/**\n * Serializes the following:\n *\n * - `null`\n * - `undefined`\n * - `boolean`\n * - `number`\n * - `bigint`\n * - `string`\n * - `array`\n * - `object` (no prototypes)\n * - `Uint8Array`\n * - `URL`\n * - `Date`\n * - `RegExp`\n * - `Set`\n * - `Map`\n *\n * Circular references are supported and objects with the same reference are\n * serialized only once.\n */\nexport function stringify(data: unknown, custom?: Stringifiers): string {\n  const out: string[] = [];\n  const indexes = new Map<unknown, number>();\n  const res = serializeInner(out, indexes, data, custom);\n  if (res < 0) {\n    return String(res);\n  }\n  return `[${out.join(\",\")}]`;\n}\n\nfunction serializeInner(\n  out: string[],\n  indexes: Map<unknown, number>,\n  value: unknown,\n  custom: Stringifiers | undefined,\n): number {\n  const seenIdx = indexes.get(value);\n  if (seenIdx !== undefined) return seenIdx;\n\n  if (value === undefined) return UNDEFINED;\n  if (value === null) return NULL;\n  if (Number.isNaN(value)) return NAN;\n  if (value === Infinity) return INFINITY_POS;\n  if (value === -Infinity) return INFINITY_NEG;\n  if (value === 0 && 1 / value < 0) return ZERO_NEG;\n\n  const idx = out.length;\n  out.push(\"\");\n  indexes.set(value, idx);\n\n  let str = \"\";\n\n  if (typeof value === \"number\") {\n    str += String(value);\n  } else if (typeof value === \"boolean\") {\n    str += String(value);\n  } else if (typeof value === \"bigint\") {\n    str += `[\"BigInt\",\"${value}\"]`;\n  } else if (typeof value === \"string\") {\n    str += JSON.stringify(value);\n  } else if (Array.isArray(value)) {\n    str += \"[\";\n    for (let i = 0; i < value.length; i++) {\n      if (i in value) {\n        str += serializeInner(out, indexes, value[i], custom);\n      } else {\n        str += HOLE;\n      }\n\n      if (i < value.length - 1) {\n        str += \",\";\n      }\n    }\n    str += \"]\";\n  } else if (typeof value === \"object\") {\n    if (custom !== undefined) {\n      for (const k in custom) {\n        const fn = custom[k];\n        if (fn === undefined) continue;\n\n        const res = fn(value);\n        if (res === undefined) continue;\n\n        const innerIdx = serializeInner(out, indexes, res.value, custom);\n        str = `[\"${k}\",${innerIdx}]`;\n        out[idx] = str;\n        return idx;\n      }\n    }\n\n    if (value instanceof URL) {\n      str += `[\"URL\",\"${value.href}\"]`;\n    } else if (value instanceof Date) {\n      str += `[\"Date\",\"${value.toISOString()}\"]`;\n    } else if (value instanceof RegExp) {\n      str += `[\"RegExp\",${JSON.stringify(value.source)}, \"${value.flags}\"]`;\n    } else if (value instanceof Uint8Array) {\n      str += `[\"Uint8Array\",\"${b64encode(value.buffer)}\"]`;\n    } else if (value instanceof Set) {\n      const items = new Array(value.size);\n      let i = 0;\n      value.forEach((v) => {\n        items[i++] = serializeInner(out, indexes, v, custom);\n      });\n      str += `[\"Set\",[${items.join(\",\")}]]`;\n    } else if (value instanceof Map) {\n      const items = new Array(value.size * 2);\n      let i = 0;\n      value.forEach((v, k) => {\n        items[i++] = serializeInner(out, indexes, k, custom);\n        items[i++] = serializeInner(out, indexes, v, custom);\n      });\n      str += `[\"Map\",[${items.join(\",\")}]]`;\n    } else {\n      str += \"{\";\n      const keys = Object.keys(value);\n      for (let i = 0; i < keys.length; i++) {\n        const key = keys[i];\n        str += JSON.stringify(key) + \":\";\n        // deno-lint-ignore no-explicit-any\n        str += serializeInner(out, indexes, (value as any)[key], custom);\n\n        if (i < keys.length - 1) {\n          str += \",\";\n        }\n      }\n      str += \"}\";\n    }\n  } else if (typeof value === \"function\") {\n    throw new Error(`Serializing functions is not supported.`);\n  }\n\n  out[idx] = str;\n  return idx;\n}\n\n// deno-fmt-ignore\nconst base64abc = [\n  \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\",\n  \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"a\", \"b\", \"c\", \"d\",\n  \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\", \"q\", \"r\", \"s\",\n  \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\",\n  \"8\", \"9\", \"+\", \"/\",\n];\n\n/**\n * CREDIT: https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727\n * Encodes a given Uint8Array, ArrayBuffer or string into RFC4648 base64 representation\n */\nexport function b64encode(buffer: ArrayBufferLike): string {\n  const uint8 = new Uint8Array(buffer);\n  let result = \"\",\n    i;\n  const l = uint8.length;\n  for (i = 2; i < l; i += 3) {\n    result += base64abc[uint8[i - 2] >> 2];\n    result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];\n    result += base64abc[((uint8[i - 1] & 0x0f) << 2) | (uint8[i] >> 6)];\n    result += base64abc[uint8[i] & 0x3f];\n  }\n  if (i === l + 1) {\n    // 1 octet yet to write\n    result += base64abc[uint8[i - 2] >> 2];\n    result += base64abc[(uint8[i - 2] & 0x03) << 4];\n    result += \"==\";\n  }\n  if (i === l) {\n    // 2 octets yet to write\n    result += base64abc[uint8[i - 2] >> 2];\n    result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];\n    result += base64abc[(uint8[i - 1] & 0x0f) << 2];\n    result += \"=\";\n  }\n  return result;\n}\n"
  },
  {
    "path": "packages/fresh/src/jsonify/stringify_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport { stringify } from \"./stringify.ts\";\n\nDeno.test(\"stringify - object prototype\", () => {\n  const obj = { __proto__: 123, foo: 1 };\n  expect(stringify(obj)).toEqual(\n    '[{\"foo\":1},1]',\n  );\n});\n\nDeno.test(\"stringify - throw serializing functions\", () => {\n  const fn = () => {};\n  expect(() => stringify(fn)).toThrow();\n});\n"
  },
  {
    "path": "packages/fresh/src/middlewares/cors.ts",
    "content": "import type { Context } from \"../context.ts\";\nimport type { Middleware } from \"./mod.ts\";\n\nexport type CORSOptions<State> = {\n  /**\n   * The value of [`Access-Control-Allow-Origin`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin) CORS header\n   */\n  origin:\n    | string\n    | string[]\n    | ((origin: string, ctx: Context<State>) => string | undefined | null);\n  /**\n   * Sets the permitted methods for the [`Access-Control-Allow-Methods`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Methods) CORS header.\n   */\n  allowMethods?: string[];\n  /**\n   * Indicate the HTTP headers that can be used during the actual request. Sets the [`Access-Control-Allow-Headers`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Headers) header\n   */\n  allowHeaders?: string[];\n  /**\n   * The value of [`Access-Control-Max-Age`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Max-Age) CORS header\n   */\n  maxAge?: number;\n  /**\n   * The value of [`Access-Control-Allow-Credentials`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Credentials) CORS header\n   */\n  credentials?: boolean;\n  /**\n   * The value of [`Access-Control-Expose-Headers`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Expose-Headers) CORS header\n   */\n  exposeHeaders?: string[];\n};\n\n/**\n * CORS Middleware to set [`Cross-Origin-Resource-Sharing`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) headers.\n *\n * @param [options] - The options for the CORS middleware.\n * @returns The Fresh middleware handler function.\n *\n * @example Basic usage\n * ```ts\n * // main.ts or routes/_middleware.ts\n * import { cors } from 'fresh';\n *\n * export const handler = [\n *   cors({ origin: '*' }), // Allow all origins\n *   // other middlewares or main route handler\n * ];\n * ```\n *\n * @example Advanced usage\n * ```ts\n * export const handler = [\n *   cors({\n *     origin: 'http://example.com',\n *     allowHeaders: ['X-Custom-Header', 'Upgrade-Insecure-Requests'],\n *     allowMethods: ['POST', 'GET', 'OPTIONS'],\n *     exposeHeaders: ['Content-Length', 'X-Kuma-Revision'],\n *     maxAge: 600,\n *     credentials: true,\n *   }),\n *   // ...\n * ];\n * ```\n */\nexport function cors<State>(options?: CORSOptions<State>): Middleware<State> {\n  const opts: CORSOptions<State> = {\n    origin: \"*\",\n    allowMethods: [\"GET\", \"HEAD\", \"PUT\", \"POST\", \"DELETE\", \"PATCH\"],\n    allowHeaders: [],\n    exposeHeaders: [],\n    ...options,\n  };\n\n  const addHeaderProperties = (\n    headers: Headers,\n    allowOrigin: string | null | undefined,\n    opts: CORSOptions<State>,\n  ) => {\n    if (allowOrigin) {\n      headers.set(\"Access-Control-Allow-Origin\", allowOrigin);\n    }\n\n    if (opts.credentials) {\n      headers.set(\"Access-Control-Allow-Credentials\", \"true\");\n    }\n\n    if (opts.exposeHeaders?.length) {\n      headers.set(\n        \"Access-Control-Expose-Headers\",\n        opts.exposeHeaders.join(\",\"),\n      );\n    }\n  };\n\n  const optsOrigin = opts.origin;\n\n  return async (ctx) => {\n    const requestOrigin = ctx.req.headers.get(\"origin\") || \"\";\n\n    let allowOrigin: string | null = null;\n    if (typeof optsOrigin === \"string\") {\n      if (optsOrigin === \"*\") {\n        allowOrigin = optsOrigin;\n      } else {\n        allowOrigin = optsOrigin === requestOrigin ? requestOrigin : null;\n      }\n    } else if (typeof optsOrigin === \"function\") {\n      allowOrigin = optsOrigin(requestOrigin, ctx) ?? null;\n    } else {\n      allowOrigin = optsOrigin.includes(requestOrigin) ? requestOrigin : null;\n    }\n\n    const vary = new Set<string>();\n    // Add 'Origin' to Vary if a specific origin is allowed, not '*'\n    if (opts.origin !== \"*\" && allowOrigin && allowOrigin !== \"*\") {\n      vary.add(\"Origin\");\n    }\n\n    if (ctx.req.method === \"OPTIONS\") {\n      const headers = new Headers();\n\n      addHeaderProperties(\n        headers,\n        allowOrigin,\n        opts,\n      );\n\n      if (opts.maxAge != null) {\n        headers.set(\"Access-Control-Max-Age\", opts.maxAge.toString());\n      }\n\n      if (opts.allowMethods?.length) {\n        headers.set(\n          \"Access-Control-Allow-Methods\",\n          opts.allowMethods.join(\",\"),\n        );\n      }\n\n      let allowHeaders = opts.allowHeaders;\n      if (!allowHeaders?.length) {\n        const reqHeaders = ctx.req.headers.get(\n          \"Access-Control-Request-Headers\",\n        );\n        if (reqHeaders) {\n          allowHeaders = reqHeaders.split(/\\s*,\\s*/);\n        }\n      }\n\n      if (allowHeaders?.length) {\n        headers.set(\n          \"Access-Control-Allow-Headers\",\n          allowHeaders.join(\",\"),\n        );\n        vary.add(\"Access-Control-Request-Headers\");\n      }\n\n      if (vary.size > 0) {\n        headers.set(\"Vary\", Array.from(vary).join(\", \"));\n      } else {\n        headers.delete(\"Vary\"); // Ensure Vary is not set if no conditions met\n      }\n\n      headers.delete(\"Content-Length\");\n      headers.delete(\"Content-Type\");\n\n      return new Response(null, {\n        status: 204,\n        statusText: \"No Content\",\n        headers,\n      });\n    }\n\n    // For non-OPTIONS requests\n    const res = await ctx.next();\n\n    addHeaderProperties(\n      res.headers,\n      allowOrigin,\n      opts,\n    );\n\n    // Merge our calculated varyValues with any existing Vary from downstream response\n    if (vary.size > 0) {\n      const existing = res.headers.get(\"Vary\");\n      if (existing) {\n        existing.split(/\\s*,\\s*/).forEach((v) => vary.add(v));\n      }\n\n      res.headers.set(\"Vary\", Array.from(vary).join(\", \"));\n    }\n\n    return res;\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/middlewares/cors_test.ts",
    "content": "import { App } from \"../app.ts\";\nimport { cors } from \"./cors.ts\";\nimport { expect } from \"@std/expect\";\n\nDeno.test(\"CORS - GET default\", async () => {\n  const handler = new App()\n    .use(cors())\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  const res = await handler(new Request(\"https://localhost/\"));\n\n  expect(res.status).toBe(200);\n  expect(res.headers.get(\"Access-Control-Allow-Origin\")).toBe(\"*\");\n  expect(res.headers.get(\"Vary\")).toBeNull();\n});\n\nDeno.test(\"CORS - Preflight default\", async () => {\n  const handler = new App()\n    .use(cors())\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  const req = new Request(\"https://localhost/\", { method: \"OPTIONS\" });\n  req.headers.append(\n    \"Access-Control-Request-Headers\",\n    \"X-PINGOTHER, Content-Type\",\n  );\n\n  const res = await handler(req);\n\n  expect(res.status).toBe(204);\n  expect(res.headers.get(\"Access-Control-Allow-Methods\")?.split(\",\")[0])\n    .toBe(\"GET\");\n  expect(res.headers.get(\"Access-Control-Allow-Headers\")?.split(\",\"))\n    .toEqual([\"X-PINGOTHER\", \"Content-Type\"]);\n});\n\nDeno.test(\"CORS - Preflight with options\", async () => {\n  const handler = new App()\n    .use(\n      cors({\n        origin: \"http://example.com\",\n        allowHeaders: [\"X-Custom-Header\", \"Upgrade-Insecure-Requests\"],\n        allowMethods: [\"POST\", \"GET\", \"OPTIONS\"],\n        exposeHeaders: [\"Content-Length\", \"X-Kuma-Revision\"],\n        maxAge: 600,\n        credentials: true,\n      }),\n    )\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  const res = await handler(\n    new Request(\"https://localhost/\", {\n      method: \"OPTIONS\",\n      headers: { origin: \"http://example.com\" },\n    }),\n  );\n\n  expect(res.headers.get(\"Access-Control-Allow-Origin\")).toBe(\n    \"http://example.com\",\n  );\n  expect(res.headers.get(\"Vary\")?.split(/\\s*,\\s*/)).toEqual(\n    expect.arrayContaining([\"Origin\"]),\n  );\n  expect(res.headers.get(\"Access-Control-Allow-Headers\")?.split(/\\s*,\\s*/))\n    .toEqual([\n      \"X-Custom-Header\",\n      \"Upgrade-Insecure-Requests\",\n    ]);\n  expect(res.headers.get(\"Access-Control-Allow-Methods\")?.split(/\\s*,\\s*/))\n    .toEqual([\n      \"POST\",\n      \"GET\",\n      \"OPTIONS\",\n    ]);\n  expect(res.headers.get(\"Access-Control-Expose-Headers\")?.split(/\\s*,\\s*/))\n    .toEqual([\n      \"Content-Length\",\n      \"X-Kuma-Revision\",\n    ]);\n  expect(res.headers.get(\"Access-Control-Max-Age\")).toBe(\"600\");\n  expect(res.headers.get(\"Access-Control-Allow-Credentials\")).toBe(\"true\");\n});\n\nDeno.test(\"CORS - Disallow an unmatched origin\", async () => {\n  const handler = new App()\n    .use(\n      cors({\n        origin: \"http://example.com\",\n        allowHeaders: [\"X-Custom-Header\", \"Upgrade-Insecure-Requests\"],\n        allowMethods: [\"POST\", \"GET\", \"OPTIONS\"],\n        exposeHeaders: [\"Content-Length\", \"X-Kuma-Revision\"],\n        maxAge: 600,\n        credentials: true,\n      }),\n    )\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  const res = await handler(\n    new Request(\"https://localhost/\", {\n      method: \"OPTIONS\",\n      headers: { origin: \"http://example.net\" },\n    }),\n  );\n\n  expect(res.headers.has(\"Access-Control-Allow-Origin\")).toBeFalsy();\n});\n\nDeno.test(\"CORS - Allow multiple origins\", async () => {\n  const handler = new App()\n    .use(cors({\n      origin: [\n        \"http://example.com\",\n        \"http://example.org\",\n        \"http://example.dev\",\n      ],\n    }))\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  let res = await handler(\n    new Request(\"http://localhost/\", {\n      headers: {\n        Origin: \"http://example.org\",\n      },\n    }),\n  );\n  expect(res.headers.get(\"Access-Control-Allow-Origin\")).toBe(\n    \"http://example.org\",\n  );\n\n  res = await handler(new Request(\"http://localhost/\"));\n  expect(\n    res.headers.has(\"Access-Control-Allow-Origin\"),\n    \"An unmatched origin should be disallowed\",\n  ).toBeFalsy();\n\n  res = await handler(\n    new Request(\"http://localhost/api3/abc\", {\n      headers: {\n        Referer: \"http://example.net/\",\n      },\n    }),\n  );\n  expect(\n    res.headers.has(\"Access-Control-Allow-Origin\"),\n    \"An unmatched origin should be disallowed\",\n  ).toBeFalsy();\n});\n\nDeno.test(\"CORS - Allow different Vary header value\", async () => {\n  const handler = new App()\n    .use(cors({\n      origin: [\n        \"http://example.com\",\n        \"http://example.org\",\n        \"http://example.dev\",\n      ],\n    }))\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  const res = await handler(\n    new Request(\"http://localhost/\", {\n      headers: {\n        Vary: \"accept-encoding\",\n        Origin: \"http://example.com\",\n      },\n    }),\n  );\n\n  expect(res.status).toBe(200);\n  expect(res.headers.get(\"Access-Control-Allow-Origin\")).toBe(\n    \"http://example.com\",\n  );\n  expect(res.headers.get(\"Vary\")).toBe(\"Origin\");\n});\n\nDeno.test(\"CORS - Allow origins by function\", async () => {\n  const handler = new App()\n    .use(cors({\n      origin: (\n        origin,\n      ) => (origin.endsWith(\".example.com\") ? origin : \"http://example.com\"),\n    }))\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  let res = await handler(\n    new Request(\"http://localhost/\", {\n      headers: {\n        Origin: \"http://subdomain.example.com\",\n      },\n    }),\n  );\n  expect(res.headers.get(\"Access-Control-Allow-Origin\")).toBe(\n    \"http://subdomain.example.com\",\n  );\n\n  res = await handler(new Request(\"http://localhost/\"));\n  expect(res.headers.get(\"Access-Control-Allow-Origin\")).toBe(\n    \"http://example.com\",\n  );\n\n  res = await handler(\n    new Request(\"http://localhost/\", {\n      headers: {\n        Referer: \"http://evil-example.com/\",\n      },\n    }),\n  );\n  expect(res.headers.get(\"Access-Control-Allow-Origin\")).toBe(\n    \"http://example.com\",\n  );\n});\n\nDeno.test(\"CORS - With raw Response object\", async () => {\n  const handler = new App()\n    .use(cors())\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  const res = await handler(new Request(\"http://localhost/\"));\n\n  expect(res.headers.get(\"Access-Control-Allow-Origin\")).toBe(\"*\");\n  expect(res.headers.get(\"Vary\")).toBeNull();\n});\n\nDeno.test(\"CORS - Should not return duplicate header values\", async () => {\n  const handler = new App()\n    .use(cors({ origin: \"http://example.com\" }))\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  const res = await handler(\n    new Request(\"http://localhost/\", {\n      headers: {\n        origin: \"http://example.com\",\n      },\n    }),\n  );\n\n  expect(res.headers.get(\"Access-Control-Allow-Origin\")).toBe(\n    \"http://example.com\",\n  );\n});\n"
  },
  {
    "path": "packages/fresh/src/middlewares/csp.ts",
    "content": "import type { Middleware } from \"./mod.ts\";\n\n/** Options for Content-Security-Policy middleware */\nexport interface CSPOptions {\n  /** If true, sets Content-Security-Policy-Report-Only header instead of Content-Security-Policy */\n  reportOnly?: boolean;\n\n  /** If set, adds Reporting-Endpoints, report-to, and report-uri directive */\n  reportTo?: string;\n\n  /** Additional CSP directives to add or override the defaults */\n  csp?: string[];\n}\n\n/**\n * Middleware to set Content-Security-Policy headers\n *\n * @param options - CSP options\n *\n * @example Basic usage\n * ```ts\n * app.use(csp({\n *   reportOnly: true,\n *   reportTo: '/api/csp-reports',\n *   csp: [\n *     \"script-src 'self' 'unsafe-inline' 'https://example.com'\",\n *   ],\n * }));\n * ```\n */\nexport function csp<State>(options: CSPOptions = {}): Middleware<State> {\n  const {\n    reportOnly = false,\n    reportTo,\n    csp = [],\n  } = options;\n\n  const defaultCsp = [\n    \"default-src 'self'\",\n    \"script-src 'self' 'unsafe-inline'\", // unsafe-inline needed for fresh\n    \"style-src 'self' 'unsafe-inline'\",\n    \"font-src 'self'\",\n    \"img-src 'self' data:\",\n    \"media-src 'self' data: blob:\",\n    \"worker-src 'self' blob:\",\n    \"connect-src 'self'\",\n    \"object-src 'none'\",\n    \"base-uri 'self'\",\n    \"form-action 'self'\",\n    \"frame-ancestors 'none'\",\n    \"upgrade-insecure-requests\",\n  ];\n\n  const cspDirectives = [...defaultCsp, ...csp];\n  if (reportTo) {\n    cspDirectives.push(`report-to csp-endpoint`);\n    cspDirectives.push(`report-uri ${reportTo}`); // deprecated but some browsers still use it\n  }\n  const cspString = cspDirectives.join(\"; \");\n\n  return async (ctx) => {\n    const res = await ctx.next();\n    const headerName = reportOnly\n      ? \"Content-Security-Policy-Report-Only\"\n      : \"Content-Security-Policy\";\n    res.headers.set(headerName, cspString);\n    if (reportTo) {\n      res.headers.set(\"Reporting-Endpoints\", `csp-endpoint=\"${reportTo}\"`);\n    }\n    return res;\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/middlewares/csp_test.ts",
    "content": "import { expect } from \"@std/expect/expect\";\nimport { App } from \"../app.ts\";\nimport { csp } from \"./csp.ts\";\n\nDeno.test(\"CSP - GET default\", async () => {\n  const handler = new App()\n    .use(csp())\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n  const res = await handler(new Request(\"https://localhost/\"));\n  expect(res.status).toBe(200);\n  expect(res.headers.get(\"Content-Security-Policy\")).toBeDefined();\n  expect(res.headers.get(\"Content-Security-Policy\")).toContain(\n    \"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data:; media-src 'self' data: blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests\",\n  );\n});\n\nDeno.test(\"CSP - GET with override options\", async () => {\n  const handler = new App()\n    .use(csp({\n      reportTo: \"/api/csp-reports\",\n      csp: [\n        \"font-src 'self' 'https://fonts.gstatic.com'\",\n        \"style-src 'self' 'https://fonts.googleapis.com'\",\n      ],\n    }))\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  const res = await handler(new Request(\"https://localhost/\"));\n\n  expect(res.status).toBe(200);\n  expect(res.headers.get(\"Content-Security-Policy\")).toContain(\n    \"font-src 'self' 'https://fonts.gstatic.com'; style-src 'self' 'https://fonts.googleapis.com'\",\n  );\n  expect(res.headers.get(\"Content-Security-Policy\")).toContain(\n    \"report-uri /api/csp-reports\",\n  );\n  expect(res.headers.get(\"Reporting-Endpoints\")).toBe(\n    'csp-endpoint=\"/api/csp-reports\"',\n  );\n});\n\nDeno.test(\"CSP - GET report only\", async () => {\n  const handler = new App()\n    .use(csp({\n      reportOnly: true,\n    }))\n    .get(\"/\", () => new Response(\"ok\"))\n    .handler();\n\n  const res = await handler(new Request(\"https://localhost/\"));\n  expect(res.status).toBe(200);\n  expect(res.headers.get(\"Content-Security-Policy-Report-Only\")).toBeDefined();\n  expect(res.headers.get(\"Content-Security-Policy-Report-Only\")).toContain(\n    \"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data:; media-src 'self' data: blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests\",\n  );\n});\n"
  },
  {
    "path": "packages/fresh/src/middlewares/csrf.ts",
    "content": "import type { Context } from \"../context.ts\";\nimport { HttpError } from \"../error.ts\";\nimport type { Middleware } from \"./mod.ts\";\n\n/** Options for {@linkcode csrf}. **/\n// deno-lint-ignore no-explicit-any\nexport interface CsrfOptions<State = any> {\n  /**\n   * origin - Specifies the allowed origin(s) for requests.\n   *  - string: A single allowed origin.\n   *  - string[]: static allowed origins.\n   *  - function: A function to determine if an origin is allowed.\n   */\n  origin?:\n    | string\n    | string[]\n    | ((origin: string, context: Context<State>) => boolean);\n}\n\n/**\n * CSRF Protection Middleware for Fresh.\n *\n * @param options Options for the CSRF protection middleware.\n * @returns The middleware handler function.\n *\n * @example Basic usage (with defaults)\n * ```ts\n * const app = new App<State>()\n *\n * app.use(csrf())\n * ```\n *\n * @example Specifying static origins\n * ```ts\n * app.use(csrf({ origin: 'https://myapp.example.com' }))\n *\n * // string[]\n * app.use(\n *   csrf({\n *     origin: ['https://myapp.example.com', 'http://development.myapp.example.com'],\n *   })\n * )\n * ```\n *\n * @example Specifying more complex origins\n * ```ts\n * app.use(\n *   '*',\n *   csrf({\n *     origin: (origin) => ['https://myapp.example.com', 'http://development.myapp.example.com'].includes(origin),\n *   })\n * )\n * ```\n */\nexport function csrf<State>(\n  options?: CsrfOptions,\n): Middleware<State> {\n  const isAllowedOrigin = (\n    origin: string | null,\n    ctx: Context<State>,\n  ) => {\n    if (origin === null) {\n      return false;\n    }\n\n    const optsOrigin = options?.origin;\n\n    if (!optsOrigin) {\n      return origin === ctx.url.origin;\n    }\n    if (typeof optsOrigin === \"string\") {\n      return origin === optsOrigin;\n    }\n    if (typeof optsOrigin === \"function\") {\n      return optsOrigin(origin, ctx);\n    }\n    return Array.isArray(optsOrigin) && optsOrigin.includes(origin);\n  };\n\n  return async (ctx) => {\n    const { method, headers } = ctx.req;\n\n    // Safe methods\n    if (method === \"GET\" || method === \"HEAD\" || method === \"OPTIONS\") {\n      return await ctx.next();\n    }\n\n    const secFetchSite = headers.get(\"Sec-Fetch-Site\");\n    const origin = headers.get(\"origin\");\n\n    if (secFetchSite !== null) {\n      if (\n        secFetchSite === \"same-origin\" || secFetchSite === \"none\" ||\n        isAllowedOrigin(origin, ctx)\n      ) {\n        return await ctx.next();\n      }\n\n      throw new HttpError(403);\n    }\n\n    // Neither `Sec-Fetch-Site` or `Origin` is set\n    if (origin === null) {\n      return await ctx.next();\n    }\n\n    if (isAllowedOrigin(origin, ctx)) {\n      return await ctx.next();\n    }\n\n    throw new HttpError(403);\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/middlewares/csrf_test.ts",
    "content": "import { App } from \"../app.ts\";\nimport type { Method } from \"../router.ts\";\nimport { csrf, type CsrfOptions } from \"./csrf.ts\";\nimport { expect } from \"@std/expect\";\n\nfunction testHandler<T>(\n  options?: CsrfOptions<T>,\n): (request: Request) => Promise<Response> {\n  return new App<T>()\n    .use(csrf(options))\n    .all(\"/\", () => new Response(\"hello\"))\n    .handler();\n}\n\nfunction createTests(trusted: CsrfOptions[\"origin\"]) {\n  return async function runTest(\n    method: Method,\n    expected: 200 | 403,\n    test: {\n      secFetchSite?: \"cross-site\" | \"same-origin\" | \"same-site\" | \"none\";\n      origin?: string;\n    } = {},\n  ) {\n    const handler = testHandler({ origin: trusted });\n\n    const headers = new Headers();\n    if (test.secFetchSite) headers.append(\"Sec-Fetch-Site\", test.secFetchSite);\n    if (test.origin) {\n      headers.append(\"Origin\", test.origin);\n    }\n\n    const res = await handler(\n      new Request(\"https://example.com\", { method, headers }),\n    );\n    await res.body?.cancel();\n    expect(res.status).toEqual(expected);\n  };\n}\n\nDeno.test(\"CSRF - allow GET/HEAD/OPTIONS\", async () => {\n  const runTest = createTests(\"https://example.com\");\n\n  await runTest(\"GET\", 200);\n  await runTest(\"HEAD\", 200);\n  await runTest(\"OPTIONS\", 200);\n});\n\nDeno.test(\"CSRF - Sec-Fetch-Site\", async () => {\n  const runTest = createTests(\"https://example.com\");\n\n  await runTest(\"POST\", 200, { secFetchSite: \"same-origin\" });\n  await runTest(\"POST\", 200, { secFetchSite: \"none\" });\n  await runTest(\"POST\", 403, { secFetchSite: \"cross-site\" });\n  await runTest(\"POST\", 403, { secFetchSite: \"same-site\" });\n\n  await runTest(\"POST\", 200);\n  await runTest(\"POST\", 200, { origin: \"https://example.com\" });\n  await runTest(\"POST\", 403, { origin: \"https://attacker.example.com\" });\n  await runTest(\"POST\", 403, { origin: \"null\" });\n\n  await runTest(\"GET\", 200, { secFetchSite: \"cross-site\" });\n  await runTest(\"HEAD\", 200, { secFetchSite: \"cross-site\" });\n  await runTest(\"OPTIONS\", 200, { secFetchSite: \"cross-site\" });\n  await runTest(\"PUT\", 403, { secFetchSite: \"cross-site\" });\n});\n\nDeno.test(\"CSRF - cross origin\", async () => {\n  const runTest = createTests(\"https://trusted.example.com\");\n\n  await runTest(\"POST\", 200, { origin: \"https://trusted.example.com\" });\n  await runTest(\"POST\", 200, {\n    origin: \"https://trusted.example.com\",\n    secFetchSite: \"cross-site\",\n  });\n\n  await runTest(\"POST\", 403, { origin: \"https://attacker.example.com\" });\n  await runTest(\"POST\", 403, {\n    origin: \"https://attacker.example.com\",\n    secFetchSite: \"cross-site\",\n  });\n});\n\nDeno.test(\"CSRF - array origin\", async () => {\n  const runTest = createTests(\n    [\"https://example.com\", \"https://trusted.example.com\"],\n  );\n\n  await runTest(\"POST\", 200, { origin: \"https://trusted.example.com\" });\n  await runTest(\"POST\", 200, { origin: \"https://example.com\" });\n  await runTest(\"POST\", 403, { origin: \"https://foo.example.com\" });\n});\n\nDeno.test(\"CSRF - function origin\", async () => {\n  const runTest = createTests(\n    (origin) => origin === \"https://example.com\",\n  );\n\n  await runTest(\"POST\", 200, { origin: \"https://example.com\" });\n  await runTest(\"POST\", 403, { origin: \"https://trusted.example.com\" });\n  await runTest(\"POST\", 403, { origin: \"https://foo.example.com\" });\n});\n"
  },
  {
    "path": "packages/fresh/src/middlewares/mod.ts",
    "content": "import { type Context, getInternals } from \"../context.ts\";\nimport type { App as _App } from \"../app.ts\";\nimport type { Define as _Define } from \"../define.ts\";\nimport { recordSpanError, tracer } from \"../otel.ts\";\n\n/**\n * A middleware function is the basic building block of Fresh. It allows you\n * to respond to an incoming request in any way you want. You can redirect\n * routes, serve files, create APIs and much more. Middlewares can be chained by\n * calling {@linkcode Context.next|ctx.next()} inside of the function.\n *\n * Middlewares can be synchronous or asynchronous. If a middleware returns a\n * {@linkcode Response} object, the response will be sent back to the client. If\n * a middleware returns a `Promise<Response>`, Fresh will wait for the promise\n * to resolve before sending the response.\n *\n * A {@linkcode Context} object is passed to the middleware function. This\n * object contains the original request object, as well as any state related to\n * the current request. The context object also contains methods to redirect\n * the client to another URL, or to call the next middleware in the chain.\n *\n * Middlewares can be defined as a single function or an array of functions.\n * When an array of middlewares is passed to\n * {@linkcode _App.prototype.use|app.use}, Fresh will call each middleware in the\n * order they are defined.\n *\n * Middlewares can also be defined using the\n * {@linkcode _Define.middleware|define.middleware} method. This\n * method is optional, but it can be useful for type checking and code\n * completion. It does not register the middleware with the app.\n *\n * ## Examples\n *\n * ### Logging middleware\n *\n * This example shows how to create a simple middleware that logs incoming\n * requests.\n *\n * ```ts\n * // Define a middleware function that logs incoming requests. Using the\n * // `define.middleware` method is optional, but it can be useful for type\n * // checking and code completion. It does not register the middleware with the\n * // app.\n * const loggerMiddleware = define.middleware((ctx) => {\n *   console.log(`${ctx.req.method} ${ctx.req.url}`);\n *   // Call the next middleware\n *   return ctx.next();\n * });\n *\n * // To register the middleware to the app, use `app.use`.\n * app.use(loggerMiddleware)\n * ```\n *\n * ### Redirect middleware\n *\n * This example shows how to create a middleware that redirects requests from\n * one URL to another.\n *\n * ```ts\n * // Any request to a URL that starts with \"/legacy/\" will be redirected to\n * // \"/modern\".\n * const redirectMiddleware = define.middleware((ctx) => {\n *   if (ctx.url.pathname.startsWith(\"/legacy/\")) {\n *     return ctx.redirect(\"/modern\");\n *   }\n *\n *   // Otherwise call the next middleware\n *   return ctx.next();\n * });\n *\n * // Again, register the middleware with the app.\n * app.use(redirectMiddleware);\n * ```\n */\nexport type Middleware<State> = (\n  ctx: Context<State>,\n) => Response | Promise<Response>;\n\n/**\n * @deprecated Use {@linkcode Middleware} instead.\n */\nexport type MiddlewareFn<State> = Middleware<State>;\n\n/**\n * A lazy {@linkcode Middleware}\n */\nexport type MaybeLazyMiddleware<State> = (\n  ctx: Context<State>,\n) => Response | Promise<Response | Middleware<State>>;\n\nexport async function runMiddlewares<State>(\n  middlewares: MaybeLazyMiddleware<State>[],\n  ctx: Context<State>,\n  onError?: (err: unknown) => void,\n): Promise<Response> {\n  return await tracer.startActiveSpan(\"middlewares\", {\n    attributes: { \"fresh.middleware.count\": middlewares.length },\n  }, async (span) => {\n    try {\n      let fn = ctx.next;\n      let i = middlewares.length;\n      while (i--) {\n        const local = fn;\n        let next = middlewares[i];\n        const idx = i;\n        fn = async () => {\n          const internals = getInternals(ctx);\n          const { app: prevApp, layouts: prevLayouts } = internals;\n\n          ctx.next = local;\n          try {\n            const result = await next(ctx);\n            if (typeof result === \"function\") {\n              middlewares[idx] = result;\n              next = result;\n              return await result(ctx);\n            }\n\n            return result;\n          } catch (err) {\n            if (ctx.error !== err) {\n              ctx.error = err;\n\n              if (onError !== undefined) {\n                onError(err);\n              }\n            }\n            throw err;\n          } finally {\n            internals.app = prevApp;\n            internals.layouts = prevLayouts;\n          }\n        };\n      }\n      return await fn();\n    } catch (err) {\n      recordSpanError(span, err);\n      throw err;\n    } finally {\n      span.end();\n    }\n  });\n}\n"
  },
  {
    "path": "packages/fresh/src/middlewares/mod_test.ts",
    "content": "import { runMiddlewares } from \"./mod.ts\";\nimport { expect } from \"@std/expect\";\nimport { serveMiddleware } from \"../test_utils.ts\";\nimport type { Middleware } from \"./mod.ts\";\nimport type { Lazy, MaybeLazy } from \"../types.ts\";\n\nDeno.test(\"runMiddleware\", async () => {\n  const middlewares: Middleware<{ text: string }>[] = [\n    (ctx) => {\n      ctx.state.text = \"A\";\n      return ctx.next();\n    },\n    (ctx) => {\n      ctx.state.text += \"B\";\n      return ctx.next();\n    },\n    async (ctx) => {\n      const res = await ctx.next();\n      ctx.state.text += \"C\"; // This should not show up\n      return res;\n    },\n    (ctx) => {\n      return new Response(ctx.state.text);\n    },\n  ];\n\n  const server = serveMiddleware<{ text: string }>((ctx) =>\n    runMiddlewares(middlewares, ctx)\n  );\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"AB\");\n});\n\nDeno.test(\"runMiddleware - middlewares should only be called once\", async () => {\n  const A: Middleware<{ count: number }> = (ctx) => {\n    if (ctx.state.count === undefined) {\n      ctx.state.count = 0;\n    } else {\n      ctx.state.count++;\n    }\n    return ctx.next();\n  };\n\n  const server = serveMiddleware<{ count: number }>((ctx) =>\n    runMiddlewares(\n      [A, (ctx) => new Response(String(ctx.state.count))],\n      ctx,\n    )\n  );\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"0\");\n});\n\nDeno.test(\"runMiddleware - runs multiple stacks\", async () => {\n  type State = { text: string };\n  const A: Middleware<State> = (ctx) => {\n    ctx.state.text += \"A\";\n    return ctx.next();\n  };\n  const B: Middleware<State> = (ctx) => {\n    ctx.state.text += \"B\";\n    return ctx.next();\n  };\n  const C: Middleware<State> = (ctx) => {\n    ctx.state.text += \"C\";\n    return ctx.next();\n  };\n  const D: Middleware<State> = (ctx) => {\n    ctx.state.text += \"D\";\n    return ctx.next();\n  };\n\n  const server = serveMiddleware<State>((ctx) => {\n    ctx.state.text = \"\";\n    return runMiddlewares(\n      [\n        A,\n        B,\n        C,\n        D,\n        (ctx) => new Response(String(ctx.state.text)),\n      ],\n      ctx,\n    );\n  });\n\n  const res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"ABCD\");\n});\n\nDeno.test(\"runMiddleware - throws errors\", async () => {\n  let thrownA: unknown = null;\n  let thrownB: unknown = null;\n  let thrownC: unknown = null;\n\n  const middlewares: Middleware<{ text: string }>[] = [\n    async (ctx) => {\n      try {\n        return await ctx.next();\n      } catch (err) {\n        thrownA = err;\n        throw err;\n      }\n    },\n    async (ctx) => {\n      try {\n        return await ctx.next();\n      } catch (err) {\n        thrownB = err;\n        throw err;\n      }\n    },\n    async (ctx) => {\n      try {\n        return await ctx.next();\n      } catch (err) {\n        thrownC = err;\n        throw err;\n      }\n    },\n    () => {\n      throw new Error(\"fail\");\n    },\n  ];\n\n  const server = serveMiddleware<{ text: string }>((ctx) =>\n    runMiddlewares(middlewares, ctx)\n  );\n\n  try {\n    await server.get(\"/\");\n  } catch {\n    // ignore\n  }\n  expect(thrownA).toBeInstanceOf(Error);\n  expect(thrownB).toBeInstanceOf(Error);\n  expect(thrownC).toBeInstanceOf(Error);\n});\n\nDeno.test(\"runMiddleware - lazy middlewares\", async () => {\n  type State = { text: string };\n\n  let called = 0;\n  // deno-lint-ignore require-await\n  const lazy: Lazy<Middleware<State>> = async () => {\n    called++;\n    return (ctx) => {\n      ctx.state.text += \"_lazy\";\n      return ctx.next();\n    };\n  };\n\n  const middlewares: MaybeLazy<Middleware<State>>[] = [\n    async (ctx) => {\n      ctx.state.text = \"A\";\n      return await ctx.next();\n    },\n    lazy,\n    (ctx) => {\n      ctx.state.text += \"_B\";\n      return new Response(ctx.state.text);\n    },\n  ];\n\n  const server = serveMiddleware<{ text: string }>((ctx) =>\n    runMiddlewares(middlewares, ctx)\n  );\n\n  let res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"A_lazy_B\");\n  expect(called).toEqual(1);\n\n  // Lazy middlewares should only be initialized ones\n  res = await server.get(\"/\");\n  expect(await res.text()).toEqual(\"A_lazy_B\");\n  expect(called).toEqual(1);\n});\n"
  },
  {
    "path": "packages/fresh/src/middlewares/static_files.ts",
    "content": "import type { Middleware } from \"./mod.ts\";\nimport { ASSET_CACHE_BUST_KEY } from \"../constants.ts\";\nimport { BUILD_ID } from \"@fresh/build-id\";\nimport { tracer } from \"../otel.ts\";\nimport { getBuildCache } from \"../context.ts\";\n\n/**\n * Fresh middleware to serve static files from the `static/` directory.\n * ```ts\n * // Enable Fresh static file serving\n * app.use(staticFiles());\n * ```\n */\nexport function staticFiles<T>(): Middleware<T> {\n  return async function freshServeStaticFiles(ctx) {\n    const { req, url, config } = ctx;\n\n    const buildCache = getBuildCache(ctx);\n    if (buildCache === null) return await ctx.next();\n\n    let pathname = decodeURIComponent(url.pathname);\n    if (config.basePath) {\n      pathname = pathname !== config.basePath\n        ? pathname.slice(config.basePath.length)\n        : \"/\";\n    }\n\n    // Fast path bail out\n    const startTime = performance.now() + performance.timeOrigin;\n    const file = await buildCache.readFile(pathname);\n    if (pathname === \"/\" || file === null) {\n      // Optimization: Prevent long responses for favicon.ico requests\n      if (pathname === \"/favicon.ico\") {\n        return new Response(null, { status: 404 });\n      }\n      return await ctx.next();\n    }\n\n    if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n      file.close();\n      return new Response(\"Method Not Allowed\", { status: 405 });\n    }\n\n    const span = tracer.startSpan(\"static file\", {\n      attributes: { \"fresh.span_type\": \"static_file\" },\n      startTime,\n    });\n\n    try {\n      const cacheKey = url.searchParams.get(ASSET_CACHE_BUST_KEY);\n      if (cacheKey !== null && BUILD_ID !== cacheKey) {\n        url.searchParams.delete(ASSET_CACHE_BUST_KEY);\n        const location = url.pathname + url.search;\n        file.close();\n        span.setAttribute(\"fresh.cache\", \"invalid_bust_key\");\n        span.setAttribute(\"fresh.cache_key\", cacheKey);\n        return new Response(null, {\n          status: 307,\n          headers: {\n            location,\n          },\n        });\n      }\n\n      const etag = file.hash;\n      const headers = new Headers({\n        \"Content-Type\": file.contentType,\n        vary: \"If-None-Match\",\n      });\n\n      // In development mode, skip ETag validation to prevent serving stale\n      // cached assets when switching between different apps on the same port\n      if (ctx.config.mode !== \"development\") {\n        const ifNoneMatch = req.headers.get(\"If-None-Match\");\n        if (\n          ifNoneMatch !== null &&\n          (ifNoneMatch === etag || ifNoneMatch === `W/\"${etag}\"`)\n        ) {\n          file.close();\n          span.setAttribute(\"fresh.cache\", \"not_modified\");\n          return new Response(null, { status: 304, headers });\n        } else if (etag !== null) {\n          headers.set(\"Etag\", `W/\"${etag}\"`);\n        }\n      }\n\n      if (\n        ctx.config.mode !== \"development\" &&\n        (BUILD_ID === cacheKey ||\n          url.pathname.startsWith(\n            `${ctx.config.basePath}/_fresh/js/${BUILD_ID}/`,\n          ))\n      ) {\n        span.setAttribute(\"fresh.cache\", \"immutable\");\n        headers.append(\"Cache-Control\", \"public, max-age=31536000, immutable\");\n      } else {\n        span.setAttribute(\"fresh.cache\", \"no_cache\");\n        headers.append(\n          \"Cache-Control\",\n          \"no-cache, no-store, max-age=0, must-revalidate\",\n        );\n      }\n\n      headers.set(\"Content-Length\", String(file.size));\n      if (req.method === \"HEAD\") {\n        file.close();\n        return new Response(null, { status: 200, headers });\n      }\n\n      // Errors in TS 5.9.2, not sure why\n      // deno-lint-ignore no-explicit-any\n      return new Response(file.readable as any, { headers });\n    } finally {\n      span.end();\n    }\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/middlewares/static_files_test.ts",
    "content": "import { staticFiles } from \"./static_files.ts\";\nimport { serveMiddleware } from \"../test_utils.ts\";\nimport type { BuildCache, StaticFile } from \"../build_cache.ts\";\nimport { expect } from \"@std/expect\";\nimport { ASSET_CACHE_BUST_KEY } from \"../constants.ts\";\nimport { BUILD_ID } from \"@fresh/build-id\";\nimport type { Command } from \"../commands.ts\";\nimport type { ServerIslandRegistry } from \"../context.ts\";\nimport { getContentType } from \"../dev/dev_build_cache.ts\";\n\nclass MockBuildCache implements BuildCache {\n  root = \"\";\n  buildId = \"MockId\";\n  files = new Map<string, StaticFile>();\n  islandRegistry: ServerIslandRegistry = new Map();\n  clientEntry = \"\";\n  features = { errorOverlay: false };\n\n  constructor(files: Record<string, { hash: string | null; content: string }>) {\n    const encoder = new TextEncoder();\n    for (const [pathname, info] of Object.entries(files)) {\n      const text = encoder.encode(info.content);\n\n      const normalized = pathname.startsWith(\"/\") ? pathname : \"/\" + pathname;\n      this.files.set(normalized, {\n        hash: info.hash,\n        size: text.byteLength,\n        readable: text,\n        contentType: getContentType(normalized),\n        close: () => {},\n      });\n    }\n  }\n\n  getEntryAssets(): string[] {\n    return [];\n  }\n\n  // deno-lint-ignore no-explicit-any\n  getFsRoutes(): Command<any>[] {\n    return [];\n  }\n\n  // deno-lint-ignore require-await\n  async readFile(pathname: string): Promise<StaticFile | null> {\n    return this.files.get(pathname) ?? null;\n  }\n}\n\nDeno.test(\"static files - 200\", async () => {\n  const buildCache = new MockBuildCache({\n    \"foo.css\": { content: \"body {}\", hash: null },\n  });\n  const server = serveMiddleware(\n    staticFiles(),\n    { buildCache },\n  );\n\n  const res = await server.get(\"/foo.css\");\n  const content = await res.text();\n  expect(res.status).toEqual(200);\n  expect(res.headers.get(\"Content-Type\")).toEqual(\"text/css; charset=UTF-8\");\n  expect(res.headers.get(\"Content-Length\")).toEqual(\"7\");\n  expect(res.headers.get(\"Cache-Control\")).toEqual(\n    \"no-cache, no-store, max-age=0, must-revalidate\",\n  );\n  expect(content).toEqual(\"body {}\");\n});\n\nDeno.test(\"static files - HEAD 200\", async () => {\n  const buildCache = new MockBuildCache({\n    \"foo.css\": { content: \"body {}\", hash: null },\n  });\n  const server = serveMiddleware(\n    staticFiles(),\n    { buildCache },\n  );\n\n  const res = await server.head(\"/foo.css\");\n  const content = await res.text();\n  expect(res.status).toEqual(200);\n  expect(res.headers.get(\"Content-Type\")).toEqual(\"text/css; charset=UTF-8\");\n  expect(res.headers.get(\"Content-Length\")).toEqual(\"7\");\n  expect(content).toEqual(\"\");\n});\n\nDeno.test(\"static files - etag in production\", async () => {\n  const buildCache = new MockBuildCache({\n    \"foo.css\": { content: \"body {}\", hash: \"123\" },\n  });\n  const server = serveMiddleware(\n    staticFiles(),\n    {\n      buildCache,\n      config: {\n        root: \"\",\n        basePath: \"\",\n        mode: \"production\",\n      },\n    },\n  );\n\n  const cacheUrl = `/foo.css?${ASSET_CACHE_BUST_KEY}=${BUILD_ID}`;\n  let res = await server.get(cacheUrl);\n  await res.body?.cancel();\n  expect(res.headers.get(\"Etag\")).toEqual('W/\"123\"');\n\n  let headers = new Headers();\n  headers.append(\"If-None-Match\", \"123\");\n  res = await server.get(cacheUrl, { headers });\n  await res.body?.cancel();\n  expect(res.status).toEqual(304);\n\n  headers = new Headers();\n  headers.append(\"If-None-Match\", 'W/\"123\"');\n  res = await server.get(cacheUrl, { headers });\n  await res.body?.cancel();\n  expect(res.status).toEqual(304);\n});\n\nDeno.test(\"static files - no etag in development\", async () => {\n  const buildCache = new MockBuildCache({\n    \"foo.css\": { content: \"body {}\", hash: \"123\" },\n  });\n  const server = serveMiddleware(\n    staticFiles(),\n    {\n      buildCache,\n      config: {\n        root: \"\",\n        basePath: \"\",\n        mode: \"development\",\n      },\n    },\n  );\n\n  const cacheUrl = `/foo.css`;\n  let res = await server.get(cacheUrl);\n  await res.body?.cancel();\n  expect(res.headers.get(\"Etag\")).toBeNull();\n\n  // Even if client sends If-None-Match in dev mode, always return 200\n  const headers = new Headers();\n  headers.append(\"If-None-Match\", \"123\");\n  res = await server.get(cacheUrl, { headers });\n  await res.body?.cancel();\n  expect(res.status).toEqual(200);\n});\n\nDeno.test(\"static files - 404 on missing favicon.ico\", async () => {\n  const buildCache = new MockBuildCache({});\n  const server = serveMiddleware(\n    staticFiles(),\n    { buildCache },\n  );\n  const res = await server.get(\"favicon.ico\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(404);\n});\n\nDeno.test(\"static files - 405 on wrong HTTP method\", async () => {\n  const buildCache = new MockBuildCache({\n    \"foo.css\": { content: \"body {}\", hash: null },\n  });\n  const server = serveMiddleware(\n    staticFiles(),\n    { buildCache },\n  );\n\n  for (const method of [\"post\", \"patch\", \"put\", \"delete\"]) {\n    // deno-lint-ignore no-explicit-any\n    const res = await (server as any)[method](\"/foo.css\");\n    await res.body?.cancel();\n    expect(res.status).toEqual(405);\n  }\n});\n\nDeno.test(\"static files - disables caching in development\", async () => {\n  const buildCache = new MockBuildCache({\n    \"foo.css\": { content: \"body {}\", hash: null },\n  });\n  const server = serveMiddleware(\n    staticFiles(),\n    {\n      buildCache,\n      config: {\n        root: \"\",\n        basePath: \"\",\n        mode: \"development\",\n      },\n    },\n  );\n\n  const res = await server.get(\"/foo.css\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(200);\n  expect(res.headers.get(\"Cache-Control\")).toEqual(\n    \"no-cache, no-store, max-age=0, must-revalidate\",\n  );\n});\n\nDeno.test(\"static files - enables caching in production\", async () => {\n  const buildCache = new MockBuildCache({\n    \"foo.css\": { content: \"body {}\", hash: null },\n  });\n  const server = serveMiddleware(\n    staticFiles(),\n    {\n      buildCache,\n      config: {\n        root: \"\",\n        basePath: \"\",\n        mode: \"production\",\n      },\n    },\n  );\n\n  const res = await server.get(`/foo.css?${ASSET_CACHE_BUST_KEY}=${BUILD_ID}`);\n  await res.body?.cancel();\n  expect(res.status).toEqual(200);\n  expect(res.headers.get(\"Cache-Control\")).toEqual(\n    \"public, max-age=31536000, immutable\",\n  );\n});\n\nDeno.test(\"static files - decoded pathname\", async () => {\n  const buildCache = new MockBuildCache({\n    \"C#.svg\": { content: \"body {}\", hash: null },\n    \"西安市.png\": { content: \"body {}\", hash: null },\n    \"인천.avif\": { content: \"body {}\", hash: null },\n  });\n  const server = serveMiddleware(\n    staticFiles(),\n    { buildCache },\n  );\n\n  for (\n    const path of [\n      \"C%23.svg\",\n      \"%E8%A5%BF%E5%AE%89%E5%B8%82.png\",\n      \"%EC%9D%B8%EC%B2%9C.avif\",\n    ]\n  ) {\n    const res = await server.get(\"/\" + path);\n    await res.body?.cancel();\n    expect(res.status).toEqual(200);\n  }\n});\n\nDeno.test(\"static files - fallthrough\", async () => {\n  const buildCache = new MockBuildCache({\n    \"foo.css\": { content: \"body {}\", hash: null },\n  });\n\n  const server = serveMiddleware(\n    staticFiles(),\n    { buildCache, next: () => Promise.resolve(new Response(\"it works\")) },\n  );\n\n  let res = await server.get(\"foo.css\");\n  let text = await res.text();\n  expect(text).toEqual(\"body {}\");\n\n  res = await server.get(\"/\");\n  text = await res.text();\n  expect(text).toEqual(\"it works\");\n});\n"
  },
  {
    "path": "packages/fresh/src/middlewares/trailing_slashes.ts",
    "content": "import type { Middleware } from \"./mod.ts\";\n\n/**\n * Fresh middleware to force URLs to end with a slash or never end with one.\n *\n * ```ts\n * // Always add trailing slash\n * app.use(trailingSlashes(\"always\"));\n * // Never add trailing slashes to URLs and remove them if present\n * app.use(trailingSlashes(\"never\"));\n * ```\n */\nexport function trailingSlashes<State>(\n  mode: \"always\" | \"never\",\n): Middleware<State> {\n  return function trailingSlashesMiddleware(ctx) {\n    const url = ctx.url;\n    if (url.pathname !== \"/\") {\n      if (mode === \"always\" && !url.pathname.endsWith(\"/\")) {\n        return ctx.redirect(`${url.pathname}/${url.search}`);\n      } else if (\n        mode === \"never\" && url.pathname.endsWith(\"/\")\n      ) {\n        return ctx.redirect(`${url.pathname.slice(0, -1)}${url.search}`);\n      }\n    }\n    return ctx.next();\n  };\n}\n"
  },
  {
    "path": "packages/fresh/src/middlewares/trailing_slashes_test.ts",
    "content": "// deno-lint-ignore-file require-await\nimport { trailingSlashes } from \"./trailing_slashes.ts\";\nimport { expect } from \"@std/expect\";\nimport { serveMiddleware } from \"../test_utils.ts\";\n\nDeno.test(\"trailingSlashes - always\", async () => {\n  const middleware = trailingSlashes(\"always\");\n  const server = serveMiddleware(middleware, {\n    next: async () => new Response(\"ok\"),\n  });\n\n  let res = await server.get(\"/\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(200);\n  expect(res.headers.get(\"Location\")).toEqual(null);\n\n  res = await server.get(\"/foo\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(302);\n  expect(res.headers.get(\"Location\")).toEqual(\"/foo/\");\n\n  res = await server.get(\"/foo/bar\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(302);\n  expect(res.headers.get(\"Location\")).toEqual(\"/foo/bar/\");\n\n  res = await server.get(\"/foo/bar/\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(200);\n  expect(res.headers.get(\"Location\")).toEqual(null);\n});\n\nDeno.test(\"trailingSlashes - never\", async () => {\n  const middleware = trailingSlashes(\"never\");\n  const server = serveMiddleware(middleware, {\n    next: async () => new Response(\"ok\"),\n  });\n\n  let res = await server.get(\"/\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(200);\n  expect(res.headers.get(\"Location\")).toEqual(null);\n\n  res = await server.get(\"/foo\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(200);\n  expect(res.headers.get(\"Location\")).toEqual(null);\n\n  res = await server.get(\"/foo/\");\n  await res.body?.cancel();\n  expect(res.status).toEqual(302);\n  expect(res.headers.get(\"Location\")).toEqual(\"/foo\");\n});\n"
  },
  {
    "path": "packages/fresh/src/mod.ts",
    "content": "export { App, type ListenOptions } from \"./app.ts\";\nexport { trailingSlashes } from \"./middlewares/trailing_slashes.ts\";\nexport {\n  type HandlerByMethod,\n  type HandlerFn,\n  page,\n  type PageResponse,\n  type RouteData,\n  type RouteHandler,\n} from \"./handlers.ts\";\nexport type { LayoutConfig, Lazy, MaybeLazy, RouteConfig } from \"./types.ts\";\nexport type { Middleware, MiddlewareFn } from \"./middlewares/mod.ts\";\nexport { staticFiles } from \"./middlewares/static_files.ts\";\nexport { csrf, type CsrfOptions } from \"./middlewares/csrf.ts\";\nexport { cors, type CORSOptions } from \"./middlewares/cors.ts\";\nexport { csp, type CSPOptions } from \"./middlewares/csp.ts\";\nexport type { FreshConfig, ResolvedFreshConfig } from \"./config.ts\";\nexport type { Context, FreshContext, Island } from \"./context.ts\";\nexport { createDefine, type Define } from \"./define.ts\";\nexport type { Method } from \"./router.ts\";\nexport { HttpError } from \"./error.ts\";\nexport type { PageProps } from \"./render.ts\";\n"
  },
  {
    "path": "packages/fresh/src/otel.ts",
    "content": "import { type Span, SpanStatusCode, trace } from \"@opentelemetry/api\";\nimport denoJson from \"../deno.json\" with { type: \"json\" };\n\nexport const CURRENT_FRESH_VERSION = denoJson.version;\n\nexport const tracer = trace.getTracer(\"fresh\", CURRENT_FRESH_VERSION);\nexport { trace };\n\nexport function recordSpanError(span: Span, err: unknown) {\n  if (err instanceof Error) {\n    span.recordException(err);\n  } else {\n    span.setStatus({\n      code: SpanStatusCode.ERROR,\n      message: String(err),\n    });\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/render.ts",
    "content": "import {\n  type AnyComponent,\n  type FunctionComponent,\n  h,\n  type RenderableProps,\n  type VNode,\n} from \"preact\";\nimport type { Context } from \"./context.ts\";\nimport { recordSpanError, tracer } from \"./otel.ts\";\n\nexport type AsyncAnyComponent<P> = {\n  (\n    props: RenderableProps<P>,\n    // deno-lint-ignore no-explicit-any\n    context?: any,\n    // deno-lint-ignore no-explicit-any\n  ): Promise<VNode<any> | Response | null>;\n  displayName?: string;\n  defaultProps?: Partial<P> | undefined;\n};\n\n// deno-lint-ignore no-explicit-any\nexport function isAsyncAnyComponent(fn: any): fn is AsyncAnyComponent<any> {\n  return typeof fn === \"function\" && fn.constructor.name === \"AsyncFunction\";\n}\n\nexport async function renderAsyncAnyComponent<Props>(\n  fn: AsyncAnyComponent<Props>,\n  props: RenderableProps<Props>,\n) {\n  return await tracer.startActiveSpan(\n    \"invoke async component\",\n    async (span) => {\n      span.setAttribute(\"fresh.span_type\", \"fs_routes/async_component\");\n      try {\n        const result = (await fn(props)) as VNode | Response;\n        span.setAttribute(\n          \"fresh.component_response\",\n          result instanceof Response ? \"http\" : \"jsx\",\n        );\n        return result;\n      } catch (err) {\n        recordSpanError(span, err);\n        throw err;\n      } finally {\n        span.end();\n      }\n    },\n  );\n}\n\nexport type PageProps<Data = unknown, T = unknown> =\n  & Pick<\n    Context<T>,\n    | \"config\"\n    | \"url\"\n    | \"req\"\n    | \"params\"\n    | \"info\"\n    | \"state\"\n    | \"isPartial\"\n    | \"Component\"\n    | \"error\"\n    | \"route\"\n  >\n  & { data: Data };\n\nexport interface ComponentDef<Data, State> {\n  props: PageProps<Data, State> | null;\n  component: AnyComponent<PageProps<Data, State>>;\n}\n\nexport async function renderRouteComponent<State>(\n  ctx: Context<State>,\n  def: ComponentDef<unknown, State>,\n  child: FunctionComponent,\n): Promise<VNode | Response> {\n  const vnodeProps: PageProps<unknown, State> = {\n    Component: child,\n    config: ctx.config,\n    data: def.props,\n    error: ctx.error,\n    info: ctx.info,\n    isPartial: ctx.isPartial,\n    params: ctx.params,\n    req: ctx.req,\n    state: ctx.state,\n    url: ctx.url,\n    route: ctx.route,\n  };\n\n  if (isAsyncAnyComponent(def.component)) {\n    const result = await renderAsyncAnyComponent(def.component, vnodeProps);\n    if (result instanceof Response) {\n      return result;\n    }\n\n    return result;\n  }\n\n  return h(def.component, vnodeProps) as VNode;\n}\n"
  },
  {
    "path": "packages/fresh/src/router.ts",
    "content": "export type Method =\n  | \"HEAD\"\n  | \"GET\"\n  | \"POST\"\n  | \"PATCH\"\n  | \"PUT\"\n  | \"DELETE\"\n  | \"OPTIONS\";\n\nexport type RouteByMethod<T> = {\n  [m in Method]: T[];\n};\n\nexport interface StaticRouteDef<T> {\n  pattern: string | URLPattern;\n  byMethod: RouteByMethod<T>;\n}\n\nexport interface DynamicRouteDef<T> {\n  pattern: URLPattern;\n  byMethod: RouteByMethod<T>;\n}\n\nfunction newByMethod<T>(): RouteByMethod<T> {\n  return {\n    GET: [],\n    POST: [],\n    PATCH: [],\n    DELETE: [],\n    PUT: [],\n    HEAD: [],\n    OPTIONS: [],\n  };\n}\n\nexport interface RouteResult<T> {\n  params: Record<string, string>;\n  handlers: T[];\n  methodMatch: boolean;\n  pattern: string | null;\n}\n\nexport interface Router<T> {\n  add(\n    method: Method | \"ALL\",\n    pathname: string,\n    handlers: T[],\n  ): void;\n  match(method: Method, url: URL, init?: T[]): RouteResult<T>;\n  getAllowedMethods(pattern: string): string[];\n}\n\nexport const IS_PATTERN = /[*:{}+?()]/;\n\nconst EMPTY: string[] = [];\n\nexport class UrlPatternRouter<T> implements Router<T> {\n  #statics = new Map<string, StaticRouteDef<T>>();\n  #dynamics = new Map<string, DynamicRouteDef<T>>();\n  #dynamicArr: DynamicRouteDef<T>[] = [];\n  #allowed = new Map<string, Set<string>>();\n\n  getAllowedMethods(pattern: string): string[] {\n    const allowed = this.#allowed.get(pattern);\n    if (allowed === undefined) return EMPTY;\n    return Array.from(allowed);\n  }\n\n  add(\n    method: Method,\n    pathname: string,\n    handlers: T[],\n  ) {\n    let allowed = this.#allowed.get(pathname);\n    if (allowed === undefined) {\n      allowed = new Set();\n      this.#allowed.set(pathname, allowed);\n    }\n\n    allowed.add(method);\n\n    let byMethod: RouteByMethod<T>;\n    if (IS_PATTERN.test(pathname)) {\n      let def = this.#dynamics.get(pathname);\n      if (def === undefined) {\n        def = {\n          pattern: new URLPattern({ pathname }),\n          byMethod: newByMethod(),\n        };\n        this.#dynamics.set(pathname, def);\n        this.#dynamicArr.push(def);\n      }\n\n      byMethod = def.byMethod;\n    } else {\n      let def = this.#statics.get(pathname);\n      if (def === undefined) {\n        def = {\n          pattern: pathname,\n          byMethod: newByMethod(),\n        };\n        this.#statics.set(pathname, def);\n      }\n\n      byMethod = def.byMethod;\n    }\n\n    byMethod[method].push(...handlers);\n  }\n\n  match(method: Method, url: URL, init: T[] = []): RouteResult<T> {\n    const result: RouteResult<T> = {\n      params: Object.create(null),\n      handlers: init,\n      methodMatch: false,\n      pattern: null,\n    };\n\n    const staticMatch = this.#statics.get(url.pathname);\n    if (staticMatch !== undefined) {\n      result.pattern = url.pathname;\n\n      let handlers = staticMatch.byMethod[method];\n      if (method === \"HEAD\" && handlers.length === 0) {\n        handlers = staticMatch.byMethod.GET;\n      }\n      if (handlers.length > 0) {\n        result.methodMatch = true;\n        result.handlers.push(...handlers);\n      }\n\n      return result;\n    }\n\n    for (let i = 0; i < this.#dynamicArr.length; i++) {\n      const route = this.#dynamicArr[i];\n\n      const match = route.pattern.exec(url);\n      if (match === null) continue;\n\n      result.pattern = route.pattern.pathname;\n\n      let handlers = route.byMethod[method];\n      if (method === \"HEAD\" && handlers.length === 0) {\n        handlers = route.byMethod.GET;\n      }\n\n      if (handlers.length > 0) {\n        result.methodMatch = true;\n        result.handlers.push(...handlers);\n\n        // Decode matched params\n        for (const [key, value] of Object.entries(match.pathname.groups)) {\n          result.params[key] = value === undefined ? \"\" : decodeURI(value);\n        }\n      }\n\n      break;\n    }\n\n    return result;\n  }\n}\n\n/**\n * Transform a filesystem URL path to a `path-to-regex` style matcher.\n */\nexport function pathToPattern(\n  path: string,\n  options?: { keepGroups: boolean },\n): string {\n  const parts = path.split(\"/\");\n  if (parts[parts.length - 1] === \"index\") {\n    if (parts.length === 1) {\n      return \"/\";\n    }\n    parts.pop();\n  }\n\n  let route = \"\";\n\n  for (let i = 0; i < parts.length; i++) {\n    const part = parts[i];\n\n    // Case: /[...foo].tsx\n    if (part.startsWith(\"[...\") && part.endsWith(\"]\")) {\n      route += `/:${part.slice(4, part.length - 1)}*`;\n      continue;\n    }\n\n    // Route groups like /foo/(bar) should not be included in URL\n    // matching. They are transparent and need to be removed here.\n    // Case: /foo/(bar) -> /foo\n    // Case: /foo/(bar)/bob -> /foo/bob\n    // Case: /(foo)/bar -> /bar\n    if (!options?.keepGroups && part.startsWith(\"(\") && part.endsWith(\")\")) {\n      continue;\n    }\n\n    // Disallow neighbouring params like `/[id][bar].tsx` because\n    // it's ambiguous where the `id` param ends and `bar` begins.\n    if (part.includes(\"][\")) {\n      throw new SyntaxError(\n        `Invalid route pattern: \"${path}\". A parameter cannot be followed by another parameter without any characters in between.`,\n      );\n    }\n\n    // Case: /[[id]].tsx\n    // Case: /[id].tsx\n    // Case: /[id]@[bar].tsx\n    // Case: /[id]-asdf.tsx\n    // Case: /[id]-asdf[bar].tsx\n    // Case: /asdf[bar].tsx\n    let pattern = \"\";\n    let groupOpen = 0;\n    let optional = false;\n    for (let j = 0; j < part.length; j++) {\n      const char = part[j];\n      if (char === \"[\") {\n        if (part[j + 1] === \"[\") {\n          // Disallow optional dynamic params like `foo-[[bar]]`\n          if (part[j - 1] !== \"/\" && !!part[j - 1]) {\n            throw new SyntaxError(\n              `Invalid route pattern: \"${path}\". An optional parameter needs to be a full segment.`,\n            );\n          }\n          groupOpen++;\n          optional = true;\n          pattern += \"{/\";\n          j++;\n        }\n        pattern += \":\";\n        groupOpen++;\n      } else if (char === \"]\") {\n        if (part[j + 1] === \"]\") {\n          // Disallow optional dynamic params like `[[foo]]-bar`\n          if (part[j + 2] !== \"/\" && !!part[j + 2]) {\n            throw new SyntaxError(\n              `Invalid route pattern: \"${path}\". An optional parameter needs to be a full segment.`,\n            );\n          }\n          groupOpen--;\n          pattern += \"}?\";\n          j++;\n        }\n        if (--groupOpen < 0) {\n          throw new SyntaxError(`Invalid route pattern: \"${path}\"`);\n        }\n      } else {\n        pattern += char;\n      }\n    }\n\n    route += (optional ? \"\" : \"/\") + pattern;\n  }\n\n  // Case: /(group)/index.tsx\n  if (route === \"\") {\n    route = \"/\";\n  }\n\n  return route;\n}\n\nexport function patternToSegments(\n  path: string,\n  root: string,\n  includeLast: boolean = false,\n): string[] {\n  const out: string[] = [root];\n\n  if (path === \"/\" || path === \"*\" || path === \"/*\") return out;\n\n  let start = -1;\n  for (let i = 0; i < path.length; i++) {\n    const ch = path[i];\n\n    if (ch === \"/\") {\n      if (i > 0) {\n        const raw = path.slice(start + 1, i);\n        out.push(raw);\n      }\n      start = i;\n    }\n  }\n\n  if (includeLast && start < path.length - 1) {\n    out.push(path.slice(start + 1));\n  }\n\n  return out;\n}\n\nexport function mergePath(\n  basePath: string,\n  path: string,\n  isMounting: boolean,\n): string {\n  if (basePath.endsWith(\"*\")) basePath = basePath.slice(0, -1);\n  if (basePath === \"/\") basePath = \"\";\n\n  if (path === \"*\") path = isMounting ? \"\" : \"/*\";\n  else if (path === \"/*\") path = \"/*\";\n\n  const s = (basePath !== \"\" && path === \"/\") ? \"\" : path;\n  return basePath + s;\n}\n\nexport function toRoutePath(path: string): string {\n  if (path === \"\") return \"*\";\n  return path;\n}\n"
  },
  {
    "path": "packages/fresh/src/router_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport {\n  IS_PATTERN,\n  mergePath,\n  pathToPattern,\n  patternToSegments,\n  UrlPatternRouter,\n} from \"./router.ts\";\n\nDeno.test(\"IS_PATTERN\", () => {\n  expect(IS_PATTERN.test(\"/foo\")).toEqual(false);\n  expect(IS_PATTERN.test(\"/foo/bar/baz.jpg\")).toEqual(false);\n  expect(IS_PATTERN.test(\"/foo/:path\")).toEqual(true);\n  expect(IS_PATTERN.test(\"/foo/*\")).toEqual(true);\n  expect(IS_PATTERN.test(\"/foo{/bar}?\")).toEqual(true);\n  expect(IS_PATTERN.test(\"/foo/(\\\\d+)\")).toEqual(true);\n  expect(IS_PATTERN.test(\"/foo/(a)\")).toEqual(true);\n});\n\nDeno.test(\"UrlPatternRouter - GET extract params\", () => {\n  const router = new UrlPatternRouter();\n  const A = () => {};\n  router.add(\"GET\", \"/:foo/:bar/c\", [A]);\n\n  let res = router.match(\"GET\", new URL(\"/a/b/c\", \"http://localhost\"));\n  expect(res).toEqual({\n    params: { foo: \"a\", bar: \"b\" },\n    handlers: [A],\n    methodMatch: true,\n    pattern: \"/:foo/:bar/c\",\n  });\n\n  // Decode params\n  res = router.match(\"GET\", new URL(\"/a%20a/b/c\", \"http://localhost\"));\n  expect(res).toEqual({\n    params: { foo: \"a a\", bar: \"b\" },\n    handlers: [A],\n    methodMatch: true,\n    pattern: \"/:foo/:bar/c\",\n  });\n});\n\nDeno.test(\"UrlPatternRouter - Wrong method match\", () => {\n  const router = new UrlPatternRouter();\n  const A = () => {};\n  router.add(\"GET\", \"/foo\", [A]);\n\n  const res = router.match(\"POST\", new URL(\"/foo\", \"http://localhost\"));\n  expect(res).toEqual({\n    params: Object.create(null),\n    handlers: [],\n    methodMatch: false,\n    pattern: \"/foo\",\n  });\n});\n\nDeno.test(\"UrlPatternRouter - wrong + correct method\", () => {\n  const router = new UrlPatternRouter();\n  const A = () => {};\n  const B = () => {};\n  router.add(\"GET\", \"/foo\", [A]);\n  router.add(\"POST\", \"/foo\", [B]);\n\n  const res = router.match(\"POST\", new URL(\"/foo\", \"http://localhost\"));\n  expect(res).toEqual({\n    params: Object.create(null),\n    handlers: [B],\n    methodMatch: true,\n    pattern: \"/foo\",\n  });\n});\n\nDeno.test(\"UrlPatternRouter - convert patterns automatically\", () => {\n  const router = new UrlPatternRouter();\n  const A = () => {};\n  router.add(\"GET\", \"/books/:id\", [A]);\n\n  const res = router.match(\"GET\", new URL(\"/books/foo\", \"http://localhost\"));\n  expect(res).toEqual({\n    params: {\n      id: \"foo\",\n    },\n    handlers: [A],\n    methodMatch: true,\n    pattern: \"/books/:id\",\n  });\n});\n\nDeno.test(\"pathToPattern\", async (t) => {\n  await t.step(\"creates pattern\", () => {\n    expect(pathToPattern(\"foo/bar\")).toEqual(\"/foo/bar\");\n  });\n\n  await t.step(\"parses index routes\", () => {\n    expect(pathToPattern(\"foo/index\")).toEqual(\"/foo\");\n  });\n\n  await t.step(\"parses parameters\", () => {\n    expect(pathToPattern(\"foo/[name]\")).toEqual(\"/foo/:name\");\n    expect(pathToPattern(\"foo/[name]/bar/[bob]\")).toEqual(\n      \"/foo/:name/bar/:bob\",\n    );\n  });\n\n  await t.step(\"parses catchall\", () => {\n    expect(pathToPattern(\"foo/[...name]\")).toEqual(\"/foo/:name*\");\n  });\n\n  await t.step(\"parses multiple params in same part\", () => {\n    expect(pathToPattern(\"foo/[mod]@[version]\")).toEqual(\"/foo/:mod@:version\");\n    expect(pathToPattern(\"foo/[bar].json\")).toEqual(\"/foo/:bar.json\");\n    expect(pathToPattern(\"foo/foo[bar]\")).toEqual(\"/foo/foo:bar\");\n  });\n\n  await t.step(\"parses optional params\", () => {\n    expect(pathToPattern(\"foo/[[name]]\")).toEqual(\"/foo{/:name}?\");\n    expect(pathToPattern(\"foo/[name]/[[bob]]\")).toEqual(\"/foo/:name{/:bob}?\");\n    expect(pathToPattern(\"foo/[[name]]/bar\")).toEqual(\"/foo{/:name}?/bar\");\n    expect(\n      pathToPattern(\"foo/[[name]]/bar/[[bob]]\"),\n    ).toEqual(\n      \"/foo{/:name}?/bar{/:bob}?\",\n    );\n  });\n\n  await t.step(\"throws on invalid patterns\", () => {\n    expect(() => pathToPattern(\"foo/[foo][bar]\")).toThrow();\n    expect(() => pathToPattern(\"foo/foo]\")).toThrow();\n    expect(() => pathToPattern(\"foo/[foo]]\")).toThrow();\n    expect(() => pathToPattern(\"foo/foo-[[name]]-bar/baz\")).toThrow();\n    expect(() => pathToPattern(\"foo/[[name]]-bar/baz\")).toThrow();\n    expect(() => pathToPattern(\"foo/foo-[[name]]/baz\")).toThrow();\n    expect(() => pathToPattern(\"foo/foo-[[name]]\")).toThrow();\n    expect(() => pathToPattern(\"foo/[[name]]-bar\")).toThrow();\n  });\n\n  await t.step(\"keep groups\", () => {\n    expect(pathToPattern(\"foo/(foo)/bar\", { keepGroups: true })).toEqual(\n      \"/foo/(foo)/bar\",\n    );\n  });\n});\n\nDeno.test(\"patternToSegments\", () => {\n  expect(patternToSegments(\"/\", \"\")).toEqual([\"\"]);\n  expect(patternToSegments(\"/foo\", \"\")).toEqual([\"\"]);\n  expect(patternToSegments(\"/foo/bar\", \"\")).toEqual([\"\", \"foo\"]);\n\n  expect(patternToSegments(\"/:foo\", \"\")).toEqual([\"\"]);\n  expect(patternToSegments(\"/:foo/:bar\", \"\")).toEqual([\"\", \":foo\"]);\n  expect(patternToSegments(\"/:foo-:bar/foo\", \"\")).toEqual([\"\", \":foo-:bar\"]);\n  expect(patternToSegments(\"/foo/\", \"\")).toEqual([\"\", \"foo\"]);\n\n  expect(patternToSegments(\"/foo/bar\", \"\", true)).toEqual([\"\", \"foo\", \"bar\"]);\n});\n\nDeno.test(\"mergePath\", () => {\n  expect(mergePath(\"\", \"/foo\", false)).toEqual(\"/foo\");\n  expect(mergePath(\"/\", \"/foo\", false)).toEqual(\"/foo\");\n  expect(mergePath(\"/foo/bar\", \"/\", false)).toEqual(\"/foo/bar\");\n  expect(mergePath(\"/foo/bar\", \"/baz\", false)).toEqual(\"/foo/bar/baz\");\n  expect(mergePath(\"*\", \"/baz\", false)).toEqual(\"/baz\");\n  expect(mergePath(\"/*\", \"/baz\", false)).toEqual(\"/baz\");\n  expect(mergePath(\"/foo\", \"*\", false)).toEqual(\"/foo/*\");\n  expect(mergePath(\"/foo\", \"/*\", false)).toEqual(\"/foo/*\");\n\n  // mounting\n  expect(mergePath(\"\", \"/foo\", true)).toEqual(\"/foo\");\n  expect(mergePath(\"/\", \"/foo\", true)).toEqual(\"/foo\");\n  expect(mergePath(\"/foo/bar\", \"/\", true)).toEqual(\"/foo/bar\");\n  expect(mergePath(\"/foo/bar\", \"/baz\", true)).toEqual(\"/foo/bar/baz\");\n  expect(mergePath(\"*\", \"/baz\", true)).toEqual(\"/baz\");\n  expect(mergePath(\"/*\", \"/baz\", true)).toEqual(\"/baz\");\n  expect(mergePath(\"/foo\", \"*\", true)).toEqual(\"/foo\");\n  expect(mergePath(\"/foo\", \"/*\", true)).toEqual(\"/foo/*\");\n});\n"
  },
  {
    "path": "packages/fresh/src/runtime/client/dev.ts",
    "content": "import \"preact/debug\";\nexport * from \"./mod.ts\";\nimport { IS_BROWSER } from \"../shared.ts\";\n\nlet ws: WebSocket;\nlet revision = 0;\n\nlet reconnectTimer: number;\nconst backoff = [\n  // Wait 100ms initially, because we could also be\n  // disconnected because of a form submit.\n  100,\n  150,\n  200,\n  250,\n  300,\n  350,\n  400,\n  450,\n  500,\n  500,\n  605,\n  750,\n  1000,\n  1250,\n  1500,\n  1750,\n  2000,\n];\nlet backoffIdx = 0;\nfunction reconnect() {\n  if (ws.readyState !== ws.CLOSED) return;\n\n  reconnectTimer = setTimeout(() => {\n    if (backoffIdx === 0) {\n      // deno-lint-ignore no-console\n      console.log(\n        `%c Fresh %c Connection closed. Trying to reconnect...`,\n        \"background-color: #86efac; color: black\",\n        \"color: inherit\",\n      );\n    }\n    backoffIdx++;\n\n    try {\n      connect();\n      clearTimeout(reconnectTimer);\n    } catch (_err) {\n      reconnect();\n    }\n  }, backoff[Math.min(backoffIdx, backoff.length - 1)]);\n}\n\nfunction onOpenWs() {\n  backoffIdx = 0;\n}\n\nfunction onCloseWs() {\n  disconnect();\n  reconnect();\n}\n\nfunction connect() {\n  const url = new URL(\"/_frsh/alive\", location.origin.replace(\"http\", \"ws\"));\n  ws = new WebSocket(\n    url,\n  );\n\n  ws.addEventListener(\"open\", onOpenWs);\n  ws.addEventListener(\"close\", onCloseWs);\n  ws.addEventListener(\"message\", handleMessage);\n  ws.addEventListener(\"error\", handleError);\n}\n\nfunction disconnect() {\n  ws.removeEventListener(\"open\", onOpenWs);\n  ws.removeEventListener(\"close\", onCloseWs);\n  ws.removeEventListener(\"message\", handleMessage);\n  ws.removeEventListener(\"error\", handleError);\n  ws.close();\n}\n\nfunction handleMessage(e: MessageEvent) {\n  const data = JSON.parse(e.data);\n  switch (data.type) {\n    case \"initial-state\": {\n      if (revision === 0) {\n        // deno-lint-ignore no-console\n        console.log(\n          `%c Fresh %c Connected to development server.`,\n          \"background-color: #86efac; color: black\",\n          \"color: inherit\",\n        );\n      }\n\n      if (revision === 0) {\n        revision = data.revision;\n      } else if (revision < data.revision) {\n        disconnect();\n        // Needs reload\n        location.reload();\n      }\n    }\n  }\n}\n\nfunction handleError(e: Event) {\n  // TODO\n  // deno-lint-ignore no-explicit-any\n  if (e && (e as any).code === \"ECONNREFUSED\") {\n    setTimeout(connect, 1000);\n  }\n}\n\nif (IS_BROWSER) {\n  connect();\n\n  addEventListener(\"message\", (ev) => {\n    if (ev.origin !== location.origin) return;\n    if (typeof ev.data !== \"string\" || ev.data !== \"close-error-overlay\") {\n      return;\n    }\n\n    document.querySelector(\"#fresh-error-overlay\")?.remove();\n  });\n\n  // Disconnect when the tab becomes inactive and re-connect when it\n  // becomes active again\n  addEventListener(\"visibilitychange\", () => {\n    if (document.hidden) {\n      disconnect();\n    } else {\n      connect();\n    }\n  });\n}\n"
  },
  {
    "path": "packages/fresh/src/runtime/client/mod.ts",
    "content": "import \"./polyfills.ts\";\nimport \"./preact_hooks_client.ts\";\nimport \"./partials.ts\";\nexport { asset, IS_BROWSER, Partial, type PartialProps } from \"../shared.ts\";\nexport { boot, revive } from \"./reviver.ts\";\n"
  },
  {
    "path": "packages/fresh/src/runtime/client/partials.ts",
    "content": "import { type ComponentChildren, h } from \"preact\";\nimport {\n  CLIENT_NAV_ATTR,\n  DATA_ANCESTOR,\n  DATA_CURRENT,\n  matchesUrl,\n  PartialMode,\n  UrlMatchKind,\n} from \"../shared_internal.ts\";\nimport {\n  ACTIVE_PARTIALS,\n  copyOldChildren,\n  CUSTOM_PARSER,\n  type DeserializedProps,\n  domToVNode,\n  ISLAND_REGISTRY,\n  Marker,\n  maybeHideMarker,\n  PartialComp,\n} from \"./reviver.ts\";\nimport { createRootFragment, isCommentNode, isElementNode } from \"./reviver.ts\";\nimport type { PartialStateJson } from \"../server/preact_hooks.ts\";\nimport { parse } from \"../../jsonify/parse.ts\";\nimport { INTERNAL_PREFIX, PARTIAL_SEARCH_PARAM } from \"../../constants.ts\";\n\nexport const PARTIAL_ATTR = \"f-partial\";\n\nclass NoPartialsError extends Error {}\n\n// Fresh partials history updates set the fClientNav flag\n// and prevent reloads in the popstate handler when\n// user-code triggers history navigation events.\nexport interface FreshHistoryState {\n  fClientNav: boolean;\n  index: number;\n  scrollX: number;\n  scrollY: number;\n}\n\nfunction checkClientNavEnabled(el: HTMLElement) {\n  const setting = el.closest(`[${CLIENT_NAV_ATTR}]`);\n  if (setting === null) return false;\n  return setting.getAttribute(CLIENT_NAV_ATTR) !== \"false\";\n}\n\n// Keep track of history state to apply forward or backward animations\nlet index = history.state?.index || 0;\nif (!history.state) {\n  const state: FreshHistoryState = {\n    fClientNav: true,\n    index,\n    scrollX,\n    scrollY,\n  };\n  history.replaceState(state, document.title);\n}\n\nfunction maybeUpdateHistory(nextUrl: URL) {\n  // Only add history entry when URL is new. Still apply\n  // the partials because sometimes users click a link to\n  // \"refresh\" the current page.\n  if (nextUrl.href !== globalThis.location.href) {\n    const state: FreshHistoryState = {\n      fClientNav: true,\n      index,\n      scrollX: globalThis.scrollX,\n      scrollY: globalThis.scrollY,\n    };\n\n    // Store current scroll position\n    history.replaceState({ ...state }, \"\", location.href);\n\n    // Now store the new position\n    index++;\n    state.scrollX = 0;\n    state.scrollY = 0;\n    history.pushState(state, \"\", nextUrl.href);\n  }\n}\n\ndocument.addEventListener(\"click\", async (e) => {\n  let el = e.target;\n  if (el && (el instanceof HTMLElement || el instanceof SVGElement)) {\n    const originalEl = el;\n\n    // Check if we clicked inside an anchor link\n    if (el.nodeName !== \"A\") {\n      el = el.closest(\"a\");\n    }\n    if (el === null) {\n      el = originalEl.closest(\"button\");\n    }\n\n    if (\n      // Check that we're still dealing with an anchor tag\n      el && el instanceof HTMLAnchorElement &&\n      // Check if it's an internal link\n      el.href && (!el.target || el.target === \"_self\") &&\n      el.origin === location.origin &&\n      // Check if it was a left click and not a right click\n      e.button === 0 &&\n      // Check that the user doesn't press a key combo to open the\n      // link in a new tab or something\n      !(e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || e.button) &&\n      // Check that the event isn't aborted already\n      !e.defaultPrevented\n    ) {\n      const partial = el.getAttribute(PARTIAL_ATTR);\n\n      // Check if the user opted out of client side navigation or if\n      // we're doing a fragment navigation.\n      if (\n        el.getAttribute(\"href\")?.startsWith(\"#\") ||\n        !checkClientNavEnabled(el)\n      ) {\n        return;\n      }\n\n      // deno-lint-ignore no-explicit-any\n      const indicator = (el as any)._freshIndicator;\n      if (indicator !== undefined) {\n        indicator.value = true;\n      }\n\n      e.preventDefault();\n\n      const nextUrl = new URL(el.href);\n      try {\n        maybeUpdateHistory(nextUrl);\n\n        const partialUrl = new URL(\n          partial ? partial : nextUrl.href,\n          location.href,\n        );\n        await fetchPartials(nextUrl, partialUrl, true);\n        updateLinks(nextUrl);\n        scrollTo({ left: 0, top: 0, behavior: \"instant\" });\n      } finally {\n        if (indicator !== undefined) {\n          indicator.value = false;\n        }\n      }\n    } else if (\n      el && el instanceof HTMLButtonElement &&\n      (el.type !== \"submit\" || el.form === null)\n    ) {\n      const partial = el.getAttribute(PARTIAL_ATTR);\n\n      // Check if the user opted out of client side navigation.\n      if (partial === null || !checkClientNavEnabled(el)) {\n        return;\n      }\n\n      const partialUrl = new URL(\n        partial,\n        location.href,\n      );\n      await fetchPartials(partialUrl, partialUrl, false);\n    }\n  }\n});\n\naddEventListener(\"popstate\", async (e) => {\n  // When state is `null` then the browser navigated to a document\n  // fragment. In this case we do nothing.\n  if (e.state === null) {\n    // Reset to browser default\n    if (history.scrollRestoration) {\n      history.scrollRestoration = \"auto\";\n    }\n    return;\n  }\n  // Do nothing if Fresh navigation is not explicitly opted-in.\n  // Other applications might manage scrollRestoration individually.\n  if (!e.state.fClientNav) return;\n\n  const state: FreshHistoryState = history.state;\n  const nextIdx = state.index ?? index + 1;\n  index = nextIdx;\n\n  const setting = document.querySelector(`[${CLIENT_NAV_ATTR}]`);\n  if (setting === null || setting.getAttribute(CLIENT_NAV_ATTR) === \"false\") {\n    location.reload();\n    return;\n  }\n\n  // We need to keep track of that ourselves since we do client side\n  // navigation.\n  if (history.scrollRestoration) {\n    history.scrollRestoration = \"manual\";\n  }\n\n  const url = new URL(location.href, location.origin);\n  try {\n    await fetchPartials(url, url, true);\n    updateLinks(url);\n    scrollTo({\n      left: state.scrollX ?? 0,\n      top: state.scrollY ?? 0,\n      behavior: \"instant\",\n    });\n  } catch (err) {\n    // If the response didn't contain a partial, then we can only\n    // do a reload.\n    if (err instanceof NoPartialsError) {\n      location.reload();\n      return;\n    }\n\n    throw err;\n  }\n});\n\n// Form submit\ndocument.addEventListener(\"submit\", async (e) => {\n  const el = e.target;\n  if (el !== null && el instanceof HTMLFormElement && !e.defaultPrevented) {\n    if (\n      // Check if form has client nav enabled\n      !checkClientNavEnabled(el) ||\n      // Bail out if submitter is set and client nav is disabled\n      (e.submitter !== null && !checkClientNavEnabled(e.submitter))\n    ) {\n      return;\n    }\n\n    const lowerMethod =\n      e.submitter?.getAttribute(\"formmethod\")?.toLowerCase() ??\n        el.method.toLowerCase();\n    if (lowerMethod !== \"get\" && lowerMethod !== \"post\") {\n      return;\n    }\n\n    const rawPartialUrl = e.submitter?.getAttribute(PARTIAL_ATTR) ??\n      e.submitter?.getAttribute(\"formaction\") ??\n      el.getAttribute(PARTIAL_ATTR) ?? el.action;\n    const rawActionUrl = e.submitter?.getAttribute(\"formaction\") ?? el.action;\n\n    if (rawPartialUrl !== \"\") {\n      e.preventDefault();\n\n      const partialUrl = new URL(rawPartialUrl, location.href);\n      const actionUrl = new URL(rawActionUrl, location.href);\n\n      let init: RequestInit | undefined;\n\n      // GET method appends form data via url search params\n      if (lowerMethod === \"get\") {\n        // TODO: Looks like constructor type for URLSearchParam is wrong\n        // deno-lint-ignore no-explicit-any\n        const qs = new URLSearchParams(new FormData(el, e.submitter) as any);\n        qs.forEach((value, key) => partialUrl.searchParams.append(key, value));\n      } else {\n        init = { body: new FormData(el, e.submitter), method: lowerMethod };\n      }\n\n      await fetchPartials(actionUrl, partialUrl, true, init);\n    }\n  }\n});\n\nfunction updateLinks(url: URL) {\n  document.querySelectorAll(\"a\").forEach((link) => {\n    const match = matchesUrl(url.pathname, link.href);\n\n    if (match === UrlMatchKind.Current) {\n      link.setAttribute(DATA_CURRENT, \"true\");\n      link.setAttribute(\"aria-current\", \"page\");\n      link.removeAttribute(DATA_ANCESTOR);\n    } else if (match === UrlMatchKind.Ancestor) {\n      link.setAttribute(DATA_ANCESTOR, \"true\");\n      link.setAttribute(\"aria-current\", \"true\");\n      link.removeAttribute(DATA_CURRENT);\n    } else {\n      link.removeAttribute(DATA_CURRENT);\n      link.removeAttribute(DATA_ANCESTOR);\n      link.removeAttribute(\"aria-current\");\n    }\n  });\n}\n\nasync function fetchPartials(\n  actualUrl: URL,\n  partialUrl: URL,\n  shouldNavigate: boolean,\n  init: RequestInit = {},\n) {\n  init.redirect = \"follow\";\n  partialUrl = new URL(partialUrl);\n  partialUrl.searchParams.set(PARTIAL_SEARCH_PARAM, \"true\");\n  const res = await fetch(partialUrl, init);\n\n  if (res.redirected) {\n    const nextUrl = new URL(res.url);\n    if (nextUrl.origin === actualUrl.origin) {\n      actualUrl = nextUrl;\n    }\n  }\n\n  if (shouldNavigate) {\n    maybeUpdateHistory(actualUrl);\n  }\n\n  await applyPartials(res);\n}\n\ninterface PartialReviveCtx {\n  foundPartials: number;\n}\n\n/**\n * Apply partials from a HTML response\n */\nexport async function applyPartials(res: Response): Promise<void> {\n  const contentType = res.headers.get(\"Content-Type\");\n  if (contentType !== \"text/html; charset=utf-8\") {\n    throw new Error(`Unable to process partial response.`);\n  }\n\n  const id = res.headers.get(\"X-Fresh-Id\");\n\n  const resText = await res.text();\n  const doc = new DOMParser().parseFromString(resText, \"text/html\") as Document;\n\n  const state = doc.querySelector(`#__FRSH_STATE_${id}`);\n  let allProps: DeserializedProps = [];\n  if (state !== null) {\n    const json = JSON.parse(state.textContent!) as PartialStateJson;\n    const promises: Promise<void>[] = [];\n\n    allProps = parse<DeserializedProps>(json.props, CUSTOM_PARSER);\n\n    for (let i = 0; i < json.islands.length; i++) {\n      const island = json.islands[i];\n      promises.push(\n        import(/* @vite-ignore */ island.chunk).then((mod) => {\n          ISLAND_REGISTRY.set(island.name, mod[island.exportName]);\n        }),\n      );\n    }\n\n    await Promise.all(promises);\n  }\n\n  const ctx: PartialReviveCtx = {\n    foundPartials: 0,\n  };\n\n  if (doc.title) {\n    document.title = doc.title;\n  }\n\n  // Needs to be converted to an array otherwise somehow <link>-tags\n  // are missing.\n  Array.from(doc.head.childNodes).forEach((childNode) => {\n    const child = childNode as HTMLElement;\n\n    if (child.nodeName === \"TITLE\") return;\n    if (child.nodeName === \"META\") {\n      const meta = child as HTMLMetaElement;\n\n      // Ignore charset which is usually set site wide anyway\n      if (meta.hasAttribute(\"charset\")) return;\n\n      const name = meta.name;\n      if (name !== \"\") {\n        const existing = document.head.querySelector(`meta[name=\"${name}\"]`) as\n          | HTMLMetaElement\n          | null;\n        if (existing !== null) {\n          if (existing.content !== meta.content) {\n            existing.content = meta.content;\n          }\n        } else {\n          document.head.appendChild(meta);\n        }\n      } else {\n        const property = child.getAttribute(\"property\");\n        const existing = document.head.querySelector(\n          `meta[property=\"${property}\"]`,\n        ) as HTMLMetaElement | null;\n        if (existing !== null) {\n          if (existing.content !== meta.content) {\n            existing.content = meta.content;\n          }\n        } else {\n          document.head.appendChild(meta);\n        }\n      }\n    } else if (child.nodeName === \"LINK\") {\n      const link = child as HTMLLinkElement;\n      if (link.rel === \"modulepreload\") return;\n      if (link.rel === \"stylesheet\") {\n        // The `href` attribute may be root relative. This ensures\n        // that they both have the same format\n        const existing = Array.from(document.head.querySelectorAll(\"link\"))\n          .find((existingLink) => existingLink.href === link.href);\n        if (existing === undefined) {\n          document.head.appendChild(link);\n        }\n      }\n    } else if (child.nodeName === \"SCRIPT\") {\n      const script = child as HTMLScriptElement;\n      if (script.src === `${INTERNAL_PREFIX}/fresh-runtime.js`) return;\n      // TODO: What to do with script tags?\n    } else if (child.nodeName === \"STYLE\") {\n      const style = child as HTMLStyleElement;\n      // TODO: Do we need a smarter merging strategy?\n      // Don't overwrie existing style sheets that are flagged as unique\n      if (style.id === \"\") {\n        document.head.appendChild(style);\n      }\n    }\n  });\n\n  revivePartials(ctx, allProps, doc.body);\n\n  if (ctx.foundPartials === 0) {\n    throw new NoPartialsError(\n      `Found no partials in HTML response. Please make sure to render at least one partial. Requested url: ${res.url}`,\n    );\n  }\n}\n\nfunction revivePartials(\n  ctx: PartialReviveCtx,\n  allProps: DeserializedProps,\n  node: Element,\n) {\n  let startNode = null;\n  let sib: ChildNode | null = node.firstChild;\n  let partialCount = 0;\n  let partialName = \"\";\n  let partialKey = \"\";\n  let partialMode = PartialMode.Replace;\n  while (sib !== null) {\n    if (isCommentNode(sib)) {\n      const comment = sib.data;\n      const parts = comment.split(\":\");\n      if (parts[0] === \"frsh\") {\n        sib = maybeHideMarker(sib);\n      }\n\n      if (parts[0] === \"frsh\" && parts[1] === \"partial\") {\n        if (++partialCount === 1) {\n          startNode = sib;\n          partialName = parts[2];\n          partialMode = +parts[3] as PartialMode;\n          partialKey = parts[4];\n        }\n      } else if (comment === \"/frsh:partial\") {\n        ctx.foundPartials++;\n\n        // Skip hydrating nested partials, only hydrate the outer one\n        if (--partialCount > 0) {\n          sib = sib.nextSibling;\n          continue;\n        }\n\n        // Create a fake DOM node that spans the partial we discovered.\n        // We need to include the partial markers itself for _walkInner\n        // to register them.\n        const container = createRootFragment(\n          node,\n          startNode as Comment,\n          sib as Comment,\n        );\n\n        const root = h(PartialComp, {\n          key: partialKey !== \"\" ? partialKey : undefined,\n          name: partialName,\n          mode: partialMode,\n          children: null,\n        });\n        domToVNode(\n          allProps,\n          [root],\n          [Marker.Partial],\n          container,\n          sib as Comment,\n        );\n\n        const instance = ACTIVE_PARTIALS.get(partialName);\n        if (instance === undefined) {\n          // deno-lint-ignore no-console\n          console.warn(`Partial \"${partialName}\" not found. Skipping...`);\n          // Partial doesn't exist on the current page\n        } else {\n          if (partialMode === PartialMode.Replace) {\n            instance.props.children = root.props.children;\n          } else if (partialMode === PartialMode.Append) {\n            const active = ACTIVE_PARTIALS.get(partialName);\n            if (active !== undefined) {\n              copyOldChildren(instance.props, active.props.children);\n\n              (instance.props.children as ComponentChildren[]).push(\n                root.props.children,\n              );\n            } else {\n              instance.props.children = root.props.children;\n            }\n          } else if (partialMode === PartialMode.Prepend) {\n            const active = ACTIVE_PARTIALS.get(partialName);\n            if (active !== undefined) {\n              copyOldChildren(instance.props, active.props.children);\n\n              (instance.props.children as ComponentChildren[]).unshift(\n                root.props.children,\n              );\n            } else {\n              instance.props.children = root.props.children;\n            }\n          }\n          instance.setState({});\n        }\n      }\n    } else if (partialCount === 0 && isElementNode(sib)) {\n      // Do not recurse if we know that we are inisde a partial\n      revivePartials(ctx, allProps, sib);\n    }\n\n    sib = sib.nextSibling;\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/runtime/client/polyfills.ts",
    "content": "// deno-lint-ignore-file ban-unknown-rule-code ban-unused-ignore\n// Polyfill for old safari versions\nif (typeof globalThis === \"undefined\") {\n  // @ts-ignore polyfill\n  // deno-lint-ignore no-window\n  window.globalThis = window;\n}\n"
  },
  {
    "path": "packages/fresh/src/runtime/client/preact_hooks_client.ts",
    "content": "import { Fragment, h, options as preactOptions } from \"preact\";\nimport {\n  assetHashingHook,\n  CLIENT_NAV_ATTR,\n  type InternalPreactOptions,\n  OptionsType,\n} from \"../shared_internal.ts\";\nimport { BUILD_ID } from \"@fresh/build-id\";\nimport { renderToString } from \"preact-render-to-string\";\nimport { useEffect } from \"preact/hooks\";\n\n// deno-lint-ignore no-explicit-any\nconst options: InternalPreactOptions = preactOptions as any;\n\nconst oldVNodeHook = options.vnode;\noptions.vnode = (vnode) => {\n  assetHashingHook(vnode, BUILD_ID);\n\n  if (typeof vnode.type === \"string\") {\n    if (CLIENT_NAV_ATTR in vnode.props) {\n      const value = vnode.props[CLIENT_NAV_ATTR];\n      if (typeof value === \"boolean\") {\n        vnode.props[CLIENT_NAV_ATTR] = String(value);\n      }\n    }\n  }\n\n  const originalType = vnode.type;\n\n  if (typeof originalType === \"string\") {\n    switch (originalType) {\n      case \"title\":\n      case \"meta\":\n      case \"link\":\n      case \"script\":\n      case \"style\":\n      case \"base\":\n      case \"noscript\":\n      case \"template\":\n        // deno-lint-ignore no-constant-condition\n        if (false) {\n          // deno-lint-ignore no-explicit-any\n          vnode.type = (props: any) => {\n            useEffect(() => {\n              const text = renderToString(h(Fragment, null, props.children));\n\n              if (originalType === \"title\") {\n                document.title = text;\n                return;\n              }\n\n              let matched: HTMLElement | null = null;\n              if (vnode.key) {\n                matched = document.head.querySelector(\n                  `head [data-key=\"${vnode.key}\"]`,\n                ) as HTMLElement ?? null;\n              }\n\n              if (matched === null && props.id) {\n                matched = document.head.querySelector(\n                  `#${props.name}`,\n                ) as HTMLElement ??\n                  null;\n              }\n\n              if (matched === null) {\n                if (originalType === \"meta\") {\n                  matched = document.head.querySelector(\n                    `head [name=\"${props.name}\"]`,\n                  ) as HTMLElement ?? null;\n                } else if (originalType === \"base\") {\n                  matched = document.head.querySelector(originalType) ?? null;\n                }\n              }\n\n              if (matched === null) {\n                matched = document.createElement(originalType as string);\n              }\n\n              if (matched.textContent !== text) {\n                matched.textContent = text;\n              }\n\n              applyProps(props, matched);\n            }, []);\n\n            return null;\n          };\n        }\n        break;\n    }\n  }\n\n  oldVNodeHook?.(vnode);\n};\n\nconst oldDiff = options[OptionsType.DIFF];\noptions[OptionsType.DIFF] = (vnode) => {\n  oldDiff?.(vnode);\n};\n\n// deno-lint-ignore no-explicit-any\nfunction applyProps(props: Record<string, any>, el: HTMLElement) {\n  const keys = Object.keys(props);\n  for (let i = 0; i < keys.length; i++) {\n    const key = keys[i];\n    const value = props[key];\n\n    if (key === \"children\") {\n      continue;\n    }\n\n    if (value === null || value === undefined || value === false) {\n      el.removeAttribute(key);\n    } else {\n      el.setAttribute(key, value);\n    }\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/runtime/client/reviver.ts",
    "content": "import {\n  Component,\n  type ComponentChildren,\n  type ComponentType,\n  Fragment,\n  h,\n  render,\n  type VNode,\n} from \"preact\";\nimport { type CustomParser, parse } from \"../../jsonify/parse.ts\";\nimport { computed, signal } from \"@preact/signals\";\nimport { DATA_FRESH_KEY, PartialMode } from \"../shared_internal.ts\";\n\nconst enum RootKind {\n  Island,\n  Partial,\n}\n\ninterface IslandReq {\n  kind: RootKind.Island;\n  name: string;\n  propsIdx: number;\n  key: string | null;\n  start: Comment | Text;\n  end: Comment | Text | null;\n}\ninterface PartialReq {\n  kind: RootKind.Partial;\n  name: string;\n  key: string | null;\n  start: Comment | Text;\n  end: Comment | Text | null;\n}\n\ninterface ReviveContext {\n  roots: Array<IslandReq | PartialReq>;\n  stack: Array<PartialReq | IslandReq>;\n  slots: Map<number, { name: string; start: Comment; end: Comment | null }>;\n  slotIdStack: number[];\n}\n\ninterface SlotRef {\n  kind: typeof SLOT_SYMBOL;\n  name: string;\n  id: number;\n}\nconst SLOT_SYMBOL = Symbol.for(\"_FRESH_SLOT\");\nfunction isSlotRef(x: unknown): x is SlotRef {\n  return x !== null && typeof x === \"object\" && \"kind\" in x &&\n    x.kind === SLOT_SYMBOL;\n}\n\nexport type DeserializedProps = {\n  props: Record<string, unknown>;\n  slots: SlotRef[];\n}[];\n\nexport const ACTIVE_PARTIALS = new Map<string, PartialComp>();\n\nexport class PartialComp extends Component<\n  { children?: ComponentChildren; mode: PartialMode; name: string }\n> {\n  override componentDidMount() {\n    ACTIVE_PARTIALS.set(this.props.name, this);\n  }\n\n  render() {\n    return this.props.children;\n  }\n}\nPartialComp.displayName = \"Partial\";\n\nexport function revive(\n  props: Record<string, unknown>,\n  component: ComponentType,\n  container: HTMLElement,\n  slots: ReviveContext[\"slots\"],\n  allProps: DeserializedProps,\n) {\n  const _render = () => {\n    for (const propName in props) {\n      const value = props[propName];\n      if (isSlotRef(value)) {\n        const marker = slots.get(value.id);\n        if (marker !== undefined) {\n          const root = h(Fragment, null);\n          const slotContainer = createRootFragment(\n            container,\n            marker.start,\n            marker.end!,\n          );\n          domToVNode(\n            allProps,\n            [root],\n            [Marker.Slot],\n            slotContainer,\n            marker.end!,\n          );\n          props[propName] = root;\n        } else {\n          const template = document.querySelector(\n            `#frsh-${value.id}-${value.name}`,\n          ) as HTMLTemplateElement | null;\n          if (template !== null) {\n            const root = h(Fragment, null);\n            domToVNode(allProps, [root], [Marker.Slot], template.content, null);\n            props[propName] = root;\n          }\n        }\n      }\n    }\n\n    // TODO: explore hydrate?\n    render(h(component, props), container as unknown as HTMLElement);\n  };\n\n  // deno-lint-ignore no-window\n  \"scheduler\" in window\n    // `scheduler.postTask` is async but that can easily\n    // fire in the background. We don't want waiting for\n    // the hydration of an island block us.\n    // @ts-ignore scheduler API is not in types yet\n    ? scheduler!.postTask(_render)\n    : setTimeout(_render, 0);\n}\n\nexport const ISLAND_REGISTRY = new Map<string, ComponentType>();\n\nexport const CUSTOM_PARSER: CustomParser = {\n  Signal: (value: unknown) => signal(value),\n  Computed: (value: unknown) => computed(() => value),\n  Slot: (value: { name: string; id: number }): SlotRef => {\n    return { kind: SLOT_SYMBOL, name: value.name, id: value.id };\n  },\n};\n\nexport function createReviveCtx(): ReviveContext {\n  return {\n    roots: [],\n    stack: [],\n    slots: new Map(),\n    slotIdStack: [],\n  };\n}\n\nexport function boot(\n  initialIslands: Record<string, ComponentType>,\n  islandProps: string,\n) {\n  const ctx = createReviveCtx();\n  _walkInner(ctx, document.body);\n\n  const keys = Object.keys(initialIslands);\n  for (let i = 0; i < keys.length; i++) {\n    const name = keys[i];\n    ISLAND_REGISTRY.set(name, initialIslands[name]);\n  }\n\n  const allProps = parse<DeserializedProps>(\n    islandProps,\n    CUSTOM_PARSER,\n  );\n\n  for (let i = 0; i < ctx.roots.length; i++) {\n    const root = ctx.roots[i];\n\n    const container = createRootFragment(\n      // deno-lint-ignore no-explicit-any\n      root.start.parentNode as any,\n      root.start,\n      root.end!,\n    );\n\n    if (root.kind === RootKind.Island) {\n      const props = allProps[root.propsIdx].props;\n      const component = ISLAND_REGISTRY.get(root.name)!;\n\n      revive(props, component, container, ctx.slots, allProps);\n    } else if (root.kind === RootKind.Partial) {\n      const props: Record<string, unknown> = {\n        name: root.name,\n        children: null,\n      };\n\n      const domRoot = h(Fragment, null);\n      domToVNode(allProps, [domRoot], [Marker.Partial], container, root.end!);\n      props.children = domRoot.props.children;\n\n      // deno-lint-ignore no-explicit-any\n      revive(props, PartialComp as any, container, ctx.slots, allProps);\n    }\n  }\n}\n\nconst SHOW_MARKERS = false;\n\ninterface FreshMarker extends Text {\n  _frshMarker: string;\n}\n\nexport function isFreshMarkerText(node: Node): node is FreshMarker {\n  return node.nodeType === Node.TEXT_NODE &&\n    // deno-lint-ignore no-explicit-any\n    typeof (node as any)._frshMarker === \"string\";\n}\n\n/**\n * Replace comment markers with empty text nodes to hide them\n * in DevTools. This is done to avoid user confusion.\n */\nexport function maybeHideMarker(marker: Comment): Comment | Text {\n  if (SHOW_MARKERS) return marker;\n  const text = new Text(\"\") as FreshMarker;\n  text._frshMarker = marker.data;\n  marker.parentNode!.insertBefore(text, marker);\n  marker.remove();\n  return text;\n}\n\nfunction _walkInner(\n  ctx: ReviveContext,\n  node: Node | Comment,\n) {\n  if (isElementNode(node)) {\n    // No need to traverse into <script> or <style> tags.\n    // No need to traverse deeper if we found a slotStart marker.\n    // We'll parse slots later when the island is revived.\n    if (\n      node.nodeName === \"SCRIPT\" || node.nodeName === \"STYLE\" ||\n      ctx.slotIdStack.length > 0\n    ) {\n      return;\n    }\n\n    for (let i = 0; i < node.childNodes.length; i++) {\n      const child = node.childNodes[i];\n      _walkInner(ctx, child);\n    }\n  } else if (isCommentNode(node)) {\n    const comment = node.data;\n    if (comment.startsWith(\"frsh:\")) {\n      node = maybeHideMarker(node);\n      const parts = comment.split(\":\");\n      const kind = parts[1];\n      if (kind === \"island\") {\n        const name = parts[2];\n        const propsIdx = parts[3];\n        const key = parts[4];\n        const found: IslandReq = {\n          kind: RootKind.Island,\n          name,\n          propsIdx: Number(propsIdx),\n          key: key === \"\" ? null : key,\n          start: node as Comment,\n          end: null,\n        };\n        if (ctx.stack.length === 0) {\n          ctx.roots.push(found);\n        }\n        ctx.stack.push(found);\n      } else if (kind === \"slot\") {\n        const id = +parts[2];\n        const slotName = parts[3];\n        ctx.slotIdStack.push(id);\n        ctx.slots.set(id, {\n          name: slotName,\n          start: node as Comment,\n          end: null,\n        });\n      } else if (kind === \"partial\") {\n        const name = parts[2];\n        const key = parts[3];\n        const found: PartialReq = {\n          kind: RootKind.Partial,\n          name,\n          key: key === \"\" ? null : key,\n          start: node as Comment,\n          end: null,\n        };\n        if (ctx.stack.length === 0) {\n          ctx.roots.push(found);\n        }\n        ctx.stack.push(found);\n      }\n    } else if (comment === \"/frsh:island\" || comment === \"/frsh:partial\") {\n      node = maybeHideMarker(node);\n      const item = ctx.stack.pop();\n      if (item !== undefined) {\n        item.end = node as Comment;\n      }\n    } else if (comment === \"/frsh:slot\") {\n      node = maybeHideMarker(node);\n      const item = ctx.slotIdStack.pop();\n      if (item !== undefined) {\n        ctx.slots.get(item)!.end = node as Comment;\n      }\n    }\n  }\n}\n\nexport const enum Marker {\n  Island,\n  Partial,\n  Slot,\n}\n\ninterface ServerSlotProps {\n  name: string;\n  id: number;\n  children?: ComponentChildren;\n}\n\n// deno-lint-ignore no-explicit-any\nfunction ServerSlot(props: ServerSlotProps): any {\n  return props.children;\n}\n\nexport function domToVNode(\n  allProps: DeserializedProps,\n  // deno-lint-ignore no-explicit-any\n  vnodeStack: VNode<any>[],\n  markerStack: Marker[],\n  node: Text | Comment | Node | DocumentFragment,\n  end: Text | Comment | null,\n): void {\n  let sib: Node | ChildNode | null = node;\n  while (sib !== null) {\n    if (sib === end) return;\n\n    // deno-lint-ignore no-explicit-any\n    if ((sib as any)._frshRootFrag || sib instanceof DocumentFragment) {\n      const firstChild = sib.firstChild;\n      if (firstChild !== null) {\n        domToVNode(allProps, vnodeStack, markerStack, firstChild, end);\n      }\n    } else if (isElementNode(sib)) {\n      const props: Record<string, unknown> = {};\n      for (let i = 0; i < sib.attributes.length; i++) {\n        const attr = sib.attributes[i];\n\n        if (attr.nodeName === DATA_FRESH_KEY) {\n          props.key = attr.nodeValue;\n          continue;\n        }\n\n        // Boolean attributes are always `true` when present.\n        // See: https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML\n        props[attr.nodeName] =\n          // deno-lint-ignore no-explicit-any\n          typeof (sib as any)[attr.nodeName] === \"boolean\"\n            ? true\n            : attr.nodeValue;\n      }\n\n      const vnode = h(sib.localName, props);\n      const marker = markerStack.at(-1);\n      const appendVNode = marker === Marker.Slot || marker === Marker.Partial;\n      if (appendVNode) {\n        addVNodeChild(vnodeStack.at(-1)!, vnode);\n        vnodeStack.push(vnode);\n      }\n\n      const firstChild = sib.firstChild;\n      if (firstChild !== null) {\n        domToVNode(allProps, vnodeStack, markerStack, firstChild, end);\n      }\n\n      if (appendVNode) {\n        vnodeStack.pop();\n      }\n    } else if (isCommentNode(sib) || isFreshMarkerText(sib)) {\n      const comment = isFreshMarkerText(sib) ? sib._frshMarker : sib.data;\n      if (comment.startsWith(\"frsh:\")) {\n        const parts = comment.split(\":\");\n\n        const kind = parts[1];\n        if (kind === \"island\") {\n          sib = maybeHideMarker(sib);\n          markerStack.push(Marker.Island);\n\n          const name = parts[2];\n          const propsIdx = parts[3];\n          const key = parts[4];\n          const island = ISLAND_REGISTRY.get(name);\n          if (island === undefined) {\n            throw new Error(`Encountered unknown island: ${name}`);\n          }\n\n          const props = allProps[+propsIdx].props;\n          props.key = key === \"\" ? undefined : key;\n\n          // deno-lint-ignore no-explicit-any\n          const islandVNode = h<any>(island, props);\n          addVNodeChild(vnodeStack.at(-1)!, islandVNode);\n          vnodeStack.push(islandVNode);\n        } else if (kind === \"slot\") {\n          sib = maybeHideMarker(sib);\n          markerStack.push(Marker.Slot);\n\n          const id = +parts[2];\n          const slotName = parts[3];\n          const key = parts[4];\n\n          const vnode = h(ServerSlot, {\n            key: key === \"\" ? undefined : key,\n            id,\n            name: slotName,\n            children: [],\n          });\n\n          const parentVNode = vnodeStack.at(-1)!;\n          parentVNode.props[slotName] = vnode;\n          vnodeStack.push(vnode);\n        } else if (kind === \"partial\") {\n          sib = maybeHideMarker(sib);\n          markerStack.push(Marker.Partial);\n\n          const name = parts[2];\n          const mode = +parts[3] as PartialMode;\n          const key = parts[4];\n          // deno-lint-ignore no-explicit-any\n          let vnode: VNode<any> = h(PartialComp, {\n            name,\n            key: key === \"\" ? undefined : key,\n            mode,\n          });\n          const parentVNode = vnodeStack.at(-1);\n          if (parentVNode !== undefined) {\n            addVNodeChild(parentVNode, vnode);\n          }\n\n          if (mode === PartialMode.Append) {\n            const active = ACTIVE_PARTIALS.get(name);\n            if (active !== undefined) {\n              copyOldChildren(vnode.props, active.props.children);\n              const wrapper = h(Fragment, null);\n              addVNodeChild(vnode, wrapper);\n              vnode = wrapper;\n            }\n          } else if (mode === PartialMode.Prepend) {\n            const active = ACTIVE_PARTIALS.get(name);\n            if (active !== undefined) {\n              copyOldChildren(vnode.props, active.props.children);\n              const wrapper = h(Fragment, null);\n              if (!vnode.props.children) {\n                vnode.props.children = [];\n              }\n              // deno-lint-ignore no-explicit-any\n              (vnode.props.children as Array<VNode<any> | string>).unshift(\n                wrapper,\n              );\n              vnode = wrapper;\n            }\n          }\n\n          vnodeStack.push(vnode);\n        } else if (kind === \"key\") {\n          if (!SHOW_MARKERS) {\n            const current = sib;\n            sib = sib.nextSibling;\n            current.remove();\n          }\n          const vnode = h(Fragment, { key: parts[2] });\n          addVNodeChild(vnodeStack.at(-1)!, vnode);\n          vnodeStack.push(vnode);\n          continue;\n        }\n      } else if (\n        comment === \"/frsh:island\" || comment === \"/frsh:slot\" ||\n        comment === \"/frsh:partial\"\n      ) {\n        vnodeStack.pop();\n        markerStack.pop();\n        sib = maybeHideMarker(sib);\n      } else if (comment === \"/frsh:key\") {\n        vnodeStack.pop();\n        sib = maybeHideMarker(sib);\n      }\n    } else if (isTextNode(sib)) {\n      const marker = markerStack.at(-1);\n      if (marker === Marker.Slot || marker === Marker.Partial) {\n        addVNodeChild(vnodeStack.at(-1)!, sib.data);\n      }\n    }\n\n    sib = sib.nextSibling;\n  }\n}\n\n// deno-lint-ignore no-explicit-any\nfunction addVNodeChild(parent: VNode<any>, child: VNode<any> | string) {\n  if (!parent.props.children) {\n    parent.props.children = [];\n  }\n  // deno-lint-ignore no-explicit-any\n  (parent.props.children as Array<VNode<any> | string>).push(child);\n}\n\nexport function copyOldChildren(\n  props: Record<string, unknown>,\n  oldChildren: ComponentChildren,\n) {\n  props.children = oldChildren !== undefined && oldChildren !== null\n    ? Array.isArray(oldChildren) ? oldChildren : [oldChildren]\n    : [];\n}\n\nexport function isCommentNode(node: Node): node is Comment {\n  return node.nodeType === Node.COMMENT_NODE;\n}\nexport function isTextNode(node: Node): node is Text {\n  return node.nodeType === Node.TEXT_NODE;\n}\nexport function isElementNode(node: Node): node is HTMLElement {\n  return node.nodeType === Node.ELEMENT_NODE && !(\"_frshRootFrag\" in node);\n}\n\nexport function createRootFragment(\n  parent: Element,\n  startMarker: Text | Comment,\n  // We need an end marker for islands because multiple\n  // islands can share the same parent node. Since\n  // islands are root-level render calls any calls to\n  // `.appendChild` would lead to a wrong result.\n  endMarker: Text | Comment,\n): HTMLElement & { _frshRootFrag: boolean } {\n  // @ts-ignore this is fine\n  const rootFrag = {\n    _frshRootFrag: true,\n    nodeType: 1,\n    parentNode: parent,\n    nextSibling: null,\n    get firstChild() {\n      const child = startMarker.nextSibling;\n      if (child === endMarker) return null;\n      return child;\n    },\n    get childNodes() {\n      const children: ChildNode[] = [];\n\n      let child = startMarker.nextSibling;\n      while (child !== null && child !== endMarker) {\n        children.push(child);\n        child = child.nextSibling;\n      }\n\n      return children;\n    },\n    insertBefore(node: Node, child: Node | null) {\n      parent.insertBefore(node, child ?? endMarker);\n    },\n    appendChild(child: Node) {\n      // We cannot blindly call `.append()` as that would add\n      // the new child to the very end of the parent node. This\n      // leads to ordering issues when the multiple islands\n      // share the same parent node.\n      parent.insertBefore(child, endMarker);\n    },\n    removeChild(child: Node) {\n      parent.removeChild(child);\n    },\n    contains(node: Node | null): boolean {\n      if (node === null) return false;\n\n      const children = rootFrag.childNodes;\n      for (let i = 0; i < children.length; i++) {\n        if (children[i].contains(node)) {\n          return true;\n        }\n      }\n      return false;\n    },\n  };\n\n  // deno-lint-ignore no-explicit-any\n  return rootFrag as any;\n}\n"
  },
  {
    "path": "packages/fresh/src/runtime/head.ts",
    "content": "import { type ComponentChildren, createContext, h } from \"preact\";\n\nexport const HeadContext = createContext(false);\n\nexport interface HeadProps {\n  children?: ComponentChildren;\n}\n\nexport function Head(props: HeadProps): ComponentChildren {\n  return h(HeadContext, { value: true }, props.children);\n}\n"
  },
  {
    "path": "packages/fresh/src/runtime/server/preact_hooks.ts",
    "content": "import {\n  type Component,\n  type ComponentChildren,\n  type ComponentType,\n  Fragment,\n  h,\n  isValidElement,\n  type Options as PreactOptions,\n  options as preactOptions,\n  type VNode,\n} from \"preact\";\nimport type { ReadonlySignal, Signal } from \"@preact/signals\";\nimport type { Stringifiers } from \"../../jsonify/stringify.ts\";\nimport type { PageProps } from \"../../render.ts\";\nimport { asset, Partial, type PartialProps } from \"../shared.ts\";\nimport { stringify } from \"../../jsonify/stringify.ts\";\nimport type { Island } from \"../../context.ts\";\nimport {\n  assetHashingHook,\n  CLIENT_NAV_ATTR,\n  DATA_FRESH_KEY,\n  OptionsType,\n  PartialMode,\n  setActiveUrl,\n} from \"../shared_internal.ts\";\nimport type { BuildCache } from \"../../build_cache.ts\";\nimport { BUILD_ID } from \"@fresh/build-id\";\nimport {\n  DEV_ERROR_OVERLAY_URL,\n  PARTIAL_SEARCH_PARAM,\n} from \"../../constants.ts\";\nimport { escape as escapeHtml } from \"@std/html\";\nimport { HttpError } from \"../../error.ts\";\nimport { getCodeFrame } from \"../../dev/middlewares/error_overlay/code_frame.ts\";\nimport { escapeScript } from \"../../utils.ts\";\nimport { HeadContext } from \"../head.ts\";\nimport { useContext } from \"preact/hooks\";\n\ninterface InternalPreactOptions extends PreactOptions {\n  [OptionsType.ATTR](name: string, value: unknown): string | void;\n  [OptionsType.VNODE](vnode: InternalVNode): void;\n  [OptionsType.HOOK](component: Component, index: number, type: number): void;\n  [OptionsType.DIFF](vnode: InternalVNode): void;\n  [OptionsType.RENDER](vnode: InternalVNode): void;\n  [OptionsType.DIFFED](vnode: InternalVNode): void;\n  [OptionsType.ERROR](\n    error: unknown,\n    vnode: InternalVNode,\n    oldVNode: InternalVNode,\n  ): void;\n}\n\ninterface InternalVNode extends VNode {\n  __c: Component | null;\n  __: InternalVNode | null;\n}\n\n// deno-lint-ignore no-explicit-any\nconst options: InternalPreactOptions = preactOptions as any;\n\nexport class RenderState {\n  nonce: string;\n  partialDepth = 0;\n  partialCount = 0;\n  error: Error | null = null;\n  // deno-lint-ignore no-explicit-any\n  slots: Array<{ id: number; name: string; vnode: VNode<any> } | null> = [];\n  // deno-lint-ignore no-explicit-any\n  islandProps: any[] = [];\n  islands = new Set<Island>();\n  islandAssets = new Set<string>();\n  // deno-lint-ignore no-explicit-any\n  encounteredPartials = new Set<any>();\n  owners = new Map<VNode, VNode>();\n  ownerStack: InternalVNode[] = [];\n\n  headComponents = new Map<string, VNode>();\n\n  // TODO: merge into bitmask field\n  renderedHtmlTag = false;\n  renderedHtmlBody = false;\n  renderedHtmlHead = false;\n  hasRuntimeScript = false;\n\n  constructor(\n    // deno-lint-ignore no-explicit-any\n    public ctx: PageProps<any, any>,\n    public buildCache: BuildCache,\n    public partialId: string,\n  ) {\n    this.nonce = crypto.randomUUID().replace(/-/g, \"\");\n  }\n\n  clear() {\n    this.islands.clear();\n    this.encounteredPartials.clear();\n    this.owners.clear();\n    this.slots = [];\n    this.islandProps = [];\n    this.ownerStack = [];\n  }\n}\n\nlet RENDER_STATE: RenderState | null = null;\nexport function setRenderState(state: RenderState | null) {\n  RENDER_STATE = state;\n}\n\nconst oldVNodeHook = options[OptionsType.VNODE];\noptions[OptionsType.VNODE] = (vnode) => {\n  if (RENDER_STATE !== null) {\n    RENDER_STATE.owners.set(vnode, RENDER_STATE!.ownerStack.at(-1)!);\n    if (vnode.type === \"a\") {\n      setActiveUrl(vnode, RENDER_STATE.ctx.url.pathname);\n    }\n  }\n  assetHashingHook(vnode, BUILD_ID);\n\n  if (typeof vnode.type === \"function\") {\n    if (vnode.type === Partial) {\n      const props = vnode.props as PartialProps;\n      const key = normalizeKey(vnode.key);\n      const mode = !props.mode || props.mode === \"replace\"\n        ? PartialMode.Replace\n        : props.mode === \"append\"\n        ? PartialMode.Append\n        : PartialMode.Prepend;\n      props.children = wrapWithMarker(\n        props.children,\n        \"partial\",\n        `${props.name}:${mode}:${key}`,\n      );\n    }\n  } else if (typeof vnode.type === \"string\") {\n    if (vnode.type === \"body\") {\n      const scripts = h(FreshScripts, null);\n      if (vnode.props.children == null) {\n        vnode.props.children = scripts;\n      } else if (Array.isArray(vnode.props.children)) {\n        vnode.props.children.push(scripts);\n      } else {\n        vnode.props.children = [vnode.props.children, scripts];\n      }\n    }\n    if (CLIENT_NAV_ATTR in vnode.props) {\n      vnode.props[CLIENT_NAV_ATTR] = String(vnode.props[CLIENT_NAV_ATTR]);\n    }\n  }\n\n  oldVNodeHook?.(vnode);\n};\n\nconst oldAttrHook = options[OptionsType.ATTR];\noptions[OptionsType.ATTR] = (name, value) => {\n  if (name === CLIENT_NAV_ATTR) {\n    return `${CLIENT_NAV_ATTR}=\"${String(Boolean(value))}\"`;\n  } else if (name === \"key\") {\n    return `${DATA_FRESH_KEY}=\"${escapeHtml(String(value))}\"`;\n  }\n\n  return oldAttrHook?.(name, value);\n};\n\nconst PATCHED = new WeakSet<VNode>();\n\nfunction normalizeKey(key: unknown): string {\n  const value = key ?? \"\";\n  const s = (typeof value !== \"string\") ? String(value) : value;\n  return s.replaceAll(\":\", \"_\");\n}\n\nconst oldDiff = options[OptionsType.DIFF];\noptions[OptionsType.DIFF] = (vnode) => {\n  if (RENDER_STATE !== null) {\n    patcher: if (\n      typeof vnode.type === \"function\" && vnode.type !== Fragment\n    ) {\n      if (vnode.type === Partial) {\n        RENDER_STATE.partialDepth++;\n\n        const name = (vnode.props as PartialProps).name;\n        if (typeof name === \"string\") {\n          if (RENDER_STATE.encounteredPartials.has(name)) {\n            throw new Error(\n              `Rendered response contains duplicate partial name: \"${name}\"`,\n            );\n          }\n\n          RENDER_STATE.encounteredPartials.add(name);\n        }\n\n        if (hasIslandOwner(RENDER_STATE, vnode)) {\n          throw new Error(\n            `<Partial> components cannot be used inside islands.`,\n          );\n        }\n      } else if (\n        !PATCHED.has(vnode)\n      ) {\n        const island = RENDER_STATE.buildCache.islandRegistry.get(vnode.type);\n        const insideIsland = hasIslandOwner(RENDER_STATE, vnode);\n        if (island === undefined) {\n          if (insideIsland) break patcher;\n\n          // Not an island, but we might need to preserve keys\n          if (vnode.key !== undefined) {\n            const key = normalizeKey(vnode.key);\n            const originalType = vnode.type;\n            vnode.type = (props) => {\n              const child = h(originalType, props);\n              PATCHED.add(child);\n              return wrapWithMarker(child, \"key\", key);\n            };\n          }\n\n          break patcher;\n        }\n\n        const { islands, islandProps, islandAssets } = RENDER_STATE;\n\n        if (insideIsland) {\n          for (let i = 0; i < island.css.length; i++) {\n            const css = island.css[i];\n            islandAssets.add(css);\n          }\n          break patcher;\n        }\n\n        islands.add(island);\n\n        const originalType = vnode.type;\n        vnode.type = (props) => {\n          for (const name in props) {\n            // deno-lint-ignore no-explicit-any\n            const value = (props as any)[name];\n            if (\n              name === \"children\" || (isValidElement(value) && !isSignal(value))\n            ) {\n              const slotId = RENDER_STATE!.slots.length;\n              RENDER_STATE!.slots.push({ id: slotId, name, vnode: value });\n              // deno-lint-ignore no-explicit-any\n              (props as any)[name] = h(Slot, {\n                name,\n                id: slotId,\n              }, value);\n            }\n          }\n          const propsIdx = islandProps.push({ slots: [], props }) - 1;\n\n          const child = h(originalType, props);\n          PATCHED.add(child);\n\n          const key = normalizeKey(vnode.key);\n          return wrapWithMarker(\n            child,\n            \"island\",\n            `${island!.name}:${propsIdx}:${key}`,\n          );\n        };\n      }\n    } else if (typeof vnode.type === \"string\") {\n      switch (vnode.type) {\n        case \"html\":\n          RENDER_STATE!.renderedHtmlTag = true;\n          break;\n        case \"head\": {\n          RENDER_STATE!.renderedHtmlHead = true;\n\n          const entryAssets = RENDER_STATE.buildCache.getEntryAssets();\n          // deno-lint-ignore no-explicit-any\n          const items: VNode<any>[] = [];\n          if (entryAssets.length > 0) {\n            for (let i = 0; i < entryAssets.length; i++) {\n              const id = entryAssets[i];\n\n              if (id.endsWith(\".css\")) {\n                items.push(\n                  // deno-lint-ignore no-explicit-any\n                  h(\"link\", { rel: \"stylesheet\", href: asset(id) } as any),\n                );\n              }\n            }\n          }\n\n          // deno-lint-ignore no-explicit-any\n          items.push(h(RemainingHead, null) as VNode<any>);\n\n          if (Array.isArray(vnode.props.children)) {\n            vnode.props.children.push(...items);\n          } else if (\n            vnode.props.children !== null &&\n            typeof vnode.props.children === \"object\"\n          ) {\n            // deno-lint-ignore no-explicit-any\n            items.unshift(vnode.props.children as any);\n            vnode.props.children = items;\n          } else {\n            vnode.props.children = items;\n          }\n\n          break;\n        }\n        case \"body\":\n          RENDER_STATE!.renderedHtmlBody = true;\n          break;\n        case \"title\":\n        case \"meta\":\n        case \"link\":\n        case \"script\":\n        case \"style\":\n        case \"base\":\n        case \"noscript\":\n        case \"template\":\n          {\n            if (PATCHED.has(vnode)) {\n              break;\n            }\n\n            const originalType = vnode.type;\n\n            let cacheKey: string | null = vnode.key ??\n              (originalType === \"title\" ? \"title\" : null);\n\n            if (cacheKey === null) {\n              // deno-lint-ignore no-explicit-any\n              const props = vnode.props as any;\n\n              const keys = Object.keys(vnode.props);\n              keys.sort();\n              cacheKey = `${originalType}`;\n              for (let i = 0; i < keys.length; i++) {\n                const key = keys[i];\n                if (key === \"children\" || key === \"nonce\" || key === \"ref\") {\n                  continue;\n                } else if (key === \"dangerouslySetInnerHTML\") {\n                  cacheKey += String(props[key].__html);\n                  continue;\n                } else if (originalType === \"meta\" && key === \"content\") {\n                  continue;\n                }\n\n                cacheKey += `::${props[key]}`;\n              }\n            }\n\n            const originalKey = vnode.key;\n\n            // deno-lint-ignore no-explicit-any\n            (vnode as any).type = (props: any) => {\n              const value = useContext(HeadContext);\n\n              if (originalKey) {\n                props[\"data-key\"] = originalKey;\n              }\n\n              const vnode = h(originalType, props);\n              PATCHED.add(vnode);\n\n              if (RENDER_STATE !== null) {\n                if (value) {\n                  RENDER_STATE.headComponents.set(cacheKey, vnode);\n                  return null;\n                } else if (value !== undefined) {\n                  const cached = RENDER_STATE.headComponents.get(cacheKey);\n                  if (cached !== undefined) {\n                    RENDER_STATE.headComponents.delete(cacheKey);\n                    return cached;\n                  }\n                }\n              }\n\n              return vnode;\n            };\n          }\n          break;\n      }\n\n      if (\n        vnode.key !== undefined &&\n        (RENDER_STATE!.partialDepth > 0 || hasIslandOwner(RENDER_STATE!, vnode))\n      ) {\n        (vnode.props as Record<string, unknown>)[DATA_FRESH_KEY] = String(\n          vnode.key,\n        );\n      }\n    }\n  }\n\n  oldDiff?.(vnode);\n};\n\nconst oldRender = options[OptionsType.RENDER];\noptions[OptionsType.RENDER] = (vnode) => {\n  if (\n    typeof vnode.type === \"function\" && vnode.type !== Fragment &&\n    RENDER_STATE !== null\n  ) {\n    RENDER_STATE.ownerStack.push(vnode);\n  }\n  oldRender?.(vnode);\n};\n\nconst oldDiffed = options[OptionsType.DIFFED];\noptions[OptionsType.DIFFED] = (vnode) => {\n  if (\n    typeof vnode.type === \"function\" && vnode.type !== Fragment &&\n    RENDER_STATE !== null\n  ) {\n    RENDER_STATE.ownerStack.pop();\n\n    if (vnode.type === Partial) {\n      RENDER_STATE.partialDepth--;\n    }\n  }\n  oldDiffed?.(vnode);\n};\n\nfunction RemainingHead() {\n  if (RENDER_STATE !== null) {\n    // deno-lint-ignore no-explicit-any\n    const items: VNode<any>[] = [];\n    if (RENDER_STATE.headComponents.size > 0) {\n      items.push(...RENDER_STATE.headComponents.values());\n    }\n\n    RENDER_STATE.islands.forEach((island) => {\n      if (island.css.length > 0) {\n        for (let i = 0; i < island.css.length; i++) {\n          const css = island.css[i];\n          items.push(h(\"link\", { rel: \"stylesheet\", href: css }));\n        }\n      }\n    });\n\n    RENDER_STATE.islandAssets.forEach((css) => {\n      items.push(h(\"link\", { rel: \"stylesheet\", href: css }));\n    });\n\n    if (items.length > 0) {\n      return h(Fragment, null, items);\n    }\n  }\n  return null;\n}\n\ninterface SlotProps {\n  name: string;\n  id: number;\n  children?: ComponentChildren;\n}\nfunction Slot(props: SlotProps) {\n  if (RENDER_STATE !== null) {\n    RENDER_STATE.slots[props.id] = null;\n  }\n  return wrapWithMarker(props.children, \"slot\", `${props.id}:${props.name}`);\n}\n\n/**\n * Check if the current component was rendered in an island\n */\nfunction hasIslandOwner(current: RenderState, vnode: VNode): boolean {\n  let tmpVNode = vnode;\n  let owner;\n  while ((owner = current.owners.get(tmpVNode)) !== undefined) {\n    if (current.buildCache.islandRegistry.has(owner.type as ComponentType)) {\n      return true;\n    }\n    tmpVNode = owner;\n  }\n\n  return false;\n}\n\nfunction wrapWithMarker(\n  vnode: ComponentChildren,\n  kind: string,\n  markerText: string,\n) {\n  return h(\n    Fragment,\n    null,\n    h(Fragment, {\n      // @ts-ignore unstable property is not typed\n      UNSTABLE_comment: `frsh:${kind}:${markerText}`,\n    }),\n    vnode,\n    h(Fragment, {\n      // @ts-ignore unstable property is not typed\n      UNSTABLE_comment: \"/frsh:\" + kind,\n    }),\n  );\n}\n\n// deno-lint-ignore no-explicit-any\nfunction isSignal(x: any): x is Signal {\n  return (\n    x !== null &&\n    typeof x === \"object\" &&\n    typeof x.peek === \"function\" &&\n    \"value\" in x\n  );\n}\n\n// deno-lint-ignore no-explicit-any\nfunction isComputedSignal(x: any): x is ReadonlySignal {\n  return isSignal(x) &&\n    ((\"x\" in x && typeof x.x === \"function\") ||\n      \"_fn\" in x && typeof x._fn === \"function\");\n}\n\n// deno-lint-ignore no-explicit-any\nfunction isVNode(x: any): x is VNode {\n  return x !== null && typeof x === \"object\" && \"type\" in x && \"ref\" in x &&\n    \"__k\" in x &&\n    isValidElement(x);\n}\n\nconst stringifiers: Stringifiers = {\n  Computed: (value: unknown) => {\n    return isComputedSignal(value) ? { value: value.peek() } : undefined;\n  },\n  Signal: (value: unknown) => {\n    return isSignal(value) ? { value: value.peek() } : undefined;\n  },\n  Slot: (value: unknown) => {\n    if (isVNode(value) && value.type === Slot) {\n      const props = value.props as SlotProps;\n      return {\n        value: {\n          name: props.name,\n          id: props.id,\n        },\n      };\n    }\n  },\n};\n\nexport function FreshScripts() {\n  if (RENDER_STATE === null) return null;\n  if (RENDER_STATE.hasRuntimeScript) {\n    return null;\n  }\n  RENDER_STATE.hasRuntimeScript = true;\n  const { slots } = RENDER_STATE;\n\n  // Remaining slots must be rendered before creating the Fresh runtime\n  // script, so that we have the full list of islands rendered\n  return (\n    h(\n      Fragment,\n      null,\n      slots.map((slot) => {\n        if (slot === null) return null;\n        return (\n          h(\"template\", {\n            key: slot.id,\n            id: `frsh-${slot.id}-${slot.name}`,\n          }, slot.vnode)\n        );\n      }),\n      h(FreshRuntimeScript, null),\n    )\n  );\n}\n\nexport interface PartialStateJson {\n  islands: {\n    name: string;\n    chunk: string;\n    exportName: string;\n  }[];\n  props: string;\n}\n\nfunction FreshRuntimeScript() {\n  const { islands, nonce, ctx, islandProps, partialId, buildCache } =\n    RENDER_STATE!;\n  const basePath = ctx.config.basePath;\n\n  const islandArr = Array.from(islands);\n\n  if (ctx.url.searchParams.has(PARTIAL_SEARCH_PARAM)) {\n    const islands = islandArr.map((island) => {\n      return {\n        exportName: island.exportName,\n        chunk: island.file,\n        name: island.name,\n      };\n    });\n\n    const serializedProps = stringify(islandProps, stringifiers);\n    const json: PartialStateJson = {\n      islands,\n      props: serializedProps,\n    };\n\n    return (\n      h(\"script\", {\n        id: `__FRSH_STATE_${partialId}`,\n        type: \"application/json\",\n        dangerouslySetInnerHTML: {\n          __html: escapeScript(JSON.stringify(json), { json: true }),\n        },\n      })\n    );\n  } else {\n    const islandImports = islandArr.map((island) => {\n      const named = island.exportName === \"default\"\n        ? island.name\n        : island.exportName === island.name\n        ? `{ ${island.exportName} }`\n        : `{ ${island.exportName} as ${island.name} }`;\n\n      const islandSpec = island.file.startsWith(\".\")\n        ? island.file.slice(1)\n        : island.file;\n      return `import ${named} from \"${basePath}${islandSpec}\";`;\n    }).join(\"\");\n\n    const islandObj = \"{\" + islandArr.map((island) => island.name)\n      .join(\",\") +\n      \"}\";\n\n    const serializedProps = escapeScript(\n      JSON.stringify(stringify(islandProps, stringifiers)),\n      { json: true },\n    );\n\n    const runtimeUrl = buildCache.clientEntry.startsWith(\".\")\n      ? buildCache.clientEntry.slice(1)\n      : buildCache.clientEntry;\n    const scriptContent =\n      `import { boot } from \"${basePath}${runtimeUrl}\";${islandImports}boot(${islandObj},${serializedProps});`;\n\n    return (\n      h(\n        Fragment,\n        null,\n        h(\"script\", {\n          type: \"module\",\n          nonce,\n          dangerouslySetInnerHTML: { __html: scriptContent },\n        }),\n        buildCache.features.errorOverlay ? h(ShowErrorOverlay, null) : null,\n      )\n    );\n  }\n}\n\nexport function ShowErrorOverlay() {\n  if (RENDER_STATE === null) return null;\n\n  const { ctx } = RENDER_STATE;\n  const error = ctx.error;\n\n  if (error === null || error === undefined) return null;\n\n  // Ignore HTTP errors <500\n  if (error instanceof HttpError && error.status < 500) {\n    return null;\n  }\n\n  const basePath = ctx.config.basePath;\n\n  const searchParams = new URLSearchParams();\n\n  if (typeof error === \"object\") {\n    if (\"message\" in error) {\n      searchParams.append(\"message\", String(error.message));\n    }\n\n    if (\"stack\" in error && typeof error.stack === \"string\") {\n      searchParams.append(\"stack\", error.stack);\n      const codeFrame = getCodeFrame(error.stack, ctx.config.root);\n      if (codeFrame !== undefined) {\n        searchParams.append(\"code-frame\", codeFrame);\n      }\n    }\n  } else {\n    searchParams.append(\"message\", String(error));\n  }\n\n  return (\n    h(\"iframe\", {\n      id: \"fresh-error-overlay\",\n      src: `${basePath}${DEV_ERROR_OVERLAY_URL}?${searchParams.toString()}`,\n      style:\n        \"unset: all; position: fixed; top: 0; left: 0; z-index: 99999; width: 100%; height: 100%; border: none;\",\n    })\n  );\n}\n"
  },
  {
    "path": "packages/fresh/src/runtime/shared.ts",
    "content": "import type { ComponentChildren, VNode } from \"preact\";\nimport { BUILD_ID } from \"@fresh/build-id\";\nimport { assetInternal, assetSrcSetInternal } from \"./shared_internal.ts\";\n\n/**\n * Returns true when the current runtime is the browser and false otherwise. This is used for guard runtime-dependent code.\n * Shorthand for the following:\n * `typeof document !== \"undefined\"`\n *\n * @example\n * ```\n *  if (IS_BROWSER) {\n *    alert('This is running in the browser!');\n *  } else {\n *    console.log('This code is running on the server, no access to window or alert');\n *  }\n * ```\n *\n * Without this guard, alert pauses the server until return is pressed in the console.\n */\nexport const IS_BROWSER = typeof document !== \"undefined\";\n\n/**\n * Create a \"locked\" asset path. This differs from a plain path in that it is\n * specific to the current version of the application, and as such can be safely\n * served with a very long cache lifetime (1 year).\n */\nexport function asset(path: string): string {\n  return assetInternal(path, BUILD_ID);\n}\n\n/** Apply the `asset` function to urls in a `srcset` attribute. */\nexport function assetSrcSet(srcset: string): string {\n  return assetSrcSetInternal(srcset, BUILD_ID);\n}\n\nexport interface PartialProps {\n  children?: ComponentChildren;\n  /**\n   * The name of the partial. This value must be unique across partials.\n   */\n  name: string;\n  /**\n   * Define how the new HTML should be applied.\n   * @default {\"replace\"}\n   */\n  mode?: \"replace\" | \"prepend\" | \"append\";\n}\n\nexport function Partial(props: PartialProps): VNode {\n  // deno-lint-ignore no-explicit-any\n  return props.children as any;\n}\nPartial.displayName = \"Partial\";\n\nexport { Head, type HeadProps } from \"./head.ts\";\n"
  },
  {
    "path": "packages/fresh/src/runtime/shared_internal.ts",
    "content": "import type { Component, Options as PreactOptions, VNode } from \"preact\";\nimport { ASSET_CACHE_BUST_KEY } from \"../constants.ts\";\n\nexport const DATA_CURRENT = \"data-current\";\nexport const DATA_ANCESTOR = \"data-ancestor\";\nexport const DATA_FRESH_KEY = \"data-frsh-key\";\nexport const CLIENT_NAV_ATTR = \"f-client-nav\";\n\nexport const enum OptionsType {\n  ATTR = \"attr\",\n  VNODE = \"vnode\",\n  HOOK = \"__h\",\n  DIFF = \"__b\",\n  RENDER = \"__r\",\n  DIFFED = \"diffed\",\n  ERROR = \"__e\",\n}\n\nexport interface InternalVNode extends VNode {\n  __c: Component | null;\n  __: InternalVNode | null;\n}\n\nexport interface InternalPreactOptions extends PreactOptions {\n  [OptionsType.ATTR](name: string, value: unknown): string | void;\n  [OptionsType.VNODE](vnode: InternalVNode): void;\n  [OptionsType.HOOK](component: Component, index: number, type: number): void;\n  [OptionsType.DIFF](vnode: InternalVNode): void;\n  [OptionsType.RENDER](vnode: InternalVNode): void;\n  [OptionsType.DIFFED](vnode: InternalVNode): void;\n  [OptionsType.ERROR](\n    error: unknown,\n    vnode: InternalVNode,\n    oldVNode: InternalVNode,\n  ): void;\n}\n\nexport const enum UrlMatchKind {\n  None,\n  Ancestor,\n  Current,\n}\n\nexport function matchesUrl(current: string, needle: string): UrlMatchKind {\n  let href = new URL(needle, \"http://localhost\").pathname;\n  if (href !== \"/\" && href.endsWith(\"/\")) {\n    href = href.slice(0, -1);\n  }\n\n  if (current !== \"/\" && current.endsWith(\"/\")) {\n    current = current.slice(0, -1);\n  }\n\n  if (current === href) {\n    return UrlMatchKind.Current;\n  } else if (current.startsWith(href + \"/\") || href === \"/\") {\n    return UrlMatchKind.Ancestor;\n  }\n\n  return UrlMatchKind.None;\n}\n\n/**\n * Mark active or ancestor link\n * Note: This function is used both on the server and the client\n */\nexport function setActiveUrl(vnode: VNode, pathname: string): void {\n  const props = vnode.props as Record<string, unknown>;\n  const hrefProp = props.href;\n  if (typeof hrefProp === \"string\" && hrefProp.startsWith(\"/\")) {\n    const match = matchesUrl(pathname, hrefProp);\n    if (match === UrlMatchKind.Current) {\n      props[DATA_CURRENT] = \"true\";\n      props[\"aria-current\"] = \"page\";\n    } else if (match === UrlMatchKind.Ancestor) {\n      props[DATA_ANCESTOR] = \"true\";\n      props[\"aria-current\"] = \"true\";\n    }\n  }\n}\n\nexport const enum PartialMode {\n  Replace,\n  Append,\n  Prepend,\n}\n\n/**\n * Create a \"locked\" asset path. This differs from a plain path in that it is\n * specific to the current version of the application, and as such can be safely\n * served with a very long cache lifetime (1 year).\n */\nexport function assetInternal(path: string, buildId: string): string {\n  if (!path.startsWith(\"/\") || path.startsWith(\"//\")) return path;\n  try {\n    const url = new URL(path, \"https://freshassetcache.local\");\n    if (\n      url.protocol !== \"https:\" || url.host !== \"freshassetcache.local\" ||\n      url.searchParams.has(ASSET_CACHE_BUST_KEY)\n    ) {\n      return path;\n    }\n    url.searchParams.set(ASSET_CACHE_BUST_KEY, buildId);\n    return url.pathname + url.search + url.hash;\n  } catch (err) {\n    // deno-lint-ignore no-console\n    console.warn(\n      `Failed to create asset() URL, falling back to regular path ('${path}'):`,\n      err,\n    );\n    return path;\n  }\n}\n\n/** Apply the `asset` function to urls in a `srcset` attribute. */\nexport function assetSrcSetInternal(srcset: string, buildId: string): string {\n  if (srcset.includes(\"(\")) return srcset; // Bail if the srcset contains complicated syntax.\n  const parts = srcset.split(\",\");\n  const constructed = [];\n  for (const part of parts) {\n    const trimmed = part.trimStart();\n    const leadingWhitespace = part.length - trimmed.length;\n    if (trimmed === \"\") return srcset; // Bail if the srcset is malformed.\n    let urlEnd = trimmed.indexOf(\" \");\n    if (urlEnd === -1) urlEnd = trimmed.length;\n    const leading = part.substring(0, leadingWhitespace);\n    const url = trimmed.substring(0, urlEnd);\n    const trailing = trimmed.substring(urlEnd);\n    constructed.push(leading + assetInternal(url, buildId) + trailing);\n  }\n  return constructed.join(\",\");\n}\n\nexport function assetHashingHook(\n  vnode: VNode<{\n    src?: string;\n    srcset?: string;\n    [\"data-fresh-disable-lock\"]?: boolean;\n  }>,\n  buildId: string,\n) {\n  if (vnode.type === \"img\" || vnode.type === \"source\") {\n    const { props } = vnode;\n    if (props[\"data-fresh-disable-lock\"]) return;\n    if (typeof props.src === \"string\") {\n      props.src = assetInternal(props.src, buildId);\n    }\n    if (typeof props.srcset === \"string\") {\n      props.srcset = assetSrcSetInternal(props.srcset, buildId);\n    }\n  }\n}\n"
  },
  {
    "path": "packages/fresh/src/segments.ts",
    "content": "import type { AnyComponent } from \"preact\";\nimport type { MaybeLazyMiddleware, Middleware } from \"./middlewares/mod.ts\";\nimport { type Method, patternToSegments } from \"./router.ts\";\nimport type { LayoutConfig, Route } from \"./types.ts\";\nimport { type Context, getInternals } from \"./context.ts\";\nimport { recordSpanError, tracer } from \"./otel.ts\";\nimport { type HandlerFn, isHandlerByMethod } from \"./handlers.ts\";\nimport {\n  type AsyncAnyComponent,\n  type PageProps,\n  renderRouteComponent,\n} from \"./render.ts\";\nimport { HttpError } from \"./error.ts\";\n\nexport type RouteComponent<State> =\n  | AsyncAnyComponent<PageProps<unknown, State>>\n  | AnyComponent<PageProps<unknown, State>>;\n\nexport interface Segment<State> {\n  pattern: string;\n  middlewares: MaybeLazyMiddleware<State>[];\n  layout: {\n    component: RouteComponent<State>;\n    config: LayoutConfig | null;\n  } | null;\n  errorRoute: Route<State> | null;\n  notFound: Middleware<State> | null;\n  app: RouteComponent<State> | null;\n  children: Map<string, Segment<State>>;\n  parent: Segment<State> | null;\n}\n\nexport function newSegment<State>(\n  pattern: string,\n  parent: Segment<State> | null,\n): Segment<State> {\n  return {\n    pattern,\n    middlewares: [],\n    layout: null,\n    app: null,\n    errorRoute: null,\n    notFound: null,\n    parent,\n    children: new Map(),\n  };\n}\n\nexport function getOrCreateSegment<State>(\n  root: Segment<State>,\n  path: string,\n  includeLast: boolean,\n): Segment<State> {\n  let current = root;\n\n  const segments = patternToSegments(path, root.pattern, includeLast);\n  for (let i = 0; i < segments.length; i++) {\n    const seg = segments[i];\n    if (seg === root.pattern) {\n      current = root;\n    } else {\n      let child = current.children.get(seg);\n      if (child === undefined) {\n        child = newSegment(seg, current);\n        current.children.set(seg, child);\n      }\n\n      current = child;\n    }\n  }\n\n  return current;\n}\n\nexport function segmentToMiddlewares<State>(\n  segment: Segment<State>,\n): MaybeLazyMiddleware<State>[] {\n  const result: MaybeLazyMiddleware<State>[] = [];\n\n  const stack: Segment<State>[] = [];\n  let current: Segment<State> | null = segment;\n  while (current !== null) {\n    stack.push(current);\n    current = current.parent;\n  }\n\n  const root = stack.at(-1)!;\n\n  for (let i = stack.length - 1; i >= 0; i--) {\n    const seg = stack[i];\n    const { layout, app, errorRoute } = seg;\n\n    result.push(async function segmentMiddleware(ctx) {\n      const internals = getInternals(ctx);\n\n      const prevApp = internals.app;\n      const prevLayouts = internals.layouts;\n\n      if (app !== null) {\n        internals.app = app;\n      }\n\n      if (layout !== null) {\n        if (layout.config?.skipAppWrapper) {\n          internals.app = null;\n        }\n\n        const def = { props: null, component: layout.component };\n        if (layout.config?.skipInheritedLayouts) {\n          internals.layouts = [def];\n        } else {\n          internals.layouts = [...internals.layouts, def];\n        }\n      }\n\n      try {\n        return await ctx.next();\n      } catch (err) {\n        const status = err instanceof HttpError ? err.status : 500;\n        if (root.notFound !== null && status === 404) {\n          return await root.notFound(ctx);\n        }\n\n        if (errorRoute !== null) {\n          return await renderRoute(ctx, errorRoute, status);\n        }\n\n        throw err;\n      } finally {\n        internals.app = prevApp;\n        internals.layouts = prevLayouts;\n      }\n    });\n\n    if (seg.middlewares.length > 0) {\n      result.push(...seg.middlewares);\n    }\n  }\n\n  return result;\n}\n\nexport async function renderRoute<State>(\n  ctx: Context<State>,\n  route: Route<State>,\n  status = 200,\n): Promise<Response> {\n  const internals = getInternals(ctx);\n  if (route.config?.skipAppWrapper) {\n    internals.app = null;\n  }\n  if (route.config?.skipInheritedLayouts) {\n    internals.layouts = [];\n  }\n\n  const method = ctx.req.method.toUpperCase() as Method;\n\n  const handlers = route.handler;\n  if (handlers === undefined) {\n    throw new Error(`Unexpected missing handlers`);\n  }\n\n  const headers = new Headers();\n  headers.set(\"Content-Type\", \"text/html;charset=utf-8\");\n\n  const res = await tracer.startActiveSpan(\"handler\", {\n    attributes: { \"fresh.span_type\": \"fs_routes/handler\" },\n  }, async (span) => {\n    try {\n      let fn: HandlerFn<unknown, State> | null = null;\n      if (isHandlerByMethod(handlers)) {\n        if (handlers[method] !== undefined) {\n          fn = handlers[method];\n        } else if (method === \"HEAD\" && handlers.GET !== undefined) {\n          fn = handlers.GET;\n        }\n      } else {\n        fn = handlers;\n      }\n\n      if (fn === null) return await ctx.next();\n\n      return await fn(ctx);\n    } catch (err) {\n      recordSpanError(span, err);\n      throw err;\n    } finally {\n      span.end();\n    }\n  });\n\n  if (res instanceof Response) {\n    return res;\n  }\n\n  if (typeof res.status === \"number\") {\n    status = res.status;\n  }\n  if (res.headers !== undefined) {\n    if (res.headers instanceof Headers) {\n      res.headers.forEach((value, key) => {\n        headers.set(key, value);\n      });\n    } else if (Array.isArray(res.headers)) {\n      for (let i = 0; i < res.headers.length; i++) {\n        const entry = res.headers[i];\n        headers.set(entry[0], entry[1]);\n      }\n    } else {\n      for (const [name, value] of Object.entries(res.headers)) {\n        headers.set(name, value);\n      }\n    }\n  }\n\n  let vnode = null;\n  if (route.component !== undefined) {\n    const result = await renderRouteComponent(ctx, {\n      component: route.component,\n      // deno-lint-ignore no-explicit-any\n      props: res.data as any,\n    }, () => null);\n\n    if (result instanceof Response) {\n      return result;\n    }\n\n    vnode = result;\n  }\n\n  return ctx.render(vnode, { headers, status });\n}\n"
  },
  {
    "path": "packages/fresh/src/segments_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport { getOrCreateSegment, newSegment } from \"./segments.ts\";\n\nDeno.test(\"findOrCreateSegment - root\", () => {\n  const root = newSegment(\"\", null);\n  const found = getOrCreateSegment(root, \"\", false);\n  expect(found).toEqual(root);\n});\n\nDeno.test(\"findOrCreateSegment - /foo/bar\", () => {\n  const root = newSegment(\"\", null);\n  const found = getOrCreateSegment(root, \"/foo/bar\", false);\n  expect(found).toEqual(root.children.get(\"foo\"));\n});\n\nDeno.test(\"findOrCreateSegment - /:foo/bar/:baz\", () => {\n  const root = newSegment(\"\", null);\n  const found = getOrCreateSegment(root, \"/:foo/bar/:baz\", false);\n  expect(found).toEqual(\n    root.children.get(\":foo\")?.children.get(\"bar\"),\n  );\n});\n\nDeno.test(\"findOrCreateSegment - /foo/bar/\", () => {\n  const root = newSegment(\"\", null);\n  const found = getOrCreateSegment(root, \"/foo/bar/\", false);\n  expect(found).toEqual(root.children.get(\"foo\")?.children.get(\"bar\"));\n});\n\nDeno.test(\"findOrCreateSegment - /foo/bar with last\", () => {\n  const root = newSegment(\"\", null);\n  const found = getOrCreateSegment(root, \"/foo/bar\", true);\n  expect(found).toEqual(root.children.get(\"foo\")?.children.get(\"bar\"));\n});\n"
  },
  {
    "path": "packages/fresh/src/server/tailwind_aot_error_page.tsx",
    "content": "const LINK = \"https://fresh.deno.dev/docs/concepts/ahead-of-time-builds\";\n\nexport default function TailwindErrorPage() {\n  return (\n    <div class=\"frsh-error-page\">\n      <div style=\"max-width: 48rem; padding: 2rem 1rem; margin: 0 auto; font-family: sans-serif\">\n        <h1>Finish setting up Fresh</h1>\n        <p style=\"line-height: 1.6;margin-bottom: 1rem;\">\n          The <b>tailwindcss</b>{\" \"}\n          plugin requires ahead of time builds to be set up for production\n          usage. To finish the setup, follow these steps:\n        </p>\n        <ol style=\"line-height: 1.6; margin-bottom: 1.5rem\">\n          <li>\n            Go to your project in Deno Deploy and click the{\" \"}\n            <code>Settings</code> tab.\n          </li>\n          <li>\n            In the Git Integration section, enter <code>deno task build</code>\n            {\" \"}\n            in the <code>Build Command</code> input.\n          </li>\n          <li>\n            Save the changes.\n          </li>\n        </ol>\n        <p>\n          See the detailed guide here: <a href={LINK}>{LINK}</a>.\n        </p>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/src/test_utils.ts",
    "content": "import { Context, type ServerIslandRegistry } from \"./context.ts\";\nimport type { FsAdapter } from \"./fs.ts\";\nimport type { BuildCache, StaticFile } from \"./build_cache.ts\";\nimport type { ResolvedFreshConfig } from \"./config.ts\";\nimport type { WalkEntry } from \"@std/fs/walk\";\nimport { DEFAULT_CONN_INFO } from \"./app.ts\";\nimport type { Command } from \"./commands.ts\";\nimport { fsItemsToCommands, type FsRouteFile } from \"./fs_routes.ts\";\nimport * as path from \"@std/path\";\n\nconst STUB = {} as unknown as Deno.ServeHandlerInfo;\n\nexport class FakeServer {\n  constructor(\n    public handler: (\n      req: Request,\n      info: Deno.ServeHandlerInfo,\n    ) => Response | Promise<Response>,\n  ) {}\n\n  async get(path: string, init?: RequestInit): Promise<Response> {\n    const url = this.toUrl(path);\n    const req = new Request(url, init);\n    return await this.handler(req, STUB);\n  }\n  async post(path: string, body?: BodyInit): Promise<Response> {\n    const url = this.toUrl(path);\n    const req = new Request(url, { method: \"post\", body });\n    return await this.handler(req, STUB);\n  }\n  async patch(path: string, body?: BodyInit): Promise<Response> {\n    const url = this.toUrl(path);\n    const req = new Request(url, { method: \"patch\", body });\n    return await this.handler(req, STUB);\n  }\n  async put(path: string, body?: BodyInit): Promise<Response> {\n    const url = this.toUrl(path);\n    const req = new Request(url, { method: \"put\", body });\n    return await this.handler(req, STUB);\n  }\n  async delete(path: string): Promise<Response> {\n    const url = this.toUrl(path);\n    const req = new Request(url, { method: \"delete\" });\n    return await this.handler(req, STUB);\n  }\n  async head(path: string): Promise<Response> {\n    const url = this.toUrl(path);\n    const req = new Request(url, { method: \"head\" });\n    return await this.handler(req, STUB);\n  }\n  async options(path: string): Promise<Response> {\n    const url = this.toUrl(path);\n    const req = new Request(url, { method: \"options\" });\n    return await this.handler(req, STUB);\n  }\n\n  async request(req: Request): Promise<Response> {\n    return await this.handler(req, STUB);\n  }\n\n  private toUrl(path: string) {\n    return new URL(path, \"http://localhost/\");\n  }\n}\n\nconst DEFAULT_CONFIG: ResolvedFreshConfig = {\n  root: \"\",\n  mode: \"production\",\n  basePath: \"\",\n};\n\nexport function serveMiddleware<T>(\n  middleware: (ctx: Context<T>) => Response | Promise<Response>,\n  options: {\n    config?: ResolvedFreshConfig;\n    buildCache?: BuildCache<T>;\n    next?: () => Promise<Response>;\n    route?: string | null;\n  } = {},\n): FakeServer {\n  return new FakeServer(async (req) => {\n    const next = options.next ??\n      (() => new Response(\"not found\", { status: 404 }));\n    const config = options.config ?? DEFAULT_CONFIG;\n    const buildCache = options.buildCache ??\n      new MockBuildCache<T>([], options.config?.mode ?? \"production\");\n\n    const ctx = new Context<T>(\n      req,\n      new URL(req.url),\n      DEFAULT_CONN_INFO,\n      options.route ?? null,\n      {},\n      config,\n      () => Promise.resolve(next()),\n      buildCache,\n    );\n    return await middleware(ctx);\n  });\n}\n\nexport function createFakeFs(files: Record<string, unknown>): FsAdapter {\n  return {\n    cwd: () => \".\",\n    async *walk(_root, options) {\n      const skip = options?.skip ?? [];\n      for (const file of Object.keys(files)) {\n        // Check if file matches any skip pattern\n        if (skip.some((pattern) => pattern.test(file))) {\n          continue;\n        }\n\n        const entry: WalkEntry = {\n          isDirectory: false,\n          isFile: true,\n          isSymlink: false,\n          name: file,\n          path: file,\n        };\n        yield entry;\n      }\n    },\n    // deno-lint-ignore require-await\n    async isDirectory(dir) {\n      return Object.keys(files).some((file) => file.startsWith(dir + \"/\"));\n    },\n    async mkdirp(_dir: string) {\n    },\n    readFile: Deno.readFile,\n    // deno-lint-ignore require-await\n    async readTextFile(path) {\n      return String(files[String(path)]);\n    },\n  };\n}\n\nexport const delay = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport async function withTmpDir(\n  options?: Deno.MakeTempOptions,\n): Promise<{ dir: string } & AsyncDisposable> {\n  const dir = await Deno.makeTempDir(options);\n  return {\n    dir,\n    async [Symbol.asyncDispose]() {\n      // Skip pointless cleanup in CI, speed up tests\n      if (Deno.env.get(\"CI\") === \"true\") return;\n\n      try {\n        await Deno.remove(dir, { recursive: true });\n      } catch {\n        // Temp files are not cleaned up automatically on Windows\n        // deno-lint-ignore no-console\n        console.warn(`Failed to clean up temp dir: \"${dir}\"`);\n      }\n    },\n  };\n}\n\nexport class MockBuildCache<State> implements BuildCache<State> {\n  #files: FsRouteFile<State>[];\n  root = \"\";\n  clientEntry = \"\";\n  islandRegistry: ServerIslandRegistry = new Map();\n  features = { errorOverlay: false };\n\n  constructor(files: FsRouteFile<State>[], mode: \"development\" | \"production\") {\n    this.features.errorOverlay = mode === \"development\";\n    this.#files = files;\n  }\n\n  getEntryAssets(): string[] {\n    return [];\n  }\n\n  getFsRoutes(): Command<State>[] {\n    return fsItemsToCommands(this.#files);\n  }\n\n  readFile(_pathname: string): Promise<StaticFile | null> {\n    return Promise.resolve(null);\n  }\n}\n\nexport async function writeFiles(dir: string, files: Record<string, string>) {\n  const entries = Object.entries(files);\n  await Promise.all(entries.map(async (entry) => {\n    const [pathname, content] = entry;\n    const fullPath = path.join(dir, pathname);\n    try {\n      await Deno.mkdir(path.dirname(fullPath), { recursive: true });\n      await Deno.writeTextFile(fullPath, content);\n    } catch (err) {\n      if (!(err instanceof Deno.errors.AlreadyExists)) {\n        throw err;\n      }\n    }\n  }));\n}\n"
  },
  {
    "path": "packages/fresh/src/types.ts",
    "content": "import type { RouteHandler } from \"./handlers.ts\";\nimport type { Method } from \"./router.ts\";\nimport type { RouteComponent } from \"./segments.ts\";\n\nexport interface RouteConfig {\n  /**\n   * A route override for the page. This is useful for pages where the route\n   * can not be expressed through the filesystem routing capabilities.\n   *\n   * The route override must be a path-to-regexp compatible route matcher.\n   */\n  routeOverride?: string;\n\n  /**\n   * If Content-Security-Policy should be enabled for this page. If 'true', a\n   * locked down policy will be used that allows only the scripts and styles\n   * that are generated by Fresh. Additional scripts and styles can be added\n   * using the `useCSP` hook.\n   */\n  csp?: boolean;\n\n  /**\n   * Skip already inherited layouts\n   * Default: `false`\n   */\n  skipInheritedLayouts?: boolean;\n\n  /**\n   * Skip rendering the `routes/_app` template\n   * Default: `false`\n   */\n  skipAppWrapper?: boolean;\n  /**\n   * Which method to use when loading this route lazily. This is only used\n   * for lazy routes.\n   * Default: `ALL`\n   */\n  methods?: \"ALL\" | Method[];\n}\n\nexport interface LayoutConfig {\n  /**\n   * Skip already inherited layouts\n   * Default: `false`\n   */\n  skipInheritedLayouts?: boolean;\n\n  /**\n   * Skip rendering the `routes/_app` template\n   * Default: `false`\n   */\n  skipAppWrapper?: boolean;\n}\n\nexport interface Route<State> {\n  component?: RouteComponent<State>;\n  config?: RouteConfig;\n  handler?: RouteHandler<unknown, State>;\n  /** Additional .css files to load */\n  css?: string[];\n}\n\n/**\n * Lazily construct items like middlewares, handlers, etc by wrapping them\n * in a function.\n */\nexport type Lazy<T> = () => Promise<T>;\n\n/**\n * Return T or lazy version of T\n */\nexport type MaybeLazy<T> = T | Lazy<T>;\n\n// TODO: Uncomment once JSR supports global types\n// declare global {\n//   namespace preact.createElement.JSX {\n//     interface HTMLAttributes {\n//       /**\n//        * Alternative url to fetch partials from on `<a>` or `<form>` tags\n//        */\n//       \"f-partial\"?: string | SignalLike<string>;\n//       /**\n//        * Enable or disable client side navigation and partials for this\n//        * particular node and its children.\n//        */\n//       \"f-client-nav\"?: boolean | SignalLike<boolean>;\n//     }\n//   }\n// }\n"
  },
  {
    "path": "packages/fresh/src/utils.ts",
    "content": "import * as path from \"@std/path\";\nimport type { Lazy, MaybeLazy } from \"./types.ts\";\nimport { pathToFileUrl, relativeUrl } from \"./file_url.ts\";\n\nexport function assertInDir(\n  filePath: string,\n  dir: string,\n): void {\n  let tmp = filePath;\n  if (!path.isAbsolute(tmp)) {\n    tmp = path.join(dir, filePath);\n  }\n\n  if (path.relative(dir, tmp).startsWith(\".\")) {\n    throw new Error(`Path \"${tmp}\" resolved outside of \"${dir}\"`);\n  }\n}\n\n/**\n * Converts a file path to a valid JS export name.\n *\n * @example\n * ```ts\n * pathToExportName(\"/islands/foo.tsx\");     // \"foo\"\n * pathToExportName(\"/islands/foo.v2.tsx\");  // \"foo_v2\"\n * pathToExportName(\"/islands/nav-bar.tsx\"); // \"nav_bar\"\n * pathToExportName(\"/islands/_.$bar.tsx\");  // \"_$bar\"\n * pathToExportName(\"/islands/1.hello.tsx\"); // \"_hello\"\n * pathToExportName(\"/islands/collapse...repeat_-dash.tsx\");\n * // \"collapse_repeat_dash\"\n * ```\n */\nexport function pathToExportName(filePath: string): string {\n  const name = path.basename(filePath, path.extname(filePath));\n  // Regex for valid JS identifier characters\n  const regex = /^[^a-z_$]|[^a-z0-9_$]/gi;\n  return name.replaceAll(regex, \"_\").replaceAll(/_{2,}/g, \"_\");\n}\n\nconst SCRIPT_ESCAPE = /<\\/(style|script)/gi;\nconst COMMENT_ESCAPE = /<!--/gi;\n\n// See https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements\nexport function escapeScript(\n  content: string,\n  options: { json?: boolean } = {},\n): string {\n  return content\n    .replaceAll(SCRIPT_ESCAPE, \"<\\\\/$1\")\n    .replaceAll(COMMENT_ESCAPE, options.json ? \"\\\\u003C!--\" : \"\\\\x3C!--\");\n}\n\nexport class UniqueNamer {\n  #seen = new Map<string, number>();\n\n  getUniqueName(name: string): string {\n    // These names are only internal, so we can convert everything to\n    // plain ASCII.\n    name = name.replaceAll(/([^A-Za-z0-9_$]+)/g, \"_\");\n\n    // Identifiers must not start with a number\n    if (/^\\d/.test(name) || JS_RESERVED.has(name)) {\n      name = \"_\" + name;\n    }\n\n    const count = this.#seen.get(name);\n    if (count === undefined) {\n      this.#seen.set(name, 1);\n    } else {\n      this.#seen.set(name, count + 1);\n      name = `${name}_${count}`;\n    }\n\n    return name;\n  }\n}\n\n// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_words\n// Includes JavaScript reserved keywords and global objects to prevent variable shadowing\nconst JS_RESERVED = new Set([\n  // Reserved keywords\n  \"break\",\n  \"case\",\n  \"catch\",\n  \"class\",\n  \"const\",\n  \"continue\",\n  \"debugger\",\n  \"default\",\n  \"delete\",\n  \"do\",\n  \"else\",\n  \"export\",\n  \"extends\",\n  \"false\",\n  \"finally\",\n  \"for\",\n  \"function\",\n  \"if\",\n  \"import\",\n  \"in\",\n  \"instanceof\",\n  \"new\",\n  \"null\",\n  \"return\",\n  \"super\",\n  \"switch\",\n  \"this\",\n  \"throw\",\n  \"true\",\n  \"try\",\n  \"typeof\",\n  \"var\",\n  \"void\",\n  \"while\",\n  \"with\",\n  \"let\",\n  \"static\",\n  \"yield\",\n  \"await\",\n  \"enum\",\n  \"implements\",\n  \"interface\",\n  \"package\",\n  \"private\",\n  \"protected\",\n  \"public\",\n  \"abstract\",\n  \"boolean\",\n  \"byte\",\n  \"char\",\n  \"double\",\n  \"final\",\n  \"float\",\n  \"goto\",\n  \"int\",\n  \"long\",\n  \"native\",\n  \"short\",\n  \"synchronized\",\n  \"throws\",\n  \"transient\",\n  \"volatile\",\n  \"arguments\",\n  \"as\",\n  \"async\",\n  \"eval\",\n  \"from\",\n  \"get\",\n  \"of\",\n  \"set\",\n  // JavaScript built-in objects that could cause shadowing bugs\n  \"Array\",\n  \"ArrayBuffer\",\n  \"Boolean\",\n  \"DataView\",\n  \"Date\",\n  \"Error\",\n  \"EvalError\",\n  \"Float32Array\",\n  \"Float64Array\",\n  \"Function\",\n  \"Infinity\",\n  \"Int8Array\",\n  \"Int16Array\",\n  \"Int32Array\",\n  \"Intl\",\n  \"JSON\",\n  \"Map\",\n  \"Math\",\n  \"NaN\",\n  \"Number\",\n  \"Object\",\n  \"Promise\",\n  \"Proxy\",\n  \"RangeError\",\n  \"ReferenceError\",\n  \"Reflect\",\n  \"RegExp\",\n  \"Set\",\n  \"String\",\n  \"Symbol\",\n  \"SyntaxError\",\n  \"TypeError\",\n  \"Uint8Array\",\n  \"Uint8ClampedArray\",\n  \"Uint16Array\",\n  \"Uint32Array\",\n  \"URIError\",\n  \"WeakMap\",\n  \"WeakSet\",\n  \"BigInt\",\n  \"BigInt64Array\",\n  \"BigUint64Array\",\n  // Web APIs commonly used in islands\n  \"console\",\n  \"fetch\",\n  \"Request\",\n  \"Response\",\n  \"Headers\",\n  \"URL\",\n  \"URLSearchParams\",\n  \"Event\",\n  \"EventTarget\",\n  \"AbortController\",\n  \"AbortSignal\",\n  \"FormData\",\n  \"Blob\",\n  \"File\",\n  \"FileReader\",\n  \"TextEncoder\",\n  \"TextDecoder\",\n  \"ReadableStream\",\n  \"WritableStream\",\n  \"TransformStream\",\n  \"WebSocket\",\n  \"Worker\",\n  \"MessageChannel\",\n  \"MessagePort\",\n  \"BroadcastChannel\",\n  \"crypto\",\n  \"atob\",\n  \"btoa\",\n  \"setTimeout\",\n  \"setInterval\",\n  \"clearTimeout\",\n  \"clearInterval\",\n  \"queueMicrotask\",\n  \"structuredClone\",\n  // Browser-specific globals\n  \"document\",\n  \"window\",\n  \"navigator\",\n  \"location\",\n  \"history\",\n  \"localStorage\",\n  \"sessionStorage\",\n  // Deno-specific globals\n  \"Deno\",\n  // Node.js compatibility globals (for Deno's Node compat mode)\n  \"process\",\n  \"global\",\n  \"Buffer\",\n]);\n\nconst PATH_TO_SPEC = /[\\\\/]+/g;\nexport function pathToSpec(outDir: string, spec: string): string {\n  const outDirUrl = pathToFileUrl(outDir);\n\n  if (\n    spec.startsWith(\"http:\") || spec.startsWith(\"https:\") ||\n    spec.startsWith(\"jsr:\")\n  ) {\n    return spec;\n  } else if (spec.startsWith(\"file://\")) {\n    const fileUrl = pathToFileUrl(spec);\n    spec = relativeUrl(outDirUrl, fileUrl);\n    return maybeDot(spec);\n  }\n\n  spec = path.normalize(spec);\n  if (path.isAbsolute(spec)) {\n    const fileUrl = pathToFileUrl(spec);\n    spec = relativeUrl(outDirUrl, fileUrl);\n    return maybeDot(spec);\n  }\n\n  spec = spec.replaceAll(PATH_TO_SPEC, \"/\");\n  if (spec.startsWith(\"/\")) {\n    const fileUrl = pathToFileUrl(spec);\n    spec = relativeUrl(outDirUrl, fileUrl);\n    return maybeDot(spec);\n  }\n\n  spec = maybeDot(spec);\n  return spec;\n}\n\nfunction maybeDot(spec: string): string {\n  return spec.startsWith(\".\") ? spec : `./${spec}`;\n}\n\nexport function isLazy<T>(value: MaybeLazy<T>): value is Lazy<T> {\n  return typeof value === \"function\";\n}\n"
  },
  {
    "path": "packages/fresh/src/utils_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport {\n  escapeScript,\n  pathToExportName,\n  pathToSpec,\n  UniqueNamer,\n} from \"./utils.ts\";\n\nDeno.test(\"filenameToExportName\", () => {\n  expect(pathToExportName(\"/islands/foo.tsx\")).toBe(\"foo\");\n  expect(pathToExportName(\"/islands/foo.v2.tsx\")).toBe(\"foo_v2\");\n  expect(pathToExportName(\"/islands/nav-bar.tsx\")).toBe(\"nav_bar\");\n  expect(pathToExportName(\"/islands/_.$bar.tsx\")).toBe(\"_$bar\");\n  expect(pathToExportName(\"/islands/1.hello.tsx\")).toBe(\"_hello\");\n  expect(pathToExportName(\"/islands/collapse...repeat_-dash.tsx\")).toBe(\n    \"collapse_repeat_dash\",\n  );\n});\n\nDeno.test(\"escapeScriptContent - closing </script> + </style>\", () => {\n  expect(\n    escapeScript(\n      \"foo</script>bar</style>baz</script>foobar\",\n    ),\n  ).toEqual(\n    \"foo<\\\\/script>bar<\\\\/style>baz<\\\\/script>foobar\",\n  );\n  expect(\n    escapeScript(\n      \"foo</ScRIpT>bar</StYLe>baz</SCRIPT>foobar\",\n    ),\n  ).toEqual(\n    \"foo<\\\\/ScRIpT>bar<\\\\/StYLe>baz<\\\\/SCRIPT>foobar\",\n  );\n});\n\nDeno.test(\"escapeScript - legacy <!--\", () => {\n  expect(escapeScript(\"<!--<script>\")).toEqual(\"\\\\x3C!--<script>\");\n  expect(escapeScript(\"<!--</script>\")).toEqual(\"\\\\x3C!--<\\\\/script>\");\n});\n\nDeno.test(\"escapeScript - script combined\", () => {\n  expect(escapeScript(`// This is a comment containing <!--\nlet foo = x <!--y; // That's valid JS operators\nconst s = \"This is a string containing <!--\";`)).toEqual(\n    `// This is a comment containing \\\\x3C!--\nlet foo = x \\\\x3C!--y; // That's valid JS operators\nconst s = \"This is a string containing \\\\x3C!--\";`,\n  );\n});\n\nDeno.test(\"escapeScript - json\", () => {\n  expect(escapeScript(\"<!--<script>\", { json: true })).toEqual(\n    \"\\\\u003C!--<script>\",\n  );\n  expect(escapeScript(\"<!--</ScRIpt>\", { json: true })).toEqual(\n    \"\\\\u003C!--<\\\\/ScRIpt>\",\n  );\n});\n\nDeno.test(\"pathToSpec\", () => {\n  expect(pathToSpec(\"/foo\", \"/foo/bar\")).toEqual(\"./bar\");\n  expect(pathToSpec(\"/\", \"/foo//bar\")).toEqual(\"./foo/bar\");\n  expect(pathToSpec(\"/foo\", \"bar\")).toEqual(\"./bar\");\n  expect(pathToSpec(\"/foo/bar\", \"/foo/baz\")).toEqual(\"../baz\");\n  expect(pathToSpec(\"/foo\", \"file:///foo//bar\")).toEqual(\"./bar\");\n  expect(pathToSpec(\"/foo\", \"http://example.com\")).toEqual(\n    \"http://example.com\",\n  );\n  expect(pathToSpec(\"/foo\", \"https://example.com\")).toEqual(\n    \"https://example.com\",\n  );\n  expect(pathToSpec(\"/foo\", \"jsr:@foo/bar\")).toEqual(\"jsr:@foo/bar\");\n});\n\nDeno.test(\"UniqueNamer - generates unique name for same names\", () => {\n  const namer = new UniqueNamer();\n  expect(namer.getUniqueName(\"foo\")).toEqual(\"foo\");\n  expect(namer.getUniqueName(\"foo\")).toEqual(\"foo_1\");\n});\n\nDeno.test(\"UniqueNamer - accounts for JS syntax\", () => {\n  const namer = new UniqueNamer();\n  expect(namer.getUniqueName(\"foo-bar\")).toEqual(\"foo_bar\");\n  expect(namer.getUniqueName(\"switch\")).toEqual(\"_switch\");\n  expect(namer.getUniqueName(\"for\")).toEqual(\"_for\");\n  expect(namer.getUniqueName(\"0foo\")).toEqual(\"_0foo\");\n  expect(namer.getUniqueName(\"0foo$_🔥\")).toEqual(\"_0foo$__\");\n});\n"
  },
  {
    "path": "packages/fresh/tests/active_links_test.tsx",
    "content": "import { App, staticFiles } from \"fresh\";\nimport {\n  ALL_ISLAND_DIR,\n  assertNotSelector,\n  assertSelector,\n  buildProd,\n  Doc,\n  parseHtml,\n  withBrowserApp,\n} from \"./test_utils.tsx\";\n\nimport { FakeServer } from \"../src/test_utils.ts\";\nimport { Partial } from \"fresh/runtime\";\n\nconst allIslandCache = await buildProd({ islandDir: ALL_ISLAND_DIR });\n\nfunction testApp<T>(): App<T> {\n  const app = new App<T>()\n    .use(staticFiles());\n\n  allIslandCache(app);\n  return app;\n}\n\nDeno.test({\n  name: \"active links - without client nav\",\n  fn: async () => {\n    function View() {\n      return (\n        <Doc>\n          <div>\n            <h1>nav</h1>\n            <p>\n              <a href=\"/active_nav/foo/bar\">/active_nav/foo/bar</a>\n            </p>\n            <p>\n              <a href=\"/active_nav/foo\">/active_nav/foo</a>\n            </p>\n            <p>\n              <a href=\"/active_nav\">/active_nav</a>\n            </p>\n            <p>\n              <a href=\"/\">/</a>\n            </p>\n          </div>\n        </Doc>\n      );\n    }\n\n    const app = testApp()\n      .get(\"/active_nav/foo\", (ctx) => {\n        return ctx.render(<View />);\n      })\n      .get(\"/active_nav\", (ctx) => {\n        return ctx.render(<View />);\n      });\n\n    const server = new FakeServer(app.handler());\n    let res = await server.get(\"/active_nav\");\n    let doc = parseHtml(await res.text());\n\n    assertSelector(doc, \"a[href='/'][data-ancestor]\");\n\n    // Current\n    assertNotSelector(doc, \"a[href='/active_nav'][data-ancestor]\");\n    assertSelector(doc, \"a[href='/active_nav'][data-current]\");\n    assertSelector(doc, `a[href='/active_nav'][aria-current=\"page\"]`);\n\n    // Unrelated links\n    assertNotSelector(doc, \"a[href='/active_nav/foo'][data-ancestor]\");\n    assertNotSelector(doc, \"a[href='/active_nav/foo'][aria-current]\");\n    assertNotSelector(doc, \"a[href='/active_nav/foo/bar'][data-ancestor]\");\n    assertNotSelector(doc, \"a[href='/active_nav/foo/bar'][aria-current]\");\n\n    res = await server.get(`/active_nav/foo`);\n    doc = parseHtml(await res.text());\n    assertSelector(doc, \"a[href='/active_nav/foo'][data-current]\");\n    assertSelector(doc, `a[href='/active_nav/foo'][aria-current=\"page\"]`);\n    assertSelector(doc, \"a[href='/active_nav'][data-ancestor]\");\n    assertSelector(doc, `a[href='/active_nav'][aria-current=\"true\"]`);\n    assertSelector(doc, \"a[href='/'][data-ancestor]\");\n    assertSelector(doc, `a[href='/'][aria-current=\"true\"]`);\n  },\n});\n\nDeno.test({\n  name: \"active links - updates outside of vdom\",\n  fn: async () => {\n    function PartialPage() {\n      return (\n        <div>\n          <Partial name=\"content\">\n            <h1>/active_nav_partial</h1>\n          </Partial>\n          <p>\n            <a href=\"/active_nav_partial/foo/bar\">\n              /active_nav_partial/foo/bar\n            </a>\n          </p>\n          <p>\n            <a href=\"/active_nav_partial/foo\">/active_nav_partial/foo</a>\n          </p>\n          <p>\n            <a href=\"/active_nav_partial\">/active_nav_partial</a>\n          </p>\n          <p>\n            <a href=\"/\">/</a>\n          </p>\n        </div>\n      );\n    }\n\n    const app = testApp()\n      .get(\"/active_nav_partial/foo\", (ctx) => {\n        return ctx.render(<PartialPage />);\n      })\n      .get(\"/active_nav_partial\", (ctx) => {\n        return ctx.render(<PartialPage />);\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/active_nav_partial`);\n\n      let doc = parseHtml(await page.content());\n      assertSelector(doc, \"a[href='/'][data-ancestor]\");\n\n      // Current\n      assertNotSelector(doc, \"a[href='/active_nav_partial'][data-ancestor]\");\n      assertSelector(doc, \"a[href='/active_nav_partial'][data-current]\");\n      assertSelector(doc, `a[href='/active_nav_partial'][aria-current=\"page\"]`);\n\n      // Unrelated links\n      assertNotSelector(\n        doc,\n        \"a[href='/active_nav_partial/foo'][data-ancestor]\",\n      );\n      assertNotSelector(\n        doc,\n        \"a[href='/active_nav_partial/foo'][aria-current]\",\n      );\n      assertNotSelector(\n        doc,\n        \"a[href='/active_nav_partial/foo/bar'][data-ancestor]\",\n      );\n      assertNotSelector(\n        doc,\n        \"a[href='/active_nav_partial/foo/bar'][aria-current]\",\n      );\n\n      await page.goto(`${address}/active_nav_partial/foo`);\n      doc = parseHtml(await page.content());\n      assertSelector(doc, \"a[href='/active_nav_partial/foo'][data-current]\");\n      assertSelector(\n        doc,\n        `a[href='/active_nav_partial/foo'][aria-current=\"page\"]`,\n      );\n      assertSelector(doc, \"a[href='/active_nav_partial'][data-ancestor]\");\n      assertSelector(\n        doc,\n        `a[href='/active_nav_partial'][data-ancestor][aria-current=\"true\"]`,\n      );\n      assertSelector(doc, \"a[href='/'][data-ancestor]\");\n      assertSelector(doc, `a[href='/'][aria-current=\"true\"]`);\n    });\n  },\n});\n"
  },
  {
    "path": "packages/fresh/tests/doc_examples_test.tsx",
    "content": "import twDenoJson from \"../../plugin-tailwindcss/deno.json\" with {\n  type: \"json\",\n};\nimport * as Marked from \"marked\";\nimport { ensureDir, walk } from \"@std/fs\";\nimport { dirname, join, relative } from \"@std/path\";\n// import { expect } from \"@std/expect/expect\";\nimport { withTmpDir } from \"../src/test_utils.ts\";\nimport { FRESH_VERSION, PREACT_VERSION } from \"../../update/src/update.ts\";\n\nDeno.test(\"Docs Code example checks\", async () => {\n  await using tmp = await withTmpDir();\n\n  for await (const { path, code } of docsMarkdownFiles()) {\n    const codePath = join(tmp.dir, path);\n    await ensureDir(dirname(codePath));\n    await Deno.writeTextFile(codePath, code);\n  }\n\n  const denoJson = {\n    lock: false,\n    imports: {\n      fresh: `jsr:@fresh/core@${FRESH_VERSION}`,\n      \"@fresh/plugin-tailwind-v3\":\n        `jsr:@fresh/plugin-tailwind@^${twDenoJson.version}`,\n      \"@fresh/plugin-tailwind\":\n        `jsr:@fresh/plugin-tailwind@^${twDenoJson.version}`,\n      preact: `npm:preact@^${PREACT_VERSION}`,\n      \"@deno/gfm\": \"jsr:@deno/gfm@^0.11.0\",\n      \"@std/expect\": \"jsr:@std/expect@^1.0.16\",\n    },\n    compilerOptions: {\n      lib: [\"dom\", \"dom.asynciterable\", \"deno.ns\", \"deno.unstable\"],\n      jsx: \"precompile\",\n      jsxImportSource: \"preact\",\n      jsxPrecompileSkipElements: [\n        \"a\",\n        \"img\",\n        \"source\",\n        \"body\",\n        \"html\",\n        \"head\",\n        \"title\",\n        \"meta\",\n        \"script\",\n        \"link\",\n        \"style\",\n        \"base\",\n        \"noscript\",\n        \"template\",\n      ],\n    },\n  };\n  await Deno.writeTextFile(\n    join(tmp.dir, \"deno.json\"),\n    JSON.stringify(denoJson, undefined, 2),\n  );\n\n  // Download and cache all dependencies (reduces `stdout` noise)\n  await new Deno.Command(Deno.execPath(), {\n    args: [\"cache\", \"**/*\"],\n    cwd: tmp.dir,\n  }).output();\n\n  const { stdout, stderr } = await new Deno.Command(Deno.execPath(), {\n    args: [\"check\", \"**/*\"],\n    cwd: tmp.dir,\n  }).output();\n\n  const decoder = new TextDecoder();\n  const output = `${decoder.decode(stdout)}\\n${decoder.decode(stderr)}`;\n  // Log `deno check` output (can be removed if expects below are enabled)\n  // deno-lint-ignore no-console\n  console.log(output);\n\n  // TODO: Enable after fixing docs check issues\n  // expect(code).toBe(0);\n  // expect(output).toBe(\"\");\n});\n\nasync function* docsMarkdownFiles() {\n  // Limit to checking Fresh v2 (canary) docs for now\n  const docsDir = join(\n    import.meta.dirname!,\n    \"..\",\n    \"..\",\n    \"..\",\n    \"docs\",\n    \"canary\",\n  );\n  const docsIter = walk(docsDir, { exts: [\".md\"], includeDirs: false });\n\n  for await (const entry of docsIter) {\n    for (const { file, code } of await extractTsCode(entry.path)) {\n      const path = join(relative(docsDir, entry.path), file);\n      yield { path, code };\n    }\n  }\n}\n\nasync function extractTsCode(path: string) {\n  const code: { file: string; code: string }[] = [];\n  const input = await Deno.readTextFile(path);\n  let index = 0;\n\n  const tokens = await Marked.lexer(input, { gfm: true, async: true });\n\n  Marked.walkTokens(tokens, (token) => {\n    // Get rid of `Marked.Tokens.Generic`\n    const t = token as Marked.MarkedToken;\n    if (t.type !== \"code\") return;\n\n    const result = /^([tj]sx?)\\s*/.exec(t.lang ?? \"\");\n    if (!result) return;\n\n    // Codeblock must be TS/JS\n    index += 1;\n\n    // Probably a filename, but perhaps a title,\n    // so get rid of non-file safe characters\n    const [match, ext] = result;\n    let file = t.lang!.slice(match.length)\n      .toLocaleLowerCase()\n      .replaceAll(/[^a-z0-9._\\-\\/\\\\]/g, \"_\")\n      .replaceAll(/_{2,}/g, \"_\") || String(index);\n\n    if (!file.endsWith(ext)) file += `.${ext}`;\n\n    code.push({ file, code: t.text });\n  });\n\n  return code;\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_head/islands/MetaIsland.tsx",
    "content": "import { useEffect, useState } from \"preact/hooks\";\nimport { Head } from \"fresh/runtime\";\n\nexport function MetaIsland() {\n  const [ready, setReady] = useState(false);\n\n  useEffect(() => {\n    setReady(true);\n  }, []);\n\n  return (\n    <div class={ready ? \"ready\" : \"\"}>\n      <Head>\n        <meta name=\"foo\" content=\"ok\" />\n      </Head>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_head/islands/StyleIdIsland.tsx",
    "content": "import { useEffect, useState } from \"preact/hooks\";\nimport { Head } from \"fresh/runtime\";\n\nexport function StyleIdIsland() {\n  const [ready, setReady] = useState(false);\n\n  useEffect(() => {\n    setReady(true);\n  }, []);\n\n  return (\n    <div class={ready ? \"ready\" : \"\"}>\n      <Head>\n        <style id=\"style-id\">ok</style>\n      </Head>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_head/islands/TemplateIsland.tsx",
    "content": "import { useEffect, useState } from \"preact/hooks\";\nimport { Head } from \"fresh/runtime\";\n\nexport function TemplateIsland() {\n  const [ready, setReady] = useState(false);\n\n  useEffect(() => {\n    setReady(true);\n  }, []);\n\n  return (\n    <div class={ready ? \"ready\" : \"\"}>\n      <Head>\n        <template key=\"a\">ok</template>\n      </Head>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_head/islands/TitleIsland.tsx",
    "content": "import { useEffect, useState } from \"preact/hooks\";\nimport { Head } from \"fresh/runtime\";\n\nexport function TitleIsland() {\n  const [s, set] = useState(0);\n  const [ready, setReady] = useState(false);\n\n  useEffect(() => {\n    setReady(true);\n  }, []);\n\n  return (\n    <div class={ready ? \"ready\" : \"\"}>\n      <Head>\n        <title>Count: {s}</title>\n      </Head>\n      <button type=\"button\" onClick={() => set((v) => v + 1)}>\n        update {s}\n      </button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_head/routes/_app.tsx",
    "content": "import type { PageProps } from \"@fresh/core\";\n\nexport default function Page({ Component }: PageProps) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charset=\"utf-8\" />\n        <title>not ok</title>\n        <meta name=\"foo\" content=\"not ok\" />\n        <meta name=\"bar\" content=\"not ok\" />\n        <style>not ok</style>\n        <style id=\"style-id\">not ok</style>\n        <template key=\"a\">not ok</template>\n        <template key=\"b\">not ok</template>\n        <template>not ok</template>\n      </head>\n      <body>\n        <Component />\n      </body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_head/routes/id.tsx",
    "content": "import { StyleIdIsland } from \"../islands/StyleIdIsland.tsx\";\n\nexport default function Page() {\n  return <StyleIdIsland />;\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_head/routes/key.tsx",
    "content": "import { TemplateIsland } from \"../islands/TemplateIsland.tsx\";\n\nexport default function Page() {\n  return <TemplateIsland />;\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_head/routes/meta.tsx",
    "content": "import { MetaIsland } from \"../islands/MetaIsland.tsx\";\n\nexport default function Page() {\n  return <MetaIsland />;\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_head/routes/title.tsx",
    "content": "import { TitleIsland } from \"../islands/TitleIsland.tsx\";\n\nexport default function Page() {\n  return <TitleIsland />;\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_island_groups/routes/both/(_islands)/Foo.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport function Foo() {\n  const active = useSignal(false);\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div id=\"foo-both\" class={active.value ? \"ready\" : \"\"}>\n      {active.value ? \"it works\" : \"it doesn't work\"}\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_island_groups/routes/both/index.tsx",
    "content": "import { Foo } from \"./(_islands)/Foo.tsx\";\nimport { Foo as Foo2 } from \"../foo/(_islands)/Foo.tsx\";\n\nexport default function Home() {\n  return (\n    <div>\n      <Foo />\n      <Foo2 />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_island_groups/routes/foo/(_islands)/Foo.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport function Foo() {\n  const active = useSignal(false);\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div id=\"foo\" class={active.value ? \"ready\" : \"\"}>\n      {active.value ? \"it works\" : \"it doesn't work\"}\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_island_groups/routes/foo/index.tsx",
    "content": "import { Foo } from \"./(_islands)/Foo.tsx\";\n\nexport default function Home() {\n  return (\n    <div>\n      <Foo />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_island_groups/routes/index.tsx",
    "content": "export default function Home() {\n  return <h1>hello world</h1>;\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_precompile/invalid/deno.json",
    "content": "{\n  \"workspace\": [],\n  \"lock\": false,\n  \"links\": [\"../../..\"],\n  \"compilerOptions\": {\n    \"jsx\": \"precompile\",\n    \"jsxImportSource\": \"preact\"\n  }\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_precompile/invalid/dev.ts",
    "content": "import { Builder } from \"../../../src/dev/mod.ts\";\n\nconst builder = new Builder();\n\nawait builder.listen(() => import(\"./main.tsx\"), {\n  port: 4001,\n});\n"
  },
  {
    "path": "packages/fresh/tests/fixture_precompile/invalid/main.tsx",
    "content": "import { App } from \"../../../src/app.ts\";\n\nexport const app = new App().get(\n  \"/\",\n  () => new Response(\"hello\"),\n);\n"
  },
  {
    "path": "packages/fresh/tests/fixture_precompile/valid/deno.json",
    "content": "{\n  \"workspace\": [],\n  \"lock\": false,\n  \"links\": [\"../../..\"],\n  \"compilerOptions\": {\n    \"jsx\": \"precompile\",\n    \"jsxImportSource\": \"preact\",\n    \"jsxPrecompileSkipElements\": [\n      \"a\",\n      \"img\",\n      \"source\",\n      \"body\",\n      \"html\",\n      \"head\",\n      \"title\",\n      \"meta\",\n      \"script\",\n      \"link\",\n      \"style\",\n      \"base\",\n      \"noscript\",\n      \"template\"\n    ]\n  }\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixture_precompile/valid/main.tsx",
    "content": "import { App } from \"../../../src/app.ts\";\n\nconst app = new App().get(\n  \"/\",\n  (ctx) =>\n    ctx.render(\n      <html>\n        <head>\n          <meta charset=\"utf-8\" />\n          <title>foo</title>\n        </head>\n        <body>\n          <div f-client-nav>\n            <span f-client-nav={false}>\n              <p>false</p>\n            </span>\n            <a href=\"/\">Home</a>\n            <img src=\"/foo.jpg\" alt=\"\" />\n            <picture>\n              <source src=\"/bar.jpg\" />\n            </picture>\n          </div>\n        </body>\n      </html>,\n    ),\n);\n\nconst handler = app.handler();\nconst res = await handler(new Request(\"http://localhost/\"));\n// deno-lint-ignore no-console\nconsole.log(await res.text());\n"
  },
  {
    "path": "packages/fresh/tests/fixture_update_check/mod.ts",
    "content": "import { DAY } from \"../../src/constants.ts\";\nimport { updateCheck } from \"../../src/dev/update_check.ts\";\n\n// deno-lint-ignore require-await\nasync function getLatestVersion() {\n  // deno-lint-ignore no-console\n  console.log(\"fetching latest version\");\n  return Deno.env.get(\"LATEST_VERSION\") ?? \"99.99.999\";\n}\n\nfunction getCurrentVersion() {\n  return Deno.env.get(\"CURRENT_VERSION\")!;\n}\n\nconst interval = +(Deno.env.get(\"UPDATE_INTERVAL\") ?? DAY);\nawait updateCheck(\n  interval,\n  () => Deno.env.get(\"TEST_HOME\")!,\n  getLatestVersion,\n  Deno.env.has(\"CURRENT_VERSION\") ? getCurrentVersion : undefined,\n);\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/Computed.tsx",
    "content": "import { useComputed, useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport function ComputedSignal(props: { id?: string }) {\n  const status = useSignal(\"it doesn't work\");\n  const c = useComputed(() => 1);\n\n  const active = useSignal(false);\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div id={props.id} class={active.value ? \"ready\" : \"\"}>\n      <button\n        type=\"button\"\n        class=\"trigger\"\n        onClick={() => {\n          try {\n            // This should throw\n            // deno-lint-ignore no-explicit-any\n            (c as any).value = 10;\n            status.value = \"it doesn't work\";\n          } catch {\n            status.value = \"it works\";\n          }\n        }}\n      >\n        trigger\n      </button>\n      <p class=\"output\">{status}</p>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/Counter.tsx",
    "content": "import { type Signal, useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport interface CounterProps {\n  id?: string;\n  count: Signal<number>;\n}\n\nexport function Counter(props: CounterProps) {\n  const active = useSignal(false);\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div id={props.id} class={active.value ? \"ready\" : \"\"}>\n      <button\n        type=\"button\"\n        class=\"decrement\"\n        onClick={() => props.count.value -= 1}\n      >\n        -1\n      </button>\n      <p class=\"output\">{props.count}</p>\n      <button\n        type=\"button\"\n        class=\"increment\"\n        onClick={() => props.count.value += 1}\n      >\n        +1\n      </button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/CounterWithSlots.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\nimport type { ComponentChildren } from \"preact\";\n\nexport function CounterWithSlots(\n  props: { children?: ComponentChildren; jsx?: ComponentChildren },\n) {\n  const sig = useSignal(0);\n  const active = useSignal(false);\n\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div class={active.value ? \"ready\" : \"\"}>\n      <div class=\"counter-with-children\">\n        <p class=\"output\">{sig}</p>\n        <button type=\"button\" onClick={() => sig.value = sig.peek() + 1}>\n          update\n        </button>\n      </div>\n      <div class=\"children\">\n        {props.children}\n      </div>\n      <div class=\"jsx\">\n        {props.jsx}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/EnvIsland.tsx",
    "content": "// deno-lint-ignore-file no-process-global\nimport { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport interface EnvIslandProps {\n  id?: string;\n}\n\nexport function EnvIsland(props: EnvIslandProps) {\n  const active = useSignal(false);\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  const deno = Deno.env.get(\"FRESH_PUBLIC_TEST_FOO\");\n  const deno2 = Deno.env.get(\"FRESH_PUBLIC_TEST_FOO\");\n  // @ts-ignore f\n  const processEnv = process.env.FRESH_PUBLIC_TEST_FOO;\n\n  let denoPrivateEnv = \"not ok\";\n  try {\n    Deno.env.get(\"FRESH_PRIVATE_TEST_FOO\");\n  } catch {\n    denoPrivateEnv = \"ok\";\n  }\n\n  let denoPrivateEnv2 = \"not ok\";\n  try {\n    Deno.env.get(\"FRESH_PRIVATE_TEST_FOO\");\n  } catch {\n    denoPrivateEnv2 = \"ok\";\n  }\n\n  let processEnvPrivate = \"not ok\";\n  try {\n    process.env.FRESH_PRIVATE_TEST_FOO;\n  } catch {\n    processEnvPrivate = \"ok\";\n  }\n\n  return (\n    <div id={props.id} class={active.value ? \"ready\" : \"\"}>\n      <pre class=\"deno-env\">{deno}</pre>\n      <pre class=\"deno-env-2\">{deno2}</pre>\n      <pre class=\"process-env\">{processEnv}</pre>\n\n      <pre class=\"deno-env-private\">{denoPrivateEnv}</pre>\n      <pre class=\"deno-env-private-2\">{denoPrivateEnv2}</pre>\n      <pre class=\"process-env-private\">{processEnvPrivate}</pre>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/EscapeIsland.tsx",
    "content": "import { useEffect } from \"preact/hooks\";\nimport { useSignal } from \"@preact/signals\";\n\nexport function EscapeIsland(props: { str: string }) {\n  const active = useSignal(false);\n\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div class={active.value ? \"ready\" : \"\"}>\n      <p>it works</p>\n      <div>{props.str}</div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/FnIsland.tsx",
    "content": "import type { VNode } from \"preact\";\n\nimport { FragmentIsland } from \"./FragmentIsland.tsx\";\nimport { useEffect } from \"preact/hooks\";\nimport { useSignal } from \"@preact/signals\";\n\nfunction Foo(props: { children: () => VNode }) {\n  return props.children();\n}\n\nexport function FnIsland() {\n  const active = useSignal(false);\n\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div class={active.value ? \"ready\" : \"\"}>\n      <Foo>\n        {() => <FragmentIsland />}\n      </Foo>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/FragmentIsland.tsx",
    "content": "// deno-lint-ignore-file jsx-curly-braces\nexport function FragmentIsland() {\n  return (\n    <>\n      <p>it{\" \"}</p>\n      <p>works</p>\n    </>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/FreshAttrs.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport interface FreshAttrs {\n  id?: string;\n}\n\nexport function FreshAttrs(props: FreshAttrs) {\n  const active = useSignal(false);\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div id={props.id} class={active.value ? \"ready\" : \"\"}>\n      <h1>Fresh attrs</h1>\n      <div class=\"f-client-nav-true\" f-client-nav>f-client-nav=true</div>\n      <div class=\"f-client-nav-false\" f-client-nav={false}>\n        f-client-nav=false\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/IslandInIsland.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { Counter } from \"./Counter.tsx\";\n\nexport function IslandInIsland() {\n  const sig = useSignal(0);\n\n  return (\n    <div>\n      <button\n        type=\"button\"\n        class=\"trigger\"\n        onClick={() => sig.value = sig.peek() + 1}\n      >\n        trigger\n      </button>\n      <Counter count={sig} />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/JsonIsland.tsx",
    "content": "import data from \"./data.json\" with { type: \"json\" };\n\nexport function JsonIsland() {\n  return <pre>{JSON.stringify(data)}</pre>;\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/JsxChildrenIsland.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\nimport type { ComponentChildren } from \"preact\";\n\nexport interface JsxIslandProps {\n  children?: ComponentChildren;\n}\n\nexport function JsxChildrenIsland(props: JsxIslandProps) {\n  const active = useSignal(false);\n\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div class={active.value ? \"ready\" : \"\"}>\n      <div class=\"children\">{props.children}</div>\n      {active.value ? <div class=\"after\">{props.children}</div> : null}\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/JsxConditional.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\nimport type { ComponentChildren } from \"preact\";\n\nexport interface JsxConditionalProps {\n  jsx?: ComponentChildren;\n  children?: ComponentChildren;\n}\n\nexport function JsxConditional(props: JsxConditionalProps) {\n  const active = useSignal(false);\n  const sig = useSignal(0);\n\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div class={active.value ? \"ready\" : \"\"}>\n      <p class=\"cond-output\">{sig}</p>\n      <button\n        type=\"button\"\n        class=\"cond-update\"\n        onClick={() => sig.value = sig.peek() + 1}\n      >\n        update\n      </button>\n      <div class=\"jsx\">{active.value ? props.jsx : null}</div>\n      <div class=\"children\">{active.value ? props.children : null}</div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/JsxIsland.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\nimport { type ComponentChildren, isValidElement } from \"preact\";\n\nexport interface JsxIslandProps {\n  jsx?: ComponentChildren;\n  children?: ComponentChildren;\n}\n\nexport function JsxIsland(props: JsxIslandProps) {\n  const active = useSignal(false);\n  const sig = useSignal(0);\n\n  useEffect(() => {\n    active.value = true;\n  }, []);\n  return (\n    <div class={active.value ? \"ready\" : \"\"}>\n      <p class=\"output\">{sig}</p>\n      <button\n        type=\"button\"\n        class=\"update\"\n        onClick={() => sig.value = sig.peek() + 1}\n      >\n        update\n      </button>\n      <div class=\"jsx\">{props.jsx}</div>\n      <div class=\"children\">{props.children}</div>\n      <pre>{JSON.stringify({jsx: isValidElement(props.jsx), children: isValidElement(props.children)})}</pre>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/Multiple.tsx",
    "content": "import type { ComponentChildren } from \"preact\";\nimport { Counter } from \"./Counter.tsx\";\nimport { useSignal } from \"@preact/signals\";\n\nexport interface MultipleProps {\n  id?: string;\n  children?: ComponentChildren;\n}\n\nexport function Multiple1(props: MultipleProps) {\n  const sig = useSignal(0);\n  return <Counter id={props.id} count={sig} />;\n}\nexport function Multiple2(props: MultipleProps) {\n  const sig = useSignal(0);\n  return <Counter id={props.id} count={sig} />;\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/NodeProcess.tsx",
    "content": "import { useEffect } from \"preact/hooks\";\nimport { useSignal } from \"@preact/signals\";\nimport { IS_BROWSER } from \"fresh/runtime\";\n\nexport function NodeProcess() {\n  const active = useSignal(false);\n\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  // @ts-ignore bundling\n  // deno-lint-ignore no-process-global\n  const value = IS_BROWSER ? process.env.NODE_ENV : \"no\";\n\n  return (\n    <div class={active.value ? \"ready\" : \"\"}>\n      value: {value}\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/NullIsland.tsx",
    "content": "import { useEffect } from \"preact/hooks\";\n\nexport function NullIsland() {\n  useEffect(() => {\n    const div = document.createElement(\"div\");\n    div.className = \"ready\";\n    document.body.appendChild(div);\n  }, []);\n  return null;\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/OptOutPartialLink.tsx",
    "content": "export function OptOutPartialLink(props: { href: string; partial: string }) {\n  return (\n    <div f-client-nav={false}>\n      <a class=\"update\" href={props.href} f-partial={props.partial}>link</a>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/PartialInIsland.tsx",
    "content": "import { Partial } from \"fresh/runtime\";\n\nexport function PartialInIsland() {\n  return (\n    <Partial name=\"invalid\">\n      <p class=\"invalid\">invalid</p>\n    </Partial>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/PassThrough.tsx",
    "content": "import type { ComponentChildren } from \"preact\";\n\nexport function PassThrough(props: { children?: ComponentChildren }) {\n  return (\n    <div class=\"passthrough\">\n      {props.children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/SelfCounter.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport interface SelfCounterProps {\n  id?: string;\n}\n\nexport function SelfCounter(props: SelfCounterProps) {\n  const count = useSignal(0);\n  const active = useSignal(false);\n\n  useEffect(() => {\n    active.value = true;\n  }, []);\n\n  return (\n    <div id={props.id} class={active.value ? \"ready self-counter\" : \"\"}>\n      <button type=\"button\" class=\"decrement\" onClick={() => count.value -= 1}>\n        -1\n      </button>\n      <p class=\"output\">{count}</p>\n      <button type=\"button\" class=\"increment\" onClick={() => count.value += 1}>\n        +1\n      </button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/fresh/tests/fixtures_islands/data.json",
    "content": "{\n  \"foo\": 123\n}\n"
  },
  {
    "path": "packages/fresh/tests/head_test.tsx",
    "content": "import { App, staticFiles } from \"fresh\";\nimport { Head } from \"fresh/runtime\";\nimport {\n  buildProd,\n  parseHtml,\n  waitFor,\n  withBrowserApp,\n} from \"./test_utils.tsx\";\nimport { expect } from \"@std/expect\";\nimport { FakeServer } from \"../src/test_utils.ts\";\nimport * as path from \"@std/path\";\n\nDeno.test(\"Head - ssr - updates title\", async () => {\n  const handler = new App()\n    .appWrapper(({ Component }) => {\n      return (\n        <html lang=\"en\">\n          <head>\n            <meta charset=\"utf-8\" />\n            <title>not ok</title>\n          </head>\n          <body>\n            <Component />\n          </body>\n        </html>\n      );\n    })\n    .get(\"/\", (ctx) => {\n      return ctx.render(\n        <>\n          <h1>heading</h1>\n          <Head>\n            <title>ok</title>\n          </Head>\n        </>,\n      );\n    }).handler();\n\n  const server = new FakeServer(handler);\n  const res = await server.get(\"/\");\n\n  const doc = parseHtml(await res.text());\n\n  expect(doc.querySelector(\"title\")?.textContent).toEqual(\"ok\");\n  expect(doc.querySelector(\"h1\")?.textContent).toEqual(\"heading\");\n});\n\nDeno.test(\"Head - ssr - updates meta\", async () => {\n  const handler = new App()\n    .appWrapper(({ Component }) => {\n      return (\n        <html lang=\"en\">\n          <head>\n            <meta charset=\"utf-8\" />\n            <title>ok</title>\n            <meta name=\"foo\" content=\"not ok\" />\n          </head>\n          <body>\n            <Component />\n          </body>\n        </html>\n      );\n    })\n    .get(\"/\", (ctx) => {\n      return ctx.render(\n        <>\n          <h1>heading</h1>\n          <Head>\n            <meta name=\"foo\" content=\"ok\" />\n          </Head>\n        </>,\n      );\n    }).handler();\n\n  const server = new FakeServer(handler);\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n\n  const meta = doc.querySelector(\"meta[name='foo']\") as HTMLMetaElement;\n  expect(meta.content).toEqual(\"ok\");\n});\n\nDeno.test(\"Head - ssr - updates style\", async () => {\n  const handler = new App()\n    .appWrapper(({ Component }) => {\n      return (\n        <html lang=\"en\">\n          <head>\n            <meta charset=\"utf-8\" />\n            <title>ok</title>\n            <meta name=\"foo\" content=\"not ok\" />\n          </head>\n          <body>\n            <Component />\n          </body>\n        </html>\n      );\n    })\n    .get(\"/\", (ctx) => {\n      return ctx.render(\n        <>\n          <h1>heading</h1>\n          <Head>\n            <style>ok</style>\n          </Head>\n        </>,\n      );\n    }).handler();\n\n  const server = new FakeServer(handler);\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n\n  const meta = doc.querySelector(\"style\") as HTMLStyleElement;\n  expect(meta.textContent).toEqual(\"ok\");\n});\n\nDeno.test(\"Head - ssr - merge keyed\", async () => {\n  const handler = new App()\n    .appWrapper(({ Component }) => {\n      return (\n        <html lang=\"en\">\n          <head>\n            <meta charset=\"utf-8\" />\n            <title>ok</title>\n            <style>not ok</style>\n            <style key=\"a\">not ok</style>\n          </head>\n          <body>\n            <Component />\n          </body>\n        </html>\n      );\n    })\n    .get(\"/\", (ctx) => {\n      return ctx.render(\n        <>\n          <h1>heading</h1>\n          <Head>\n            <style key=\"a\">ok</style>\n          </Head>\n        </>,\n      );\n    }).handler();\n\n  const server = new FakeServer(handler);\n  const res = await server.get(\"/\");\n  const doc = parseHtml(await res.text());\n\n  const last = doc.head.lastChild;\n  expect(last?.textContent).toEqual(\"ok\");\n});\n\nDeno.test({\n  ignore: true, // Temporarily until client perf is fixed\n  name: \"Head - client - set title\",\n  fn: async () => {\n    const applyCache = await buildProd({\n      root: path.join(import.meta.dirname!, \"fixture_head\"),\n    });\n\n    const app = new App({})\n      .use(staticFiles())\n      .fsRoutes();\n\n    applyCache(app);\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/title`);\n\n      await page.locator(\".ready\").wait();\n      await page.locator(\"button\").click();\n\n      await waitFor(async () => {\n        const title = await page.evaluate(() => document.title);\n        return title === \"Count: 1\";\n      });\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  ignore: true, // Temporarily until client perf is fixed\n  name: \"Head - client - match meta\",\n  fn: async () => {\n    const applyCache = await buildProd({\n      root: path.join(import.meta.dirname!, \"fixture_head\"),\n    });\n\n    const app = new App({})\n      .use(staticFiles())\n      .fsRoutes();\n\n    applyCache(app);\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/meta`);\n      await page.locator(\".ready\").wait();\n\n      await waitFor(async () => {\n        const metas = await page.evaluate(() => {\n          const metas = Array.from(\n            document.querySelectorAll(\"meta[name]\"),\n          ) as HTMLMetaElement[];\n\n          return metas.map((\n            el: HTMLMetaElement,\n          ) => ({ name: el.name, content: el.content }));\n        });\n\n        try {\n          expect(metas).toEqual([\n            { name: \"foo\", content: \"ok\" },\n            { name: \"bar\", content: \"not ok\" },\n          ]);\n          return true;\n        } catch {\n          return false;\n        }\n      });\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  ignore: true, // Temporarily until client perf is fixed\n  name: \"Head - client - match style by id\",\n  fn: async () => {\n    const applyCache = await buildProd({\n      root: path.join(import.meta.dirname!, \"fixture_head\"),\n    });\n\n    const app = new App({})\n      .use(staticFiles())\n      .fsRoutes();\n\n    applyCache(app);\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/id`);\n      await page.locator(\".ready\").wait();\n\n      await waitFor(async () => {\n        const styles = await page.evaluate(() => {\n          const els = Array.from(\n            document.querySelectorAll(\"head style\"),\n          ) as HTMLStyleElement[];\n\n          return els.map((\n            el: HTMLStyleElement,\n          ) => ({ id: el.id, text: el.textContent }));\n        });\n\n        try {\n          expect(styles).toEqual([\n            { id: \"\", text: \"not ok\" },\n            { id: \"style-id\", text: \"ok\" },\n          ]);\n          return true;\n        } catch {\n          return false;\n        }\n      });\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  ignore: true, // Temporarily until client perf is fixed\n  name: \"Head - client - match key\",\n  fn: async () => {\n    const applyCache = await buildProd({\n      root: path.join(import.meta.dirname!, \"fixture_head\"),\n    });\n\n    const app = new App({})\n      .use(staticFiles())\n      .fsRoutes();\n\n    applyCache(app);\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/key`);\n      await page.locator(\".ready\").wait();\n\n      await waitFor(async () => {\n        const tpls = await page.evaluate(() => {\n          const els = Array.from(\n            document.querySelectorAll(\"head template\"),\n          ) as HTMLTemplateElement[];\n\n          return els.map((\n            el: HTMLTemplateElement,\n          ) => ({\n            key: el.getAttribute(\"data-key\"),\n            text: el.content.textContent,\n          }));\n        });\n\n        try {\n          expect(tpls).toEqual([\n            { key: \"a\", text: \"ok\" },\n            { key: \"b\", text: \"not ok\" },\n            { key: null, text: \"not ok\" },\n          ]);\n          return true;\n        } catch {\n          return false;\n        }\n      });\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n"
  },
  {
    "path": "packages/fresh/tests/islands_test.tsx",
    "content": "import { App, staticFiles } from \"fresh\";\nimport { Counter } from \"./fixtures_islands/Counter.tsx\";\nimport { IslandInIsland } from \"./fixtures_islands/IslandInIsland.tsx\";\nimport { JsonIsland } from \"./fixtures_islands/JsonIsland.tsx\";\nimport { SelfCounter } from \"./fixtures_islands/SelfCounter.tsx\";\nimport { CounterWithSlots } from \"./fixtures_islands/CounterWithSlots.tsx\";\nimport { PassThrough } from \"./fixtures_islands/PassThrough.tsx\";\nimport { NullIsland } from \"./fixtures_islands/NullIsland.tsx\";\nimport { Multiple1, Multiple2 } from \"./fixtures_islands/Multiple.tsx\";\nimport { JsxIsland } from \"./fixtures_islands/JsxIsland.tsx\";\nimport { JsxChildrenIsland } from \"./fixtures_islands/JsxChildrenIsland.tsx\";\nimport { NodeProcess } from \"./fixtures_islands/NodeProcess.tsx\";\nimport { signal } from \"@preact/signals\";\nimport {\n  ALL_ISLAND_DIR,\n  buildProd,\n  Doc,\n  ISLAND_GROUP_DIR,\n  withBrowserApp,\n} from \"./test_utils.tsx\";\nimport { parseHtml, waitForText } from \"./test_utils.tsx\";\nimport { expect } from \"@std/expect\";\nimport { JsxConditional } from \"./fixtures_islands/JsxConditional.tsx\";\nimport { FnIsland } from \"./fixtures_islands/FnIsland.tsx\";\nimport { EscapeIsland } from \"./fixtures_islands/EscapeIsland.tsx\";\nimport type { FreshConfig } from \"../src/config.ts\";\nimport { FreshAttrs } from \"./fixtures_islands/FreshAttrs.tsx\";\nimport { FakeServer } from \"../src/test_utils.ts\";\nimport { PARTIAL_SEARCH_PARAM } from \"../src/constants.ts\";\nimport { ComputedSignal } from \"./fixtures_islands/Computed.tsx\";\nimport { EnvIsland } from \"./fixtures_islands/EnvIsland.tsx\";\n\nDeno.env.set(\"FRESH_PUBLIC_TEST_FOO\", \"test-env-value\");\nDeno.env.set(\"FRESH_PRIVATE_TEST_FOO\", \"i-should-not-be-visible\");\nconst allIslandCache = await buildProd({ islandDir: ALL_ISLAND_DIR });\nconst islandGroupCache = await buildProd({ root: ISLAND_GROUP_DIR });\n\nfunction testApp(config?: FreshConfig): App<unknown> {\n  const app = new App(config)\n    .use(staticFiles())\n    .fsRoutes();\n\n  allIslandCache(app);\n\n  return app;\n}\n\nfunction testGroupApp(config?: FreshConfig): App<unknown> {\n  const app = new App(config)\n    .use(staticFiles())\n    .fsRoutes();\n\n  islandGroupCache(app);\n\n  return app;\n}\n\nDeno.test({\n  name: \"islands - should make signals interactive\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        const sig = signal(3);\n        return ctx.render(\n          <Doc>\n            <Counter count={sig} />\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"4\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - revive multiple islands from one island file\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Multiple1 id=\"multiple-1\" />\n            <Multiple2 id=\"multiple-2\" />\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\"#multiple-1.ready\").wait();\n      await page.locator(\"#multiple-2.ready\").wait();\n      await page.locator(\"#multiple-1 .increment\").click();\n      await page.locator(\"#multiple-2 .increment\").click();\n      await waitForText(page, \"#multiple-1 .output\", \"1\");\n      await waitForText(page, \"#multiple-2 .output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - revive multiple islands with shared signal\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        const sig = signal(0);\n        return ctx.render(\n          <Doc>\n            <Counter id=\"counter-1\" count={sig} />\n            <Counter id=\"counter-2\" count={sig} />\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\"#counter-1.ready\").wait();\n      await page.locator(\"#counter-2.ready\").wait();\n      await page.locator(\"#counter-1 .increment\").click();\n      await waitForText(page, \"#counter-1 .output\", \"1\");\n      await waitForText(page, \"#counter-2 .output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  // Note that computed signals are detached because we cannot\n  // serialize the whole signal tree at the moment.\n  name: \"islands - should serialize computed\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <ComputedSignal id=\"comp\" />\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\"#comp.ready\").wait();\n      await page.locator(\"#comp .trigger\").click();\n      await waitForText(page, \"#comp .output\", \"it works\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - import json\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <JsonIsland />\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\"pre\").wait();\n      const text = await page\n        .locator<HTMLPreElement>(\"pre\")\n        .evaluate((el) => el.textContent!);\n      const json = JSON.parse(text);\n      expect(json).toEqual({ foo: 123 });\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - returns null\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <NullIsland />\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - only instantiate top level island\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <IslandInIsland />\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n      await page.locator(\".trigger\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      const html = await page.content();\n      expect(html).not.toContain(\"import { Counter }\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - pass null JSX props to islands\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <JsxIsland jsx={null}>{null}</JsxIsland>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      const html = await page.content();\n      const doc = parseHtml(html);\n      expect(doc.querySelector(\".jsx\")!.childNodes.length).toEqual(0);\n      expect(doc.querySelector(\".children\")!.childNodes.length).toEqual(0);\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - pass JSX props to islands\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <JsxIsland jsx={<p>foo</p>}>\n              <p>bar</p>\n            </JsxIsland>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      const text = await page\n        .locator<HTMLPreElement>(\"pre\")\n        .evaluate((el) => el.textContent!);\n      expect(JSON.parse(text)).toEqual({ jsx: true, children: true });\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - never serialize children prop\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <JsxChildrenIsland>\n              foobar\n            </JsxChildrenIsland>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      const text = await page\n        .locator<HTMLScriptElement>(\"script\")\n        .evaluate((el) => el.textContent!);\n      expect(text).not.toContain(\"foobar\");\n\n      const childText = await page\n        .locator<HTMLDivElement>(\".after\")\n        .evaluate((el) => el.textContent!);\n      expect(childText).toEqual(\"foobar\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - instantiate islands in jsx children\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <PassThrough>\n              <div>\n                <SelfCounter />\n              </div>\n            </PassThrough>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - instantiate islands in jsx children with slots\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <CounterWithSlots\n              jsx={\n                <div>\n                  <SelfCounter />\n                </div>\n              }\n            >\n              <div>\n                <SelfCounter />\n              </div>\n            </CounterWithSlots>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".jsx .increment\").click();\n      await page.locator(\".children .increment\").click();\n      await page.locator(\".counter-with-children button\").click();\n\n      await waitForText(page, \".counter-with-children .output\", \"1\");\n      await waitForText(page, \".jsx .output\", \"1\");\n      await waitForText(page, \".children .output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - nested children slots\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <PassThrough>\n              <PassThrough>\n                <div>\n                  <SelfCounter id=\"a\" />\n                </div>\n              </PassThrough>\n              <PassThrough>\n                <div>\n                  <SelfCounter id=\"b\" />\n                </div>\n              </PassThrough>\n            </PassThrough>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\"#a .increment\").click();\n      await page.locator(\"#b .increment\").click();\n\n      await waitForText(page, \"#a .output\", \"1\");\n      await waitForText(page, \"#b .output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - conditional jsx children\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <JsxConditional\n              jsx={\n                <div>\n                  <SelfCounter />\n                </div>\n              }\n            >\n              <div>\n                <SelfCounter />\n              </div>\n            </JsxConditional>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".jsx .increment\").click();\n      await page.locator(\".children .increment\").click();\n      await page.locator(\".cond-update\").click();\n\n      await waitForText(page, \".cond-output\", \"1\");\n      await waitForText(page, \".jsx .output\", \"1\");\n      await waitForText(page, \".children .output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - revive DOM attributes\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <JsxConditional>\n              <div class=\"foo\">\n                <form>\n                  <label for=\"check\">\n                    checked\n                  </label>\n                  <input name=\"check\" type=\"checkbox\" checked />\n                  <label for=\"text\">\n                    is required\n                  </label>\n                  <input name=\"text\" type=\"text\" required />\n                  <label for=\"foo-1\">\n                    not selected\n                  </label>\n                  <input id=\"foo-1\" type=\"radio\" name=\"foo\" value=\"1\" />\n                  <label for=\"foo-2\">\n                    selected\n                  </label>\n                  <input id=\"foo-2\" type=\"radio\" name=\"foo\" value=\"2\" checked />\n                  <label for=\"select\">\n                    select value should be \"bar\"\n                  </label>\n                  <select name=\"select\">\n                    <option value=\"foo\">foo</option>\n                    <option value=\"bar\" selected>bar</option>\n                  </select>\n                </form>\n                <SelfCounter />\n              </div>\n            </JsxConditional>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".children > .foo\").wait();\n\n      const checkboxChecked = await page\n        .locator<HTMLInputElement>(\"input[name='check']\")\n        .evaluate((el) => el.checked);\n      expect(checkboxChecked).toEqual(true);\n\n      const required = await page\n        .locator<HTMLInputElement>(\"input[name='text']\")\n        .evaluate((el) => el.required);\n      expect(required).toEqual(true);\n\n      const radio1 = await page\n        .locator<HTMLInputElement>(\"input[type='radio'][value='1']\")\n        .evaluate((el) => el.checked);\n      expect(radio1).toEqual(false);\n\n      const radio2 = await page\n        .locator<HTMLInputElement>(\"input[type='radio'][value='2']\")\n        .evaluate((el) => el.checked);\n      expect(radio2).toEqual(true);\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - revive island with fn inside\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <FnIsland />\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      const text = await page\n        .locator<HTMLDivElement>(\".ready\")\n        .evaluate((el) => el.textContent!);\n      expect(text).toEqual(\"it works\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - escape props\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <EscapeIsland str={`\"foo\"asdf`} />\n          </Doc>,\n        );\n      })\n      .get(\"/foo\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <EscapeIsland str={`<script>alert('hey')</script>`} />\n          </Doc>,\n        );\n      })\n      .get(\"/bar\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <EscapeIsland str={`<!--<script>alert('hey')</script>`} />\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      // Page would error here\n      const text = await page\n        .locator<HTMLDivElement>(\".ready p\")\n        .evaluate((el) => el.textContent!);\n      expect(text).toEqual(\"it works\");\n\n      // Page would error here\n      const text2 = await page\n        .locator<HTMLDivElement>(\".ready div\")\n        .evaluate((el) => el.textContent!);\n      expect(text2).toEqual(`\"foo\"asdf`);\n    });\n\n    // Check escaping of `</`\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/foo`, { waitUntil: \"load\" });\n\n      await page.locator(\".ready\").wait();\n\n      const text = await page\n        .locator<HTMLDivElement>(\".ready p\")\n        .evaluate((el) => el.textContent!);\n      expect(text).toEqual(\"it works\");\n\n      const text2 = await page\n        .locator<HTMLDivElement>(\".ready div\")\n        .evaluate((el) => el.textContent!);\n      expect(text2).toEqual(`<script>alert('hey')</script>`);\n    });\n\n    // Check escaping of `<!--`\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/bar`, { waitUntil: \"load\" });\n\n      await page.locator(\".ready\").wait();\n\n      const text = await page\n        .locator<HTMLDivElement>(\".ready p\")\n        .evaluate((el) => el.textContent!);\n      expect(text).toEqual(\"it works\");\n\n      const text2 = await page\n        .locator<HTMLDivElement>(\".ready div\")\n        .evaluate((el) => el.textContent!);\n      expect(text2).toEqual(`<!--<script>alert('hey')</script>`);\n    });\n\n    // Partials (they use a different code path)\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/foo?${PARTIAL_SEARCH_PARAM}`, {\n        waitUntil: \"load\",\n      });\n\n      const text = await page\n        .locator<HTMLScriptElement>(\"script[type='application/json']\")\n        .evaluate((el) => el.textContent!);\n\n      const json = JSON.parse(text);\n      expect(json.props).toContain(\"<script>alert('hey')</script>\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - stub Node 'process.env'\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) =>\n        ctx.render(\n          <Doc>\n            <NodeProcess />\n          </Doc>,\n        ));\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/`, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      // Page would error here\n      const text = await page\n        .locator<HTMLDivElement>(\".ready\")\n        .evaluate((el) => el.textContent!);\n      expect(text).toEqual(\"value: production\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - in base path\",\n  fn: async () => {\n    const app = testApp({ basePath: \"/foo\" })\n      .get(\"/\", (ctx) =>\n        ctx.render(\n          <Doc>\n            <SelfCounter />\n          </Doc>,\n        ));\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/foo`, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - preserve f-* attributes\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) =>\n        ctx.render(\n          <Doc>\n            <FreshAttrs />\n          </Doc>,\n        ));\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      const truthy = await page.locator<HTMLDivElement>(\".f-client-nav-true\")\n        .evaluate((el) => el.getAttribute(\"f-client-nav\"));\n      const falsy = await page.locator<HTMLDivElement>(\".f-client-nav-false\")\n        .evaluate((el) => el.getAttribute(\"f-client-nav\"));\n\n      expect(truthy).toEqual(\"true\");\n      expect(falsy).toEqual(\"false\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - inlines FRESH_PUBLIC_* env vars\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) =>\n        ctx.render(\n          <Doc>\n            <EnvIsland id=\"test\" />\n          </Doc>,\n        ));\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      const denoEnv = await page.locator<HTMLDivElement>(\".deno-env\")\n        .evaluate((el) => el.textContent);\n      const denoEnv2 = await page.locator<HTMLDivElement>(\".deno-env-2\")\n        .evaluate((el) => el.textContent);\n      const processEnv = await page.locator<HTMLDivElement>(\".process-env\")\n        .evaluate((el) => el.textContent);\n\n      const denoEnvPrivate = await page.locator<HTMLDivElement>(\n        \".deno-env-private\",\n      )\n        .evaluate((el) => el.textContent);\n      const denoEnvPrivate2 = await page.locator<HTMLDivElement>(\n        \".deno-env-private-2\",\n      )\n        .evaluate((el) => el.textContent);\n      const processEnvPrivate = await page.locator<HTMLDivElement>(\n        \".process-env-private\",\n      )\n        .evaluate((el) => el.textContent);\n\n      expect(denoEnv).toEqual(\"test-env-value\");\n      expect(denoEnv2).toEqual(\"test-env-value\");\n      expect(processEnv).toEqual(\"test-env-value\");\n\n      expect(denoEnvPrivate).toEqual(\"ok\");\n      expect(denoEnvPrivate2).toEqual(\"ok\");\n      expect(processEnvPrivate).toEqual(\"ok\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"fsRoutes - load islands from group folder\",\n  fn: async () => {\n    const app = testGroupApp();\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/foo`, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      // Page would error here\n      const text = await page\n        .locator<HTMLDivElement>(\".ready\")\n        .evaluate((el) => el.textContent!);\n      expect(text).toEqual(\"it works\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"fsRoutes - load islands from group folder with same name\",\n  fn: async () => {\n    const app = testGroupApp();\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(`${address}/both`, { waitUntil: \"load\" });\n\n      await page.locator(\".ready\").wait();\n\n      // Page would error here\n      let text = await page\n        .locator<HTMLDivElement>(\"#foo.ready\")\n        .evaluate((el) => el.textContent!);\n      expect(text).toEqual(\"it works\");\n\n      text = await page\n        .locator<HTMLDivElement>(\"#foo-both.ready\")\n        .evaluate((el) => el.textContent!);\n      expect(text).toEqual(\"it works\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"islands - adds preload HTTP headers\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) =>\n        ctx.render(\n          <Doc>\n            <SelfCounter />\n          </Doc>,\n        ));\n\n    const server = new FakeServer(app.handler());\n    const res = await server.get(\"/\");\n    await res.body?.cancel();\n\n    const link = res.headers.get(\"Link\");\n\n    expect(link).toMatch(\n      /<\\/_fresh\\/js\\/[a-zA-Z0-9]+\\/fresh-runtime\\.js>; rel=\"modulepreload\"; as=\"script\", <\\/_fresh\\/js\\/[a-zA-Z0-9]+\\/SelfCounter\\.js>; rel=\"modulepreload\"; as=\"script\"/,\n    );\n  },\n});\n"
  },
  {
    "path": "packages/fresh/tests/lorem_ipsum.txt",
    "content": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\nAenean commodo ligula eget dolor. Aenean massa. Cum sociis\nnatoque penatibus et magnis dis parturient montes, nascetur\nridiculus mus. Donec quam felis, ultricies nec, pellentesque\neu, pretium quis, sem. Nulla consequat massa quis enim.\nDonec pede justo, fringilla vel, aliquet nec, vulputate\neget, arcu. In enim justo, rhoncus ut, imperdiet a,\nvenenatis vitae, justo. Nullam dictum felis eu pede mollis\npretium. Integer tincidunt. Cras dapibus. Vivamus elementum\nsemper nisi. Aenean vulputate eleifend tellus. Aenean leo\nligula, porttitor eu, consequat vitae, eleifend ac, enim.\nAliquam lorem ante, dapibus in, viverra quis, feugiat a,\ntellus. Phasellus viverra nulla ut metus varius laoreet.\nQuisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel\naugue. Curabitur ullamcorper ultricies nisi. Nam eget dui.\nEtiam rhoncus. Maecenas tempus, tellus eget condimentum\nrhoncus, sem quam semper libero, sit amet adipiscing sem\nneque sed ipsum. Nam quam nunc, blandit vel, luctus\npulvinar, hendrerit id, lorem. Maecenas nec odio et ante\ntincidunt tempus. Donec vitae sapien ut libero venenatis\nfaucibus. Nullam quis ante. Etiam sit amet orci eget eros\nfaucibus tincidunt. Duis leo. Sed fringilla mauris sit amet\nnibh. Donec sodales sagittis magna. Sed consequat, leo eget\nbibendum sodales, augue velit cursus nunc,"
  },
  {
    "path": "packages/fresh/tests/partials_test.tsx",
    "content": "import { App, staticFiles } from \"fresh\";\nimport { Partial } from \"fresh/runtime\";\nimport {\n  ALL_ISLAND_DIR,\n  assertMetaContent,\n  assertNotSelector,\n  buildProd,\n  charset,\n  Doc,\n  favicon,\n  parseHtml,\n  waitFor,\n  waitForText,\n  withBrowserApp,\n} from \"./test_utils.tsx\";\nimport { SelfCounter } from \"./fixtures_islands/SelfCounter.tsx\";\nimport { expect } from \"@std/expect\";\nimport { PartialInIsland } from \"./fixtures_islands/PartialInIsland.tsx\";\nimport { FakeServer } from \"../src/test_utils.ts\";\nimport { JsonIsland } from \"./fixtures_islands/JsonIsland.tsx\";\nimport { OptOutPartialLink } from \"./fixtures_islands/OptOutPartialLink.tsx\";\nimport * as path from \"@std/path\";\nimport { retry } from \"@std/async/retry\";\n\nconst loremIpsum = await Deno.readTextFile(\n  path.join(import.meta.dirname!, \"lorem_ipsum.txt\"),\n);\n\nconst applyBuildCache = await buildProd({ islandDir: ALL_ISLAND_DIR });\n\nfunction testApp<T>(): App<T> {\n  const app = new App<T>()\n    .use(staticFiles());\n\n  applyBuildCache<T>(app);\n\n  return app;\n}\n\nDeno.test({\n  name: \"partials - updates content\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Partial name=\"foo\">\n            <p class=\"output\">partial update</p>\n          </Partial>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p class=\"output\">hello world</p>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".update\").click();\n      await waitForText(page, \".output\", \"partial update\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - revive island not seen before\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <SelfCounter />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p class=\"init\">hello world</p>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".update\").click();\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      const doc = parseHtml(await page.content());\n      assertNotSelector(doc, \".init\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - warn on missing partial\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"bar\">\n              <p class=\"ready\">bar</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p class=\"init\">hello world</p>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      const logs: string[] = [];\n      page.addEventListener(\"console\", (msg) => logs.push(msg.detail.text));\n\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".update\").click();\n\n      await waitFor(() =>\n        logs.find((line) => /^Partial.*not found/.test(line))\n      );\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - errors on duplicate partial name\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p class=\"ready\">foo</p>\n              </Partial>\n              <Partial name=\"foo\">\n                <p class=\"ready\">foo</p>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    const server = new FakeServer(app.handler());\n    let checked = false;\n    try {\n      const res = await server.get(\"/\");\n      await res.body?.cancel();\n\n      expect(res.status).toEqual(500);\n      checked = true;\n    } catch {\n      // Ignore\n    }\n\n    expect(checked).toEqual(true);\n\n    // TODO: Check error overlay\n  },\n});\n\n// See https://github.com/denoland/fresh/issues/2254\nDeno.test({\n  name: \"partials - should not be able to override __FRSH_STATE\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <SelfCounter />\n              <script id=\"__FRSH_STATE\">{\"{}\"}</script>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p class=\"init\">hello world</p>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      let didError = false;\n      page.addEventListener(\"pageerror\", () => {\n        didError = true;\n      });\n\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".update\").click();\n      await page.locator(\".ready\").wait();\n\n      expect(didError).toEqual(false);\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - finds partial nested in response\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div>\n              <div>\n                <Partial name=\"foo\">\n                  <SelfCounter />\n                </Partial>\n              </div>\n            </div>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p>hello world</p>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".update\").click();\n      await page.locator(\".ready\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - throws when instantiated inside island\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <PartialInIsland />\n            </div>\n          </Doc>,\n        );\n      });\n\n    applyBuildCache(app);\n    const server = new FakeServer(app.handler());\n    let checked = false;\n    try {\n      const res = await server.get(\"/\");\n      await res.body?.cancel();\n\n      expect(res.status).toEqual(500);\n      checked = true;\n    } catch {\n      // Ignore\n    }\n\n    expect(checked).toEqual(true);\n\n    // TODO: Test error overlay\n  },\n});\n\nDeno.test({\n  name: \"partials - unmounts island\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p>done</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n      await page.locator(\".update\").click();\n\n      await waitFor(async () => {\n        const doc = parseHtml(await page.content());\n        assertNotSelector(doc, \".increment\");\n        return true;\n      });\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - keeps island state\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class=\"partial-update\">partial update</p>\n              <SelfCounter />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p class=\"init\">init</p>\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".partial-update\").wait();\n\n      const doc = parseHtml(await page.content());\n      const counter = doc.querySelector(\".output\")?.textContent;\n      expect(counter).toEqual(\"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - replaces island\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class=\"partial-update\">partial update</p>\n              <JsonIsland />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p class=\"init\">init</p>\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".partial-update\").wait();\n\n      const doc = parseHtml(await page.content());\n      const raw = JSON.parse(doc.querySelector(\"pre\")!.textContent!);\n      expect(raw).toEqual({ foo: 123 });\n\n      assertNotSelector(doc, \".output\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - only updates inner partial\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"inner\">\n              <p class=\"inner-update\">inner update</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"outer\">\n                <p class=\"outer\">outer</p>\n                <Partial name=\"inner\">\n                  <p class=\"inner\">inner</p>\n                </Partial>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".inner\").wait();\n\n      await page.locator(\".update\").click();\n      await page.locator(\".inner-update\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - updates sibling partials\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"sib-1\">\n              <p class=\"sib-1-update\">sib-1 update</p>\n            </Partial>\n            <Partial name=\"sib-2\">\n              <p class=\"sib-2-update\">sib-2 update</p>\n            </Partial>\n            <Partial name=\"sib-3\">\n              <p class=\"sib-3-update\">sib-3 update</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"sib-1\">\n                <p class=\"sib-1\">sib-1</p>\n              </Partial>\n              <Partial name=\"sib-2\">\n                <p class=\"sib-2\">sib-2</p>\n              </Partial>\n              <p>foo</p>\n              <Partial name=\"sib-3\">\n                <p class=\"sib-3\">sib-3</p>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".sib-3\").wait();\n      await page.locator(\".update\").click();\n\n      await page.locator(\".sib-1-update\").wait();\n      await page.locator(\".sib-2-update\").wait();\n      await page.locator(\".sib-3-update\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - reconcile keyed islands in update\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p key=\"p\" class=\"done\">done</p>\n              <SelfCounter key=\"b\" id=\"b\" />\n              <SelfCounter key=\"c\" id=\"c\" />\n              <SelfCounter key=\"a\" id=\"a\" />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p key=\"p\" class=\"init\">init</p>\n                <SelfCounter key=\"a\" id=\"a\" />\n                <SelfCounter key=\"b\" id=\"b\" />\n                <SelfCounter key=\"c\" id=\"c\" />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\"#a .increment\").click();\n\n      await page.locator(\"#b .increment\").click();\n      await page.locator(\"#b .increment\").click();\n\n      await page.locator(\"#c .increment\").click();\n      await page.locator(\"#c .increment\").click();\n      await page.locator(\"#c .increment\").click();\n\n      await waitForText(page, \"#a .output\", \"1\");\n      await waitForText(page, \"#b .output\", \"2\");\n      await waitForText(page, \"#c .output\", \"3\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n\n      await waitForText(page, \"#a .output\", \"1\");\n      await waitForText(page, \"#b .output\", \"2\");\n      await waitForText(page, \"#c .output\", \"3\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - reconcile keyed partials\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"outer\">\n              <p key=\"p\" class=\"done\">done</p>\n              <Partial key=\"b\" name=\"b\">\n                <SelfCounter id=\"b\" />\n              </Partial>\n              <Partial key=\"c\" name=\"c\">\n                <SelfCounter id=\"c\" />\n              </Partial>\n              <Partial key=\"a\" name=\"a\">\n                <SelfCounter id=\"a\" />\n              </Partial>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"outer\">\n                <p key=\"p\" class=\"init\">init</p>\n\n                <Partial key=\"a\" name=\"a\">\n                  <SelfCounter id=\"a\" />\n                </Partial>\n                <Partial key=\"b\" name=\"b\">\n                  <SelfCounter id=\"b\" />\n                </Partial>\n                <Partial key=\"c\" name=\"c\">\n                  <SelfCounter id=\"c\" />\n                </Partial>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\"#a .increment\").click();\n\n      await page.locator(\"#b .increment\").click();\n      await page.locator(\"#b .increment\").click();\n\n      await page.locator(\"#c .increment\").click();\n      await page.locator(\"#c .increment\").click();\n      await page.locator(\"#c .increment\").click();\n\n      await waitForText(page, \"#a .output\", \"1\");\n      await waitForText(page, \"#b .output\", \"2\");\n      await waitForText(page, \"#c .output\", \"3\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n\n      await waitForText(page, \"#a .output\", \"1\");\n      await waitForText(page, \"#b .output\", \"2\");\n      await waitForText(page, \"#c .output\", \"3\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - reconcile keyed div inside partials\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"outer\">\n              <p key=\"p\" class=\"done\">done</p>\n              <div key=\"b\">\n                <SelfCounter id=\"b\" />\n              </div>\n              <div key=\"c\">\n                <SelfCounter id=\"c\" />\n              </div>\n              <div key=\"a\">\n                <SelfCounter id=\"a\" />\n              </div>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"outer\">\n                <p key=\"p\" class=\"init\">init</p>\n\n                <div key=\"a\">\n                  <SelfCounter id=\"a\" />\n                </div>\n                <div key=\"b\">\n                  <SelfCounter id=\"b\" />\n                </div>\n                <div key=\"c\">\n                  <SelfCounter id=\"c\" />\n                </div>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\"#a .increment\").click();\n\n      await page.locator(\"#b .increment\").click();\n      await page.locator(\"#b .increment\").click();\n\n      await page.locator(\"#c .increment\").click();\n      await page.locator(\"#c .increment\").click();\n      await page.locator(\"#c .increment\").click();\n\n      await waitForText(page, \"#a .output\", \"1\");\n      await waitForText(page, \"#b .output\", \"2\");\n      await waitForText(page, \"#c .output\", \"3\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n\n      await waitForText(page, \"#a .output\", \"1\");\n      await waitForText(page, \"#b .output\", \"2\");\n      await waitForText(page, \"#c .output\", \"3\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - reconcile keyed component inside partials\",\n  fn: async () => {\n    function Foo(props: { id: string }) {\n      return <SelfCounter id={props.id} />;\n    }\n\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"outer\">\n              <p key=\"p\" class=\"done\">done</p>\n              <Foo key=\"b\" id=\"b\" />\n              <Foo key=\"c\" id=\"c\" />\n              <Foo key=\"a\" id=\"a\" />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"outer\">\n                <p key=\"p\" class=\"init\">init</p>\n\n                <Foo key=\"a\" id=\"a\" />\n                <Foo key=\"b\" id=\"b\" />\n                <Foo key=\"c\" id=\"c\" />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\"#a .increment\").click();\n\n      await page.locator(\"#b .increment\").click();\n      await page.locator(\"#b .increment\").click();\n\n      await page.locator(\"#c .increment\").click();\n      await page.locator(\"#c .increment\").click();\n      await page.locator(\"#c .increment\").click();\n\n      await waitForText(page, \"#a .output\", \"1\");\n      await waitForText(page, \"#b .output\", \"2\");\n      await waitForText(page, \"#c .output\", \"3\");\n\n      await page.locator(\".update\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n\n      await waitForText(page, \"#a .output\", \"1\");\n      await waitForText(page, \"#b .output\", \"2\");\n      await waitForText(page, \"#c .output\", \"3\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - skip key serialization if outside root\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <p key=\"outside\" class=\"init\">outside</p>\n            <Partial name=\"outer\">\n              <p key=\"inside\" class=\"init\">inside</p>\n            </Partial>\n          </Doc>,\n        );\n      });\n\n    const server = new FakeServer(app.handler());\n    const res = await server.get(\"/\");\n    const html = await res.text();\n\n    expect(html).not.toMatch(/frsh:key:outside/);\n  },\n});\n\nDeno.test({\n  name: \"partials - mode replace\",\n  fn: async () => {\n    let i = 0;\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        const id = i++;\n        return ctx.render(\n          <Doc>\n            <Partial name=\"outer\" mode=\"replace\">\n              <p class={`done-${id}`}>{id}</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"outer\">\n                <p class=\"init\">init</p>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".init\").wait();\n      await page.locator(\".update\").click();\n\n      await page.locator(\".done-0\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".done-1\").wait();\n\n      const doc = parseHtml(await page.content());\n      assertNotSelector(doc, \".init\");\n      assertNotSelector(doc, \".done-0\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - mode replace inner\",\n  fn: async () => {\n    let i = 0;\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        const id = i++;\n        return ctx.render(\n          <Doc>\n            <Partial name=\"outer\">\n              <Partial name=\"inner\" mode=\"replace\">\n                <p class={`done-${id}`}>{id}</p>\n              </Partial>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"outer\">\n                <Partial name=\"inner\">\n                  <p class=\"init\">init</p>\n                </Partial>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".init\").wait();\n      await page.locator(\".update\").click();\n\n      await page.locator(\".done-0\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".done-1\").wait();\n\n      const doc = parseHtml(await page.content());\n      assertNotSelector(doc, \".init\");\n      assertNotSelector(doc, \".done-0\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - mode append\",\n  fn: async () => {\n    let i = 0;\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        const id = i++;\n        return ctx.render(\n          <Doc>\n            <Partial name=\"outer\" mode=\"append\">\n              <p class={`done-${id}`}>{id}</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <div class=\"content\">\n                <Partial name=\"outer\">\n                  <p class=\"init\">init</p>\n                </Partial>\n              </div>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".init\").wait();\n      await page.locator(\".update\").click();\n\n      await page.locator(\".done-0\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".done-1\").wait();\n\n      const doc = parseHtml(await page.content());\n\n      expect(doc.querySelector(\".content\")!.textContent).toEqual(\"init01\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - mode append inner\",\n  fn: async () => {\n    let i = 0;\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        const id = i++;\n        return ctx.render(\n          <Doc>\n            <Partial name=\"outer\">\n              <Partial name=\"inner\" mode=\"append\">\n                <p class={`done-${id}`}>{id}</p>\n              </Partial>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <div class=\"content\">\n                <Partial name=\"outer\">\n                  <Partial name=\"inner\">\n                    <p class=\"init\">init</p>\n                  </Partial>\n                </Partial>\n              </div>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".init\").wait();\n      await page.locator(\".update\").click();\n\n      await page.locator(\".done-0\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".done-1\").wait();\n\n      const doc = parseHtml(await page.content());\n      expect(doc.querySelector(\".content\")!.textContent).toEqual(\"init01\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - mode prepend\",\n  fn: async () => {\n    let i = 0;\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        const id = i++;\n        return ctx.render(\n          <Doc>\n            <Partial name=\"outer\" mode=\"prepend\">\n              <p class={`done-${id}`}>{id}</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <div class=\"content\">\n                <Partial name=\"outer\">\n                  <p class=\"init\">init</p>\n                </Partial>\n              </div>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".init\").wait();\n      await page.locator(\".update\").click();\n\n      await page.locator(\".done-0\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".done-1\").wait();\n\n      const doc = parseHtml(await page.content());\n      expect(doc.querySelector(\".content\")!.textContent).toEqual(\"10init\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - mode prepend inner\",\n  fn: async () => {\n    let i = 0;\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        const id = i++;\n        return ctx.render(\n          <Doc>\n            <Partial name=\"outer\">\n              <Partial name=\"inner\" mode=\"prepend\">\n                <p class={`done-${id}`}>{id}</p>\n              </Partial>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <div class=\"content\">\n                <Partial name=\"outer\">\n                  <Partial name=\"inner\">\n                    <p class=\"init\">init</p>\n                  </Partial>\n                </Partial>\n              </div>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".init\").wait();\n      await page.locator(\".update\").click();\n\n      await page.locator(\".done-0\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".done-1\").wait();\n\n      const doc = parseHtml(await page.content());\n      expect(doc.querySelector(\".content\")!.textContent).toEqual(\"10init\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - navigate\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class=\"done\">done</p>\n              <SelfCounter />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <a href=\"/partial\" class=\"update\">update</a>\n              <Partial name=\"foo\">\n                <p class=\"init\">init</p>\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n\n      await page.waitForFunction(() => {\n        const url = new URL(window.location.href);\n        return url.pathname === \"/partial\";\n      });\n\n      await waitForText(page, \".output\", \"1\");\n\n      await page.evaluate(() => window.history.go(-1));\n\n      await page.locator(\".init\").wait();\n      await page.waitForFunction(() => {\n        const url = new URL(window.location.href);\n        return url.pathname === \"/\";\n      });\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - uses f-partial instead\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class=\"done\">done</p>\n              <SelfCounter />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <a href=\"/foo\" f-partial=\"/partial\" class=\"update\">update</a>\n              <Partial name=\"foo\">\n                <p class=\"init\">init</p>\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n\n      await page.waitForFunction(() => {\n        const url = new URL(window.location.href);\n        return url.pathname === \"/foo\";\n      });\n      await waitForText(page, \".output\", \"1\");\n\n      await page.evaluate(() => window.history.go(-1));\n\n      await page.locator(\".init\").wait();\n      await page.waitForFunction(() => {\n        const url = new URL(window.location.href);\n        return url.pathname === \"/\";\n      });\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - with SVG in link\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class=\"done\">done</p>\n              <SelfCounter />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <a href=\"/foo\" f-partial=\"/partial\">\n                <svg\n                  width=\"100\"\n                  height=\"100\"\n                  viewBox=\"-256 -256 512 512\"\n                  version=\"1.1\"\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  xmlnsXlink=\"http://www.w3.org/1999/xlink\"\n                >\n                  <path\n                    class=\"update\"\n                    d=\"M0,-256 221.7025033688164,-128 221.7025033688164,128 0,256 -221.7025033688164,128 -221.7025033688164,-128z\"\n                    fill=\"#673ab8\"\n                  />\n                  <ellipse\n                    cx=\"0\"\n                    cy=\"0\"\n                    stroke-width=\"16px\"\n                    rx=\"75px\"\n                    ry=\"196px\"\n                    fill=\"none\"\n                    stroke=\"white\"\n                    transform=\"rotate(52.5)\"\n                  />\n                  <ellipse\n                    cx=\"0\"\n                    cy=\"0\"\n                    stroke-width=\"16px\"\n                    rx=\"75px\"\n                    ry=\"196px\"\n                    fill=\"none\"\n                    stroke=\"white\"\n                    transform=\"rotate(-52.5)\"\n                  />\n                  <circle cx=\"0\" cy=\"0\" r=\"34\" fill=\"white\" />\n                </svg>\n                update\n              </a>\n              <Partial name=\"foo\">\n                <p class=\"init\">init</p>\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n\n      await page.waitForFunction(() => {\n        const url = new URL(window.location.href);\n        return url.pathname === \"/foo\";\n      });\n      await page.locator(\".done\").wait();\n      await waitForText(page, \".output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - with SVG in button\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class=\"done\">done</p>\n              <SelfCounter />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\">\n                <svg\n                  width=\"100\"\n                  height=\"100\"\n                  viewBox=\"-256 -256 512 512\"\n                  version=\"1.1\"\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  xmlnsXlink=\"http://www.w3.org/1999/xlink\"\n                >\n                  <path\n                    class=\"update\"\n                    d=\"M0,-256 221.7025033688164,-128 221.7025033688164,128 0,256 -221.7025033688164,128 -221.7025033688164,-128z\"\n                    fill=\"#673ab8\"\n                  />\n                  <ellipse\n                    cx=\"0\"\n                    cy=\"0\"\n                    stroke-width=\"16px\"\n                    rx=\"75px\"\n                    ry=\"196px\"\n                    fill=\"none\"\n                    stroke=\"white\"\n                    transform=\"rotate(52.5)\"\n                  />\n                  <ellipse\n                    cx=\"0\"\n                    cy=\"0\"\n                    stroke-width=\"16px\"\n                    rx=\"75px\"\n                    ry=\"196px\"\n                    fill=\"none\"\n                    stroke=\"white\"\n                    transform=\"rotate(-52.5)\"\n                  />\n                  <circle cx=\"0\" cy=\"0\" r=\"34\" fill=\"white\" />\n                </svg>\n                update\n              </button>\n              <Partial name=\"foo\">\n                <p class=\"init\">init</p>\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n\n      await page.locator(\".done\").wait();\n      await waitForText(page, \".output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - opt out of partial navigation\",\n  ignore: true, // TODO: test is flaky\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"fail\">Fail</h1>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/foo\", (ctx) =>\n        ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"done\">done</h1>\n              <SelfCounter />\n            </Partial>\n          </Doc>,\n        ))\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <a\n                href=\"/foo\"\n                f-client-nav={false}\n                f-partial=\"/partial\"\n                class=\"update\"\n              >\n                update\n              </a>\n              <Partial name=\"foo\">\n                <p class=\"init\">init</p>\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await retry(async () => {\n        await page.goto(address, { waitUntil: \"load\" });\n        await page.locator(\".ready\").wait();\n\n        await page.locator(\".increment\").click();\n        await waitForText(page, \".output\", \"1\");\n\n        await page.locator(\".update\").click();\n        await page.locator(\".done\").wait();\n\n        await page.waitForFunction(() => {\n          const url = new URL(window.location.href);\n          return url.pathname === \"/foo\";\n        });\n        await page.locator(\".output\").wait();\n        await waitForText(page, \".output\", \"0\");\n      });\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - opt out of partial navigation #2\",\n  ignore: true, // TODO: test is flaky\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"fail\">Fail</h1>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/foo\", (ctx) =>\n        ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"done\">done</h1>\n              <SelfCounter />\n            </Partial>\n          </Doc>,\n        ))\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <div>\n                <div f-client-nav={false}>\n                  <a\n                    href=\"/foo\"\n                    f-partial=\"/partial\"\n                    class=\"update\"\n                  >\n                    update\n                  </a>\n                </div>\n              </div>\n              <Partial name=\"foo\">\n                <p class=\"init\">init</p>\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await retry(async () => {\n        await page.goto(address, { waitUntil: \"load\" });\n        await page.locator(\".ready\").wait();\n\n        await page.locator(\".increment\").click();\n        await waitForText(page, \".output\", \"1\");\n\n        await page.locator(\".update\").click();\n        await page.waitForSelector(\".done\");\n\n        const url = new URL(page.url!);\n        expect(url.pathname).toEqual(\"/foo\");\n        await waitForText(page, \".output\", \"0\");\n      });\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - opt out of partial navigation in island\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"fail\">Fail</h1>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/foo\", (ctx) =>\n        ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"done\">done</h1>\n              <SelfCounter />\n            </Partial>\n          </Doc>,\n        ))\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <div>\n                <OptOutPartialLink href=\"/foo\" partial=\"/partial\" />\n              </div>\n              <Partial name=\"foo\">\n                <p class=\"init\">init</p>\n                <SelfCounter />\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await retry(async () => {\n        await page.goto(address, { waitUntil: \"load\" });\n        await page.locator(\".ready\").wait();\n\n        await page.locator(\".increment\").click();\n        await waitForText(page, \".output\", \"1\");\n\n        await Promise.all([\n          page.waitForNavigation(),\n          page.locator(\".update\").click(),\n        ]);\n        await page.waitForSelector(\".done\");\n\n        const url = new URL(page.url!);\n        expect(url.pathname).toEqual(\"/foo\");\n        await waitForText(page, \".output\", \"0\");\n      });\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - restore scroll position\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"partial-content\">foo</h1>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <Partial name=\"foo\">\n                {new Array(10).fill(0).map((it) => {\n                  return <p key={it}>{loremIpsum}</p>;\n                })}\n                <p class=\"init\">init</p>\n              </Partial>\n              <p>\n                <a\n                  class=\"update\"\n                  href=\"/partial\"\n                >\n                  update\n                </a>\n              </p>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.evaluate(() => {\n        document.querySelector(\".update\")?.scrollIntoView({\n          behavior: \"instant\",\n        });\n      });\n      await page.locator(\".update\").click();\n\n      await page.locator(\".partial-content\").wait();\n      await page.evaluate(() => window.history.go(-1));\n      await page.locator(\".init\").wait();\n      // deno-lint-ignore no-explicit-any\n      const scroll: any = await page.evaluate(() => ({ scrollX, scrollY }));\n\n      expect(scroll.scrollY > 100).toEqual(true);\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - submit form\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        const name = ctx.url.searchParams.get(\"name\")!;\n        const submitter = ctx.url.searchParams.get(\"submitter\")!;\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class={`done-${name}-${submitter}`}>done</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <form action=\"/partial\">\n                <input name=\"name\" value=\"foo\" />\n                <Partial name=\"foo\">\n                  <p class=\"init\">init</p>\n                </Partial>\n                <SelfCounter />\n                <button\n                  type=\"submit\"\n                  class=\"update\"\n                  name=\"submitter\"\n                  value=\"sub\"\n                >\n                  update\n                </button>\n              </form>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done-foo-sub\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - submit form f-partial\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        const name = ctx.url.searchParams.get(\"name\")!;\n        const submitter = ctx.url.searchParams.get(\"submitter\")!;\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class={`done-${name}-${submitter}`}>done</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <form action=\"/foo\" f-partial=\"/partial\">\n                <input name=\"name\" value=\"foo\" />\n                <Partial name=\"foo\">\n                  <p class=\"init\">init</p>\n                </Partial>\n                <SelfCounter />\n                <button\n                  type=\"submit\"\n                  class=\"update\"\n                  name=\"submitter\"\n                  value=\"sub\"\n                >\n                  update\n                </button>\n              </form>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done-foo-sub\").wait();\n\n      const pathname = await page.evaluate(() => window.location.pathname);\n      expect(pathname).toEqual(\"/foo\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - submit form POST\",\n  fn: async () => {\n    const app = testApp()\n      .post(\"/partial\", async (ctx) => {\n        const data = await ctx.req.formData();\n        const name = data.get(\"name\");\n        const submitter = data.get(\"submitter\");\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class={`done-${name}-${submitter}`}>done</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <form action=\"/partial\" method=\"post\">\n                <input name=\"name\" value=\"foo\" />\n                <Partial name=\"foo\">\n                  <p class=\"init\">init</p>\n                </Partial>\n                <SelfCounter />\n                <button\n                  type=\"submit\"\n                  class=\"update\"\n                  name=\"submitter\"\n                  value=\"sub\"\n                >\n                  update\n                </button>\n              </form>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done-foo-sub\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - submit form dialog should do nothing\",\n  fn: async () => {\n    const app = testApp()\n      .post(\"/partial\", () => {\n        throw new Error(\"FAIL\");\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <dialog open>\n                <p>Greetings, one and all!</p>\n                <form method=\"dialog\">\n                  <Partial name=\"foo\">\n                    <p class=\"init\">init</p>\n                  </Partial>\n                  <SelfCounter />\n                  <button type=\"submit\" class=\"update\">OK</button>\n                </form>\n              </dialog>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\"dialog:not([open])\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - submit form redirect\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/done\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"done\">success</h1>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .post(\"/partial\", async (ctx) => {\n        const data = await ctx.req.formData();\n        const name = String(data.get(\"name\"));\n\n        return new Response(null, {\n          status: 303,\n          headers: { Location: `/done?name=${encodeURIComponent(name)}` },\n        });\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <form action=\"/partial\" method=\"post\">\n                <input name=\"name\" value=\"foo\" />\n                <Partial name=\"foo\">\n                  <p class=\"init\">init</p>\n                </Partial>\n                <SelfCounter />\n                <button type=\"submit\" class=\"update\">\n                  update\n                </button>\n              </form>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n\n      const pathname = await page.evaluate(() => window.location.pathname);\n      expect(pathname).toEqual(\"/done\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - submit form via external submitter\",\n  fn: async () => {\n    const app = testApp()\n      .post(\"/partial\", async (ctx) => {\n        const data = await ctx.req.formData();\n        const name = data.get(\"name\");\n        const submitter = data.get(\"submitter\");\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class={`done-${name}-${submitter}`}>done</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <form action=\"/foo\" id=\"foo\">\n                <input name=\"name\" value=\"foo\" />\n                <Partial name=\"foo\">\n                  <p class=\"init\">init</p>\n                </Partial>\n                <SelfCounter />\n                <button type=\"button\">\n                  nothing\n                </button>\n              </form>\n              <button\n                type=\"submit\"\n                class=\"update\"\n                form=\"foo\"\n                formaction=\"/partial\"\n                formmethod=\"POST\"\n                name=\"submitter\"\n                value=\"sub\"\n              >\n                submit\n              </button>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done-foo-sub\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - submit form via external submitter f-partial\",\n  fn: async () => {\n    const app = testApp()\n      .post(\"/partial\", async (ctx) => {\n        const data = await ctx.req.formData();\n        const name = data.get(\"name\");\n        const submitter = data.get(\"submitter\");\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class={`done-${name}-${submitter}`}>done</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <form action=\"/foo\" id=\"foo\">\n                <input name=\"name\" value=\"foo\" />\n                <Partial name=\"foo\">\n                  <p class=\"init\">init</p>\n                </Partial>\n                <SelfCounter />\n                <button type=\"button\">\n                  nothing\n                </button>\n              </form>\n              <button\n                type=\"submit\"\n                class=\"update\"\n                form=\"foo\"\n                formaction=\"/foo\"\n                formmethod=\"POST\"\n                f-partial=\"/partial\"\n                name=\"submitter\"\n                value=\"sub\"\n              >\n                submit\n              </button>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done-foo-sub\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name:\n    \"partials - don't apply partials when submitter has client nav disabled\",\n  fn: async () => {\n    const app = testApp()\n      .post(\"/partial\", async (ctx) => {\n        const data = await ctx.req.formData();\n        const name = data.get(\"name\");\n        const submitter = data.get(\"submitter\");\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class={`done-${name}-${submitter}`}>done</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <form action=\"/foo\" id=\"foo\">\n                <input name=\"name\" value=\"foo\" />\n                <Partial name=\"foo\">\n                  <p class=\"init\">init</p>\n                </Partial>\n                <SelfCounter />\n                <button type=\"button\">\n                  nothing\n                </button>\n              </form>\n              <button\n                f-client-nav={false}\n                type=\"submit\"\n                class=\"update\"\n                form=\"foo\"\n                formaction=\"/partial\"\n                formmethod=\"POST\"\n                f-partial=\"/partial\"\n                name=\"submitter\"\n                value=\"sub\"\n              >\n                submit\n              </button>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await Promise.all([\n        page.waitForNavigation(),\n        page.locator(\".update\").click(),\n      ]);\n      await page.locator(\".done-foo-sub\").wait();\n\n      const doc = parseHtml(await page.content());\n      assertNotSelector(doc, \"button\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - form submit multiple values\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        const values = ctx.url.searchParams.getAll(\"name\");\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <p class={`done-${values.join(\"-\")}`}>done</p>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <form action=\"/partial\" method=\"get\">\n                <input type=\"checkbox\" name=\"name\" value=\"a\" />\n                <input type=\"checkbox\" name=\"name\" value=\"b\" />\n                <input type=\"checkbox\" name=\"name\" value=\"c\" />\n                <Partial name=\"foo\">\n                  <p class=\"init\">init</p>\n                </Partial>\n                <SelfCounter />\n                <button type=\"submit\" class=\"update\">\n                  submit\n                </button>\n              </form>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n\n      await page.locator(\".increment\").click();\n      await waitForText(page, \".output\", \"1\");\n\n      await page.locator<HTMLInputElement>(\"input[value=a]\").evaluate((el) =>\n        el.checked = true\n      );\n      await page.locator<HTMLInputElement>(\"input[value=b]\").evaluate((el) =>\n        el.checked = true\n      );\n      await page.locator<HTMLInputElement>(\"input[value=c]\").evaluate((el) =>\n        el.checked = true\n      );\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done-a-b-c\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - fragment nav should not cause infinite loop\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <h1 id=\"foo\">Same nav</h1>\n              <a href=\"#foo\">#foo</a>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      const logs: string[] = [];\n      page.addEventListener(\"console\", (msg) => logs.push(msg.detail.text));\n\n      await page.goto(address, { waitUntil: \"load\" });\n\n      await page.locator(\"a\").click();\n      await page.waitForFunction(() => location.hash === \"#foo\");\n      expect(logs).toEqual([]);\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - fragment navigation should not scroll to top\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              {new Array(10).fill(0).map((it) => {\n                return <p key={it}>{loremIpsum}</p>;\n              })}\n              <h1 id=\"foo\">Same nav</h1>\n              <a href=\"#foo\">#foo</a>\n              <Partial name=\"foo\">\n                <p class=\"partial-text\">\n                  foo partial\n                </p>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n\n      await page.locator(\"a\").click();\n      await page.waitForFunction(() => location.hash === \"#foo\");\n      // deno-lint-ignore no-explicit-any\n      const scroll: any = await page.evaluate(() => globalThis.scrollY);\n      expect(scroll > 0).toEqual(true);\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - throws an error when response contains no partials\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) =>\n        ctx.render(\n          <Doc>\n            <p class=\"status-append\">append content</p>\n          </Doc>,\n        ))\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <Partial name=\"body\">\n                <p class=\"init\">\n                  init\n                </p>\n              </Partial>\n              <p>\n                <button type=\"button\" class=\"update\" f-partial=\"/partial\">\n                  update\n                </button>\n              </p>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      const logs: string[] = [];\n      page.addEventListener(\"pageerror\", (msg) => {\n        logs.push(String(msg.detail));\n      });\n\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".update\").click();\n\n      await waitFor(() => logs.length > 0);\n      expect(logs[0]).toMatch(/Found no partials/);\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - merges <head> content\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/other.css\", () =>\n        new Response(\"h1 { color: red }\", {\n          headers: {\n            \"Content-Type\": \"text/css\",\n          },\n        }))\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <html>\n            <head>\n              {charset}\n              {favicon}\n              <title>Head merge updated</title>\n              <meta name=\"foo\" content=\"bar baz\" />\n              <meta property=\"og:foo\" content=\"og value foo\" />\n              <meta property=\"og:bar\" content=\"og value bar\" />\n              <link rel=\"stylesheet\" href=\"/other.css\" />\n              <style>{`p { color: green }`}</style>\n            </head>\n            <body f-client-nav>\n              <Partial name=\"body\">\n                <h1>updated heading</h1>\n                <p class=\"updated\">\n                  updated\n                </p>\n              </Partial>\n            </body>\n          </html>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <html>\n            <head>\n              {charset}\n              {favicon}\n              <title>Head merge</title>\n              <meta name=\"foo\" content=\"bar\" />\n              <meta property=\"og:foo\" content=\"og value foo\" />\n              <style id=\"style-foo\">{`.foo { color: red}`}</style>\n            </head>\n            <body f-client-nav>\n              <Partial name=\"body\">\n                <p class=\"init\">\n                  init\n                </p>\n              </Partial>\n              <p>\n                <button type=\"button\" class=\"update\" f-partial=\"/partial\">\n                  update\n                </button>\n              </p>\n            </body>\n          </html>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n\n      await page.locator(\".update\").click();\n      await page.locator(\".updated\").wait();\n\n      await waitFor(async () => {\n        return (await page.evaluate(() => document.title)) ===\n          \"Head merge updated\";\n      });\n\n      const doc = parseHtml(await page.content());\n      expect(doc.title).toEqual(\"Head merge updated\");\n\n      assertMetaContent(doc, \"foo\", \"bar baz\");\n      assertMetaContent(doc, \"og:foo\", \"og value foo\");\n      assertMetaContent(doc, \"og:bar\", \"og value bar\");\n\n      await waitFor(async () => {\n        const color = await page\n          .locator<HTMLHeadingElement>(\"h1\")\n          .evaluate((el) => {\n            return globalThis.getComputedStyle(el).color;\n          });\n        expect(color).toEqual(\"rgb(255, 0, 0)\");\n        return true;\n      });\n\n      await waitFor(async () => {\n        const textColor = await page\n          .locator<HTMLParagraphElement>(\"p\")\n          .evaluate((el) => {\n            return globalThis.getComputedStyle(el).color;\n          });\n        expect(textColor).toEqual(\"rgb(0, 128, 0)\");\n        return true;\n      });\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - does not merge duplicate <head> content\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/style.css\", () =>\n        new Response(\"h1 { color: red }\", {\n          headers: {\n            \"Content-Type\": \"text/css\",\n          },\n        }))\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <html>\n            <head>\n              {charset}\n              {favicon}\n              <title>Head merge duplicated</title>\n              <meta name=\"foo\" content=\"bar\" />\n              <meta property=\"og:foo\" content=\"og value foo\" />\n              <link rel=\"stylesheet\" href=\"/style.css\" />\n              <style id=\"style-foo\">{`.foo { color: red}`}</style>\n            </head>\n            <body f-client-nav>\n              <Partial name=\"body\">\n                <p class=\"updated\">\n                  updated\n                </p>\n              </Partial>\n              <p>\n                <button type=\"button\" class=\"update\" f-partial=\"/partial\">\n                  update\n                </button>\n              </p>\n            </body>\n          </html>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <html>\n            <head>\n              {charset}\n              {favicon}\n              <title>Head merge</title>\n              <meta name=\"foo\" content=\"bar\" />\n              <meta property=\"og:foo\" content=\"og value foo\" />\n              <link rel=\"stylesheet\" href=\"/style.css\" />\n              <style id=\"style-foo\">{`.foo { color: red}`}</style>\n            </head>\n            <body f-client-nav>\n              <Partial name=\"body\">\n                <p class=\"init\">\n                  init\n                </p>\n              </Partial>\n              <p>\n                <button type=\"button\" class=\"update\" f-partial=\"/partial\">\n                  update\n                </button>\n              </p>\n            </body>\n          </html>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".update\").click();\n      await page.locator(\".updated\").wait();\n\n      await waitFor(async () => {\n        return (await page.evaluate(() => document.title)) ===\n          \"Head merge duplicated\";\n      });\n\n      const html = await page.content();\n      expect(\n        Array.from(html.matchAll(/id=\"style-foo\"/g)).length === 1,\n      ).toEqual(true);\n\n      expect(\n        Array.from(html.matchAll(/style\\.css/g)).length === 1,\n      ).toEqual(true);\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - supports relative links\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        const { searchParams } = ctx.url;\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <Partial name=\"body\">\n                <p\n                  class={searchParams.has(\"refresh\")\n                    ? \"status-refreshed\"\n                    : \"status-initial\"}\n                >\n                  {searchParams.has(\"refresh\")\n                    ? \"Refreshed content\"\n                    : \"Initial content\"}\n                </p>\n              </Partial>\n              <p>\n                <button type=\"button\" f-partial=\"?refresh\">\n                  refresh\n                </button>\n              </p>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".status-initial\").wait();\n\n      await page.locator(\"button\").click();\n      await page.locator(\".status-refreshed\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - update stateful inner partials\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"inner\">\n              <p class=\"done\">done</p>\n              <SelfCounter id=\"inner\" />\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"outer\">\n                <SelfCounter id=\"outer\" />\n                <Partial name=\"inner\">\n                  <p>init</p>\n                  <SelfCounter id=\"inner\" />\n                </Partial>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n      await page.locator(\"#outer .increment\").click();\n      await page.locator(\"#outer .increment\").click();\n\n      await page.locator(\"#inner .increment\").click();\n\n      await waitForText(page, \"#outer .output\", \"2\");\n      await waitForText(page, \"#inner .output\", \"1\");\n\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n\n      await waitForText(page, \"#outer .output\", \"2\");\n      await waitForText(page, \"#inner .output\", \"1\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - with redirects\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/a\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"done\">foo update</h1>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/partial\", (ctx) => ctx.redirect(\"/a\"))\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <h1>foo</h1>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\"h1\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - render 404 partial\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <h1>foo</h1>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      })\n      .get(\"/*\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"error-404\">404</h1>\n            </Partial>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\"h1\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".error-404\").wait();\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - render with new title\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/partial\", (ctx) => {\n        return ctx.render(\n          <Doc title=\"after update\">\n            <Partial name=\"foo\">\n              <h1 class=\"done\">foo update</h1>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/partial\" class=\"update\">\n                update\n              </button>\n              <Partial name=\"foo\">\n                <h1>foo</h1>\n              </Partial>\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\"h1\").wait();\n      await page.locator(\".update\").click();\n      await page.locator(\".done\").wait();\n\n      const title = await page.evaluate(() => document.title);\n      expect(title).toEqual(\"after update\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - button should not update history\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/other\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"done\">other</h1>\n            </Partial>\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <button type=\"button\" f-partial=\"/other\">click</button>\n              <Partial name=\"foo\">\n                <h1 class=\"init\">foo</h1>\n              </Partial>\n              <SelfCounter />\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n      await page.locator(\"button\").click();\n      await page.locator(\".done\").wait();\n\n      const rawUrl = await page.evaluate(() => window.location.href);\n      const url = new URL(rawUrl);\n      expect(`${url.pathname}${url.search}`).toEqual(\"/\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - backwards navigation should keep URLs\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/other\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <Partial name=\"foo\">\n              <h1 class=\"done\">other</h1>\n            </Partial>\n            <SelfCounter />\n          </Doc>,\n        );\n      })\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div f-client-nav>\n              <a href=\"/other\">other</a>\n              <Partial name=\"foo\">\n                <h1 class=\"init\">foo</h1>\n              </Partial>\n              <SelfCounter />\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator(\".ready\").wait();\n      await page.locator(\"a\").click();\n      await page.locator(\".done\").wait();\n\n      await page.evaluate(() => window.history.go(-1));\n      await page.locator(\".init\").wait();\n      const rawUrl = await page.evaluate(() => window.location.href);\n      const url = new URL(rawUrl);\n      expect(`${url.pathname}${url.search}`).toEqual(\"/\");\n    });\n  },\n});\n\nDeno.test({\n  name: \"partials - independent user popstate\",\n  fn: async () => {\n    const app = testApp()\n      .get(\"/\", (ctx) => {\n        return ctx.render(\n          <Doc>\n            <div class=\"container\">\n            </div>\n          </Doc>,\n        );\n      });\n\n    await withBrowserApp(app, async (page, address) => {\n      await page.goto(address, { waitUntil: \"load\" });\n      await page.locator<HTMLDivElement>(\".container\").evaluate((el) => {\n        const dynamicContent = document.createElement(\"span\");\n        dynamicContent.classList.add(\"dynamic-content\");\n        el.appendChild(dynamicContent);\n      });\n      await page.evaluate(() => {\n        window.history.replaceState({ custom: true }, \"\", \"#\");\n        window.history.pushState({ custom: true }, \"\", \"#custom\");\n      });\n      // Fresh partials popstate gets called on back navigation and\n      // should exit early/avoid reload due to custom user history entries\n      await page.evaluate(() => window.history.go(-1));\n      await page.locator(\".dynamic-content\").wait();\n    });\n  },\n});\n"
  },
  {
    "path": "packages/fresh/tests/precompile_test.ts",
    "content": "import { expect } from \"@std/expect\";\n\nDeno.test(\"JSX precompile - check config\", async () => {\n  const { stderr, success } = await new Deno.Command(Deno.execPath(), {\n    args: [\"run\", \"-A\", \"dev.ts\"],\n    cwd: new URL(\"./fixture_precompile/invalid\", import.meta.url),\n  }).output();\n\n  const stderrText = new TextDecoder().decode(stderr);\n  expect(stderrText).toContain(\"jsxPrecompileSkipElements to contain\");\n  expect(success).toEqual(false);\n});\n\nDeno.test(\"JSX precompile - run vnode hooks\", async () => {\n  const { stdout, success } = await new Deno.Command(Deno.execPath(), {\n    args: [\"run\", \"-A\", \"main.tsx\"],\n    cwd: new URL(\"./fixture_precompile/valid\", import.meta.url),\n  }).output();\n\n  const stdoutText = new TextDecoder().decode(stdout);\n  expect(stdoutText).toContain('<img src=\"/foo.jpg?__frsh_c=');\n  expect(stdoutText).toContain('<source src=\"/bar.jpg?__frsh_c=');\n  expect(stdoutText).toContain('<div f-client-nav=\"true\">');\n  expect(stdoutText).toContain('<span f-client-nav=\"false\">');\n  expect(success).toEqual(true);\n});\n"
  },
  {
    "path": "packages/fresh/tests/test_utils.tsx",
    "content": "import type { App } from \"../src/app.ts\";\nimport { launch, type Page } from \"@astral/astral\";\nimport * as colors from \"@std/fmt/colors\";\nimport { DOMParser, HTMLElement } from \"linkedom\";\nimport { Builder, type BuildOptions } from \"../src/dev/builder.ts\";\nimport { TextLineStream } from \"@std/streams/text-line-stream\";\nimport * as path from \"@std/path\";\nimport type { ComponentChildren } from \"preact\";\nimport { expect } from \"@std/expect\";\nimport { mergeReadableStreams } from \"@std/streams\";\n\nconst browser = await launch({\n  args: [\n    \"--window-size=1280,720\",\n    ...((Deno.env.get(\"CI\") && Deno.build.os === \"linux\")\n      ? [\"--no-sandbox\"]\n      : []),\n  ],\n  headless: Deno.env.get(\"HEADLESS\") !== \"false\",\n});\n\nexport const charset = <meta charset=\"utf-8\" />;\n\nexport const favicon = (\n  <link\n    href=\"data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAAF0lEQVRIx2NgGAWjYBSMglEwCkbBSAcACBAAAeaR9cIAAAAASUVORK5CYII=\"\n    rel=\"icon\"\n    type=\"image/x-icon\"\n  />\n);\n\nexport function Doc(props: { children?: ComponentChildren; title?: string }) {\n  return (\n    <html>\n      <head>\n        {charset}\n        <title>{props.title ?? \"Test\"}</title>\n        {favicon}\n      </head>\n      <body>\n        {props.children}\n      </body>\n    </html>\n  );\n}\n\nexport const ALL_ISLAND_DIR = path.join(\n  import.meta.dirname!,\n  \"fixtures_islands\",\n);\nexport const ISLAND_GROUP_DIR = path.join(\n  import.meta.dirname!,\n  \"fixture_island_groups\",\n);\n\nexport async function buildProd(\n  options: Omit<BuildOptions, \"outDir\">,\n): Promise<<T>(app: App<T>) => void> {\n  const outDir = await Deno.makeTempDir();\n  const builder = new Builder({ outDir, ...options });\n  return await builder.build({ mode: \"production\", snapshot: \"memory\" });\n}\n\nexport async function withBrowserApp(\n  app: App<unknown>,\n  fn: (page: Page, address: string) => void | Promise<void>,\n) {\n  const aborter = new AbortController();\n  await using server = Deno.serve({\n    hostname: \"localhost\",\n    port: 0,\n    signal: aborter.signal,\n    onListen: () => {}, // Don't spam terminal with \"Listening on...\"\n  }, app.handler());\n\n  try {\n    await using page = await browser.newPage();\n    await fn(page, `http://localhost:${server.addr.port}`);\n  } finally {\n    aborter.abort();\n  }\n}\n\nexport async function withBrowser(fn: (page: Page) => void | Promise<void>) {\n  await using page = await browser.newPage();\n  try {\n    await fn(page);\n  } catch (err) {\n    const raw = await page.content();\n    const doc = parseHtml(raw);\n    const html = prettyDom(doc);\n    // deno-lint-ignore no-console\n    console.log(html);\n    throw err;\n  }\n}\n\nexport interface TestChildServerOptions {\n  cwd: string;\n  args: string[];\n  bin?: string;\n  env?: Record<string, string>;\n}\n\nexport async function withChildProcessServer(\n  options: TestChildServerOptions,\n  fn: (address: string) => void | Promise<void>,\n) {\n  const aborter = new AbortController();\n  const cp = await new Deno.Command(options.bin ?? Deno.execPath(), {\n    args: options.args,\n    stdin: \"null\",\n    stdout: \"piped\",\n    stderr: \"piped\",\n    cwd: options.cwd,\n    signal: aborter.signal,\n    env: options.env,\n  }).spawn();\n\n  const linesStdout: ReadableStream<string> = cp.stdout\n    .pipeThrough(new TextDecoderStream())\n    .pipeThrough(new TextLineStream());\n\n  const linesStderr: ReadableStream<string> = cp.stderr\n    .pipeThrough(new TextDecoderStream())\n    .pipeThrough(new TextLineStream());\n\n  const lines = mergeReadableStreams(linesStdout, linesStderr);\n\n  const output: string[] = [];\n  let address = \"\";\n  let found = false;\n  // @ts-ignore yes it does\n  for await (const raw of lines.values({ preventCancel: true })) {\n    const line = colors.stripAnsiCode(raw);\n    output.push(line);\n    const match = line.match(\n      /https?:\\/\\/[^:]+:\\d+(\\/\\w+[-\\w]*)*/g,\n    );\n    if (match) {\n      address = match[0];\n      found = true;\n      break;\n    }\n  }\n\n  if (!found) {\n    // deno-lint-ignore no-console\n    console.log(output);\n    throw new Error(`Could not find server address`);\n  }\n\n  let failed = false;\n  try {\n    await fn(address);\n  } catch (err) {\n    // deno-lint-ignore no-console\n    console.log(output);\n    failed = true;\n    throw err;\n  } finally {\n    aborter.abort();\n    await cp.status;\n    for await (const line of lines) {\n      output.push(line);\n    }\n\n    if (failed) {\n      // deno-lint-ignore no-console\n      console.log(output);\n    }\n  }\n}\n\nexport const VOID_ELEMENTS =\n  /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;\nfunction prettyDom(doc: Document) {\n  let out = colors.dim(`<!DOCTYPE ${doc.doctype?.name ?? \"\"}>\\n`);\n\n  const node = doc.documentElement;\n  out += _printDomNode(node, 0);\n\n  return out;\n}\n\nfunction _printDomNode(\n  node: HTMLElement | Text | Node,\n  indent: number,\n) {\n  const space = \"  \".repeat(indent);\n\n  if (node.nodeType === 3) {\n    return space + colors.dim(node.textContent ?? \"\") + \"\\n\";\n  } else if (node.nodeType === 8) {\n    return space + colors.dim(`<--${(node as Text).data}-->`) + \"\\n\";\n  }\n\n  let out = space;\n  if (node instanceof HTMLElement || node instanceof HTMLMetaElement) {\n    out += colors.dim(colors.cyan(\"<\"));\n    out += colors.cyan(node.localName);\n\n    for (let i = 0; i < node.attributes.length; i++) {\n      const attr = node.attributes.item(i);\n      if (attr === null) continue;\n      out += \" \" + colors.yellow(attr.name);\n      out += colors.dim(\"=\");\n      out += colors.green(`\"${attr.value}\"`);\n    }\n\n    if (VOID_ELEMENTS.test(node.localName)) {\n      out += colors.dim(colors.cyan(\">\")) + \"\\n\";\n      return out;\n    }\n\n    out += colors.dim(colors.cyan(\">\"));\n    if (node.childNodes.length) {\n      out += \"\\n\";\n\n      for (let i = 0; i < node.childNodes.length; i++) {\n        const child = node.childNodes[i];\n        out += _printDomNode(child, indent + 1);\n      }\n\n      out += space;\n    }\n\n    out += colors.dim(colors.cyan(\"</\"));\n    out += colors.cyan(node.localName);\n    out += colors.dim(colors.cyan(\">\"));\n    out += \"\\n\";\n  }\n\n  return out;\n}\n\nexport interface TestDocument extends Document {\n  debug(): void;\n}\n\nexport function parseHtml(input: string): TestDocument {\n  // deno-lint-ignore no-explicit-any\n  const doc = new DOMParser().parseFromString(input, \"text/html\") as any;\n  Object.defineProperty(doc, \"debug\", {\n    // deno-lint-ignore no-console\n    value: () => console.log(prettyDom(doc)),\n    enumerable: false,\n  });\n  return doc;\n}\n\nexport function assertSelector(doc: Document, selector: string) {\n  if (doc.querySelector(selector) === null) {\n    const html = prettyDom(doc);\n    throw new Error(\n      `Selector \"${selector}\" not found in document.\\n\\n${html}`,\n    );\n  }\n}\n\nexport function assertNotSelector(doc: Document, selector: string) {\n  if (doc.querySelector(selector) !== null) {\n    const html = prettyDom(doc);\n    throw new Error(\n      `Selector \"${selector}\" found in document.\\n\\n${html}`,\n    );\n  }\n}\n\nexport function assertMetaContent(\n  doc: Document,\n  nameOrProperty: string,\n  expected: string,\n) {\n  let el = doc.querySelector(`meta[name=\"${nameOrProperty}\"]`) as\n    | HTMLMetaElement\n    | null;\n\n  if (el === null) {\n    el = doc.querySelector(`meta[property=\"${nameOrProperty}\"]`) as\n      | HTMLMetaElement\n      | null;\n  }\n\n  if (el === null) {\n    // deno-lint-ignore no-console\n    console.log(prettyDom(doc));\n    throw new Error(\n      `No <meta>-tag found with content \"${expected}\"`,\n    );\n  }\n  expect(el.content).toEqual(expected);\n}\n\nexport async function waitForText(\n  page: Page,\n  selector: string,\n  text: string,\n) {\n  await page.waitForSelector(selector);\n  try {\n    await page.waitForFunction(\n      (sel: string, value: string) => {\n        const el = document.querySelector(sel);\n        if (el === null) return false;\n        return el.textContent === value;\n      },\n      { args: [selector, String(text)] },\n    );\n  } catch (err) {\n    const body = await page.content();\n    // deno-lint-ignore no-explicit-any\n    const pretty = prettyDom(parseHtml(body) as any);\n\n    // deno-lint-ignore no-console\n    console.log(\n      `Text \"${text}\" not found on selector \"${selector}\" in html:\\n\\n${pretty}`,\n    );\n    throw err;\n  }\n}\n\nexport async function waitFor(\n  fn: () => Promise<unknown> | unknown,\n): Promise<void> {\n  let now = Date.now();\n  const limit = now + 2000;\n\n  while (now < limit) {\n    try {\n      if (await fn()) return;\n    } catch (err) {\n      if (now > limit) {\n        throw err;\n      }\n    } finally {\n      await new Promise((r) => setTimeout(r, 250));\n      now = Date.now();\n    }\n  }\n\n  throw new Error(`Timed out`);\n}\n\nexport function getStdOutput(\n  out: Deno.CommandOutput,\n): { stdout: string; stderr: string } {\n  const decoder = new TextDecoder();\n  const stdout = colors.stripAnsiCode(decoder.decode(out.stdout));\n\n  const decoderErr = new TextDecoder();\n  const stderr = colors.stripAnsiCode(decoderErr.decode(out.stderr));\n\n  return { stdout, stderr };\n}\n\nconst ISLAND_FIXTURE_DIR = path.join(import.meta.dirname!, \"fixtures_islands\");\nconst allIslandBuilder = new Builder({});\nfor await (const entry of Deno.readDirSync(ISLAND_FIXTURE_DIR)) {\n  if (entry.name.endsWith(\".json\")) continue;\n\n  const spec = path.join(ISLAND_FIXTURE_DIR, entry.name);\n  allIslandBuilder.registerIsland(spec);\n}\n"
  },
  {
    "path": "packages/init/README.md",
    "content": "# Create a new Fresh project.\n\nThis is a CLI tool to bootstrap a new Fresh project. To do so, run this command:\n\n```sh\ndeno run -Ar jsr:@fresh/init\n```\n\nGo to [https://fresh.deno.dev/](https://fresh.deno.dev/) for more information\nabout Fresh.\n"
  },
  {
    "path": "packages/init/deno.json",
    "content": "{\n  \"name\": \"@fresh/init\",\n  \"version\": \"2.3.0\",\n  \"license\": \"MIT\",\n  \"exports\": \"./src/mod.ts\",\n  \"exclude\": [\"**/tmp/*\"],\n  \"publish\": {\n    \"include\": [\n      \"src/**/*.ts\",\n      \"deno.json\",\n      \"README.md\"\n    ],\n    \"exclude\": [\"**/*_test.*\", \"*.todo\"]\n  }\n}\n"
  },
  {
    "path": "packages/init/src/init.ts",
    "content": "// deno-lint-ignore-file no-console\nimport * as colors from \"@std/fmt/colors\";\nimport * as path from \"@std/path\";\nimport * as semver from \"@std/semver\";\nimport initConfig from \"../deno.json\" with { type: \"json\" };\n\n// Keep these as is, as we replace these version in our release script\nconst FRESH_VERSION = \"2.2.1\";\nconst FRESH_TAILWIND_VERSION = \"1.0.0\";\nconst FRESH_VITE_PLUGIN = \"1.0.0\";\nconst PREACT_VERSION = \"10.28.3\";\nconst PREACT_SIGNALS_VERSION = \"2.7.1\";\nconst TAILWINDCSS_VERSION = \"4.1.10\";\nconst TAILWINDCSS_POSTCSS_VERSION = \"4.1.10\";\nconst POSTCSS_VERSION = \"8.5.6\";\n\nfunction css(strs: TemplateStringsArray, ...exprs: string[]): string {\n  let out = \"\";\n\n  for (let i = 0; i < exprs.length; i++) {\n    out += strs[i];\n    out += String(exprs[i]);\n  }\n  out += strs.at(-1) ?? \"\";\n\n  return out;\n}\n\nexport class InitError extends Error {}\n\nfunction error(message: string): never {\n  console.error(`%cerror%c: ${message}`, \"color: red; font-weight: bold\", \"\");\n  throw new InitError();\n}\n\nexport const HELP_TEXT = `\n${\n  colors.bgRgb8(\n    colors.rgb8(\n      ` 🍋 @fresh/init${colors.rgb8(`@${initConfig.version}`, 248)} `,\n      0,\n    ),\n    121,\n  )\n}\n\nInitialize a new Fresh project. This will create all the necessary files\nfor a new project.\n\nTo generate a project in the './foobar' subdirectory:\n    ${colors.rgb8(\"deno run -Ar jsr:@fresh/init ./foobar\", 245)}\n\nTo generate a project in the current directory:\n    ${colors.rgb8(\"deno run -Ar jsr:@fresh/init .\", 245)}\n\n${colors.rgb8(\"USAGE:\", 3)}\n    ${colors.rgb8(\"deno run -Ar jsr:@fresh/init [DIRECTORY]\", 245)}\n\n${colors.rgb8(\"OPTIONS:\", 3)}\n    ${colors.rgb8(\"--force\", 2)}      Overwrite existing files\n    ${colors.rgb8(\"--tailwind\", 2)}   Use Tailwind for styling\n    ${colors.rgb8(\"--vscode\", 2)}     Setup project for VS Code\n    ${colors.rgb8(\"--docker\", 2)}     Setup Project to use Docker\n    ${colors.rgb8(\"--builder\", 2)}    Setup with builder instead of vite\n    ${colors.rgb8(\"--help, -h\", 2)}   Show this help message\n`;\n\nexport const CONFIRM_EMPTY_MESSAGE =\n  \"The target directory is not empty (files could get overwritten). Do you want to continue anyway?\";\nexport const CONFIRM_TAILWIND_MESSAGE = `Set up ${\n  colors.cyan(\"Tailwind CSS\")\n} for styling?`;\nexport const CONFIRM_VSCODE_MESSAGE = `Do you use ${colors.cyan(\"VS Code\")}?`;\nexport const CONFIRM_VITE_MESSAGE = `Set up ${\n  colors.cyan(\"Vite\")\n} for build tooling?`;\n\nexport async function initProject(\n  cwd = Deno.cwd(),\n  input: (string | number)[],\n  flags: {\n    docker?: boolean | null;\n    force?: boolean | null;\n    tailwind?: boolean | null;\n    vscode?: boolean | null;\n    builder?: boolean | null;\n    help?: boolean | null;\n    h?: boolean | null;\n    skipInstall?: boolean | null;\n  } = {},\n): Promise<void> {\n  const freshVersion = await getLatestVersion(\"@fresh/core\", FRESH_VERSION);\n\n  if (flags.help || flags.h) {\n    console.log(HELP_TEXT);\n    return;\n  }\n\n  console.log();\n  console.log(\n    colors.bgRgb8(\n      colors.rgb8(\" 🍋 Fresh: The next-gen web framework. \", 0),\n      121,\n    ),\n  );\n  console.log(`    version ${colors.rgb8(freshVersion, 4)}`);\n  console.log();\n\n  let unresolvedDirectory;\n  if (input.length !== 1) {\n    const userInput = prompt(\n      \"Project Name:\",\n      \"fresh-project\",\n    );\n    if (!userInput) {\n      error(HELP_TEXT);\n    }\n\n    unresolvedDirectory = userInput;\n  } else {\n    unresolvedDirectory = String(input[0]);\n  }\n\n  const projectDir = path.resolve(cwd, unresolvedDirectory);\n\n  try {\n    const dir = [...Deno.readDirSync(projectDir)];\n    const isEmpty = dir.length === 0 ||\n      dir.length === 1 && dir[0].name === \".git\";\n    if (\n      !isEmpty &&\n      !(flags.force === null ? confirm(CONFIRM_EMPTY_MESSAGE) : flags.force)\n    ) {\n      error(\"Directory is not empty.\");\n    }\n  } catch (err) {\n    if (!(err instanceof Deno.errors.NotFound)) {\n      throw err;\n    }\n  }\n\n  const useVite = !flags.builder;\n\n  const useDocker = flags.docker;\n  let useTailwind = flags.tailwind || false;\n  if (flags.tailwind == null) {\n    if (\n      confirm(CONFIRM_TAILWIND_MESSAGE)\n    ) {\n      useTailwind = true;\n    }\n  }\n\n  const useVSCode = flags.vscode == null\n    ? confirm(CONFIRM_VSCODE_MESSAGE)\n    : flags.vscode;\n\n  const writeFile = async (\n    pathname: string,\n    content:\n      | string\n      | Uint8Array\n      | ReadableStream<Uint8Array>\n      | Record<string, unknown>,\n  ) => await writeProjectFile(projectDir, pathname, content);\n\n  const GITIGNORE = `# dotenv environment variable files\n.env\n.env.development.local\n.env.test.local\n.env.production.local\n.env.local\n\n# Fresh build directory\n_fresh/\n# npm + other dependencies\nnode_modules/\nvendor/\n`;\n\n  await writeFile(\".gitignore\", GITIGNORE);\n\n  if (useDocker) {\n    const DENO_VERSION = Deno.version.deno;\n    const DOCKERFILE_TEXT = `\nFROM denoland/deno:${DENO_VERSION}\n\nARG GIT_REVISION\nENV DENO_DEPLOYMENT_ID=\\${GIT_REVISION}\n\nWORKDIR /app\n\nCOPY . .\nRUN deno cache _fresh/server.js\n\nEXPOSE 8000\n\nCMD [\"serve\", \"-A\", \"_fresh/server.js\"]\n\n`;\n    await writeFile(\"Dockerfile\", DOCKERFILE_TEXT);\n  }\n\n  // deno-fmt-ignore\n  const GRADIENT_CSS = css`.fresh-gradient {\n  background-color: rgb(134, 239, 172);\n  background-image: linear-gradient(\n    to right bottom,\n    rgb(219, 234, 254),\n    rgb(187, 247, 208),\n    rgb(254, 249, 195)\n  );\n}`;\n  // deno-fmt-ignore\n  const NO_TAILWIND_STYLES = css`*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n* {\n  margin: 0;\n}\nbutton {\n  color: inherit;\n}\nbutton, [role=\"button\"] {\n  cursor: pointer;\n}\ncode {\n  font-family:\n    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n    \"Courier New\", monospace;\n  font-size: 1em;\n}\nimg,\nsvg {\n  display: block;\n}\nimg,\nvideo {\n  max-width: 100%;\n  height: auto;\n}\n\nhtml {\n  line-height: 1.5;\n  -webkit-text-size-adjust: 100%;\n  font-family:\n    ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\",\n    Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif,\n    \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\",\n    \"Noto Color Emoji\";\n}\n.transition-colors {\n  transition-property: background-color, border-color, color, fill, stroke;\n  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n  transition-duration: 150ms;\n}\n.my-6 {\n  margin-bottom: 1.5rem;\n  margin-top: 1.5rem;\n}\n.text-4xl {\n  font-size: 2.25rem;\n  line-height: 2.5rem;\n}\n.mx-2 {\n  margin-left: 0.5rem;\n  margin-right: 0.5rem;\n}\n.my-4 {\n  margin-bottom: 1rem;\n  margin-top: 1rem;\n}\n.mx-auto {\n  margin-left: auto;\n  margin-right: auto;\n}\n.px-4 {\n  padding-left: 1rem;\n  padding-right: 1rem;\n}\n.py-8 {\n  padding-bottom: 2rem;\n  padding-top: 2rem;\n}\n.bg-\\\\[\\\\#86efac\\\\] {\n  background-color: #86efac;\n}\n.text-3xl {\n  font-size: 1.875rem;\n  line-height: 2.25rem;\n}\n.py-6 {\n  padding-bottom: 1.5rem;\n  padding-top: 1.5rem;\n}\n.px-2 {\n  padding-left: 0.5rem;\n  padding-right: 0.5rem;\n}\n.py-1 {\n  padding-bottom: 0.25rem;\n  padding-top: 0.25rem;\n}\n.border-gray-500 {\n  border-color: #6b7280;\n}\n.bg-white {\n  background-color: #fff;\n}\n.flex {\n  display: flex;\n}\n.gap-8 {\n  grid-gap: 2rem;\n  gap: 2rem;\n}\n.font-bold {\n  font-weight: 700;\n}\n.max-w-screen-md {\n  max-width: 768px;\n}\n.flex-col {\n  flex-direction: column;\n}\n.items-center {\n  align-items: center;\n}\n.justify-center {\n  justify-content: center;\n}\n.border-2 {\n  border-width: 2px;\n}\n.rounded-sm {\n  border-radius: 0.25rem;\n}\n.hover\\\\:bg-gray-200:hover {\n  background-color: #e5e7eb;\n}\n.tabular-nums {\n  font-variant-numeric: tabular-nums;\n}\n.min-h-screen {\n  min-height: 100vh;\n}\n\n${GRADIENT_CSS}`;\n  // deno-fmt-ignore\n  const TAILWIND_CSS = css`@import \"tailwindcss\";\n${GRADIENT_CSS}`;\n\n  const cssStyles = useTailwind ? TAILWIND_CSS : NO_TAILWIND_STYLES;\n\n  if (useVite) {\n    await writeFile(\"assets/styles.css\", cssStyles);\n    await writeFile(\n      \"client.ts\",\n      `// Import CSS files here for hot module reloading to work.\nimport \"./assets/styles.css\";`,\n    );\n  } else {\n    await writeFile(\"static/styles.css\", cssStyles);\n  }\n  // deno-fmt-ignore\n  const STATIC_LOGO =\n    `<svg width=\"40\" height=\"40\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n  <path\n    d=\"M34.092 8.845C38.929 20.652 34.092 27 30 30.5c1 3.5-2.986 4.222-4.5 2.5-4.457 1.537-13.512 1.487-20-5C2 24.5 4.73 16.714 14 11.5c8-4.5 16-7 20.092-2.655Z\"\n    fill=\"#FFDB1E\"\n  />\n  <path\n    d=\"M14 11.5c6.848-4.497 15.025-6.38 18.368-3.47C37.5 12.5 21.5 22.612 15.5 25c-6.5 2.587-3 8.5-6.5 8.5-3 0-2.5-4-5.183-7.75C2.232 23.535 6.16 16.648 14 11.5Z\"\n    fill=\"#fff\"\n    stroke=\"#FFDB1E\"\n  />\n  <path\n    d=\"M28.535 8.772c4.645 1.25-.365 5.695-4.303 8.536-3.732 2.692-6.606 4.21-7.923 4.83-.366.173-1.617-2.252-1.617-1 0 .417-.7 2.238-.934 2.326-1.365.512-4.223 1.29-5.835 1.29-3.491 0-1.923-4.754 3.014-9.122.892-.789 1.478-.645 2.283-.645-.537-.773-.534-.917.403-1.546C17.79 10.64 23 8.77 25.212 8.42c.366.014.82.35.82.629.41-.14 2.095-.388 2.503-.278Z\"\n    fill=\"#FFE600\"\n  />\n  <path\n    d=\"M14.297 16.49c.985-.747 1.644-1.01 2.099-2.526.566.121.841-.08 1.29-.701.324.466 1.657.608 2.453.701-.715.451-1.057.852-1.452 2.106-1.464-.611-3.167-.302-4.39.42Z\"\n    fill=\"#fff\"\n  />\n</svg>`;\n  await writeFile(\"static/logo.svg\", STATIC_LOGO);\n\n  try {\n    const res = await fetch(\"https://fresh.deno.dev/favicon.ico\");\n    const buf = await res.arrayBuffer();\n    await writeFile(\"static/favicon.ico\", new Uint8Array(buf));\n  } catch {\n    // Skip this and be silent if there is a network issue.\n  }\n\n  const MAIN_TS = `import { App, staticFiles } from \"fresh\";\nimport { define, type State } from \"./utils.ts\";\n\nexport const app = new App<State>();\n\napp.use(staticFiles());\n\n// Pass a shared value from a middleware\napp.use(async (ctx) => {\n  ctx.state.shared = \"hello\";\n  return await ctx.next();\n});\n\n// this is the same as the /api/:name route defined via a file. feel free to delete this!\napp.get(\"/api2/:name\", (ctx) => {\n  const name = ctx.params.name;\n  return new Response(\n    \\`Hello, \\${name.charAt(0).toUpperCase() + name.slice(1)}!\\`,\n  );\n});\n\n// this can also be defined via a file. feel free to delete this!\nconst exampleLoggerMiddleware = define.middleware((ctx) => {\n  console.log(\\`\\${ctx.req.method} \\${ctx.req.url}\\`);\n  return ctx.next();\n});\napp.use(exampleLoggerMiddleware);\n\n// Include file-system based routes here\napp.fsRoutes();`;\n  await writeFile(\"main.ts\", MAIN_TS);\n\n  const COMPONENTS_BUTTON_TSX =\n    `import type { ComponentChildren } from \"preact\";\n\nexport interface ButtonProps {\n  id?: string;\n  onClick?: () => void;\n  children?: ComponentChildren;\n  disabled?: boolean;\n}\n\nexport function Button(props: ButtonProps) {\n  return (\n    <button\n      {...props}\n      class=\"px-2 py-1 border-gray-500 border-2 rounded-sm bg-white hover:bg-gray-200 transition-colors\"\n    />\n  );\n}`;\n  await writeFile(\"components/Button.tsx\", COMPONENTS_BUTTON_TSX);\n\n  const UTILS_TS = `import { createDefine } from \"fresh\";\n\n// This specifies the type of \"ctx.state\" which is used to share\n// data among middlewares, layouts and routes.\nexport interface State {\n  shared: string;\n}\n\nexport const define = createDefine<State>();`;\n  await writeFile(\"utils.ts\", UTILS_TS);\n\n  const ROUTES_HOME = `import { useSignal } from \"@preact/signals\";\nimport { Head } from \"fresh/runtime\";\nimport { define } from \"../utils.ts\";\nimport Counter from \"../islands/Counter.tsx\";\n\nexport default define.page(function Home(ctx) {\n  const count = useSignal(3);\n\n  console.log(\"Shared value \" + ctx.state.shared);\n\n  return (\n    <div class=\"px-4 py-8 mx-auto fresh-gradient min-h-screen\">\n      <Head>\n        <title>Fresh counter</title>\n      </Head>\n      <div class=\"max-w-screen-md mx-auto flex flex-col items-center justify-center\">\n        <img\n          class=\"my-6\"\n          src=\"/logo.svg\"\n          width=\"128\"\n          height=\"128\"\n          alt=\"the Fresh logo: a sliced lemon dripping with juice\"\n        />\n        <h1 class=\"text-4xl font-bold\">Welcome to Fresh</h1>\n        <p class=\"my-4\">\n          Try updating this message in the\n          <code class=\"mx-2\">./routes/index.tsx</code> file, and refresh.\n        </p>\n        <Counter count={count} />\n      </div>\n    </div>\n  );\n});`;\n  await writeFile(\"routes/index.tsx\", ROUTES_HOME);\n\n  const APP_WRAPPER = `import { define } from \"../utils.ts\";\n\nexport default define.page(function App({ Component }) {\n  return (\n    <html>\n      <head>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>${path.basename(projectDir)}</title>${\n    useVite ? \"\" : `\\n        <link rel=\"stylesheet\" href=\"/styles.css\" />`\n  }\n      </head>\n      <body>\n        <Component />\n      </body>\n    </html>\n  );\n});`;\n  await writeFile(\"routes/_app.tsx\", APP_WRAPPER);\n\n  const API_NAME = `import { define } from \"../../utils.ts\";\n\nexport const handler = define.handlers({\n  GET(ctx) {\n    const name = ctx.params.name;\n    return new Response(\n      \\`Hello, \\${name.charAt(0).toUpperCase() + name.slice(1)}!\\`,\n    );\n  },\n});`;\n  await writeFile(\"routes/api/[name].tsx\", API_NAME);\n\n  const ISLANDS_COUNTER_TSX = `import type { Signal } from \"@preact/signals\";\nimport { Button } from \"../components/Button.tsx\";\n\ninterface CounterProps {\n  count: Signal<number>;\n}\n\nexport default function Counter(props: CounterProps) {\n  return (\n    <div class=\"flex gap-8 py-6\">\n      <Button id=\"decrement\" onClick={() => props.count.value -= 1}>-1</Button>\n      <p class=\"text-3xl tabular-nums\">{props.count}</p>\n      <Button id=\"increment\" onClick={() => props.count.value += 1}>+1</Button>\n    </div>\n  );\n}`;\n  await writeFile(\"islands/Counter.tsx\", ISLANDS_COUNTER_TSX);\n\n  const DEV_TS = `#!/usr/bin/env -S deno run -A --watch=static/,routes/\n${useTailwind ? `import { tailwind } from \"@fresh/plugin-tailwind\";\\n` : \"\"}\nimport { Builder } from \"fresh/dev\";\n\nconst builder = new Builder();\n${useTailwind ? \"tailwind(builder);\" : \"\"}\nif (Deno.args.includes(\"build\")) {\n  await builder.build();\n} else {\n  await builder.listen(() => import(\"./main.ts\"));\n}`;\n\n  if (!useVite) {\n    await writeFile(\"dev.ts\", DEV_TS);\n  }\n\n  const denoJson = {\n    nodeModulesDir: \"manual\",\n    tasks: {\n      check: \"deno fmt --check . && deno lint . && deno check\",\n      dev: \"deno run -A --watch=static/,routes/ dev.ts\",\n      build: \"deno run -A dev.ts build\",\n      start: \"deno serve -A _fresh/server.js\",\n      update: \"deno run -A -r jsr:@fresh/update .\",\n    },\n    lint: {\n      rules: {\n        tags: [\"fresh\", \"recommended\"],\n      },\n    },\n    exclude: [\"**/_fresh/*\"],\n    imports: {\n      \"@/\": \"./\",\n      \"fresh\": `jsr:@fresh/core@^${freshVersion}`,\n      \"preact\": `npm:preact@^${PREACT_VERSION}`,\n      \"@preact/signals\": `npm:@preact/signals@^${PREACT_SIGNALS_VERSION}`,\n    } as Record<string, string>,\n    compilerOptions: {\n      lib: [\"dom\", \"dom.asynciterable\", \"dom.iterable\", \"deno.ns\"],\n      jsx: \"precompile\",\n      jsxImportSource: \"preact\",\n      jsxPrecompileSkipElements: [\n        \"a\",\n        \"img\",\n        \"source\",\n        \"body\",\n        \"html\",\n        \"head\",\n        \"title\",\n        \"meta\",\n        \"script\",\n        \"link\",\n        \"style\",\n        \"base\",\n        \"noscript\",\n        \"template\",\n      ],\n    } as Record<string, unknown>,\n  };\n\n  if (useVite) {\n    denoJson.compilerOptions.types = [\"vite/client\"];\n    denoJson.tasks.dev = \"vite\";\n    denoJson.tasks.build = \"vite build\";\n\n    const vitePluginVersion = await getLatestVersion(\n      \"@fresh/plugin-vite\",\n      FRESH_VITE_PLUGIN,\n    );\n\n    denoJson.imports[\"@fresh/plugin-vite\"] =\n      `jsr:@fresh/plugin-vite@^${vitePluginVersion}`;\n    denoJson.imports[\"vite\"] = \"npm:vite@^7.1.3\";\n\n    if (useTailwind) {\n      denoJson.imports[\"tailwindcss\"] =\n        `npm:tailwindcss@^${TAILWINDCSS_VERSION}`;\n      denoJson.imports[\"@tailwindcss/vite\"] = `npm:@tailwindcss/vite@^4.1.12`;\n    }\n  } else if (useTailwind) {\n    denoJson.imports[\"tailwindcss\"] = `npm:tailwindcss@^${TAILWINDCSS_VERSION}`;\n    denoJson.imports[\"@fresh/plugin-tailwind\"] =\n      `jsr:@fresh/plugin-tailwind@^${FRESH_TAILWIND_VERSION}`;\n    denoJson.imports[\"@tailwindcss/postcss\"] =\n      `npm:@tailwindcss/postcss@^${TAILWINDCSS_POSTCSS_VERSION}`;\n    denoJson.imports[\"postcss\"] = `npm:postcss@^${POSTCSS_VERSION}`;\n  }\n\n  await writeFile(\"deno.json\", denoJson);\n\n  if (useVite) {\n    let viteConfig = `import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\\n`;\n\n    if (useTailwind) {\n      viteConfig += `import tailwindcss from \"@tailwindcss/vite\";\\n`;\n    }\n\n    viteConfig += `\\nexport default defineConfig({\n  plugins: [fresh()${useTailwind ? \", tailwindcss()\" : \"\"}],\n});`;\n\n    await writeFile(\"vite.config.ts\", viteConfig);\n  }\n\n  const README_MD = `# Fresh project\n\nYour new Fresh project is ready to go. You can follow the Fresh \"Getting\nStarted\" guide here: https://fresh.deno.dev/docs/getting-started\n\n### Usage\n\nMake sure to install Deno:\nhttps://docs.deno.com/runtime/getting_started/installation\n\nThen start the project in development mode:\n\n\\`\\`\\`\ndeno task dev\n\\`\\`\\`\n\nThis will watch the project directory and restart as necessary.`;\n  await writeFile(\"README.md\", README_MD);\n\n  if (useVSCode) {\n    const vscodeSettings = {\n      \"deno.enable\": true,\n      \"deno.lint\": true,\n      \"editor.defaultFormatter\": \"denoland.vscode-deno\",\n      \"[typescriptreact]\": {\n        \"editor.defaultFormatter\": \"denoland.vscode-deno\",\n      },\n      \"[typescript]\": {\n        \"editor.defaultFormatter\": \"denoland.vscode-deno\",\n      },\n      \"[javascriptreact]\": {\n        \"editor.defaultFormatter\": \"denoland.vscode-deno\",\n      },\n      \"[javascript]\": {\n        \"editor.defaultFormatter\": \"denoland.vscode-deno\",\n      },\n      \"files.associations\": useTailwind\n        ? {\n          \"*.css\": \"tailwindcss\",\n        }\n        : undefined,\n    };\n\n    await writeFile(\".vscode/settings.json\", vscodeSettings);\n\n    const recommendations = [\"denoland.vscode-deno\"];\n    if (useTailwind) recommendations.push(\"bradlc.vscode-tailwindcss\");\n    await writeFile(\".vscode/extensions.json\", { recommendations });\n  }\n\n  if (!flags.skipInstall) {\n    console.log(\"Installing dependencies...\");\n    await new Deno.Command(Deno.execPath(), {\n      cwd: unresolvedDirectory,\n      args: [\"install\"],\n    }).output();\n    console.log(\"Installing dependencies...%cdone!\", \"color: green\");\n  }\n\n  // Specifically print unresolvedDirectory, rather than resolvedDirectory in order to\n  // not leak personal info (e.g. `/Users/MyName`)\n  console.log(\n    \"\\n%cProject initialized!\\n\",\n    \"color: green; font-weight: bold\",\n  );\n\n  if (unresolvedDirectory !== \".\") {\n    console.log(\n      `Enter your project directory using %ccd ${unresolvedDirectory}%c.`,\n      \"color: cyan\",\n      \"\",\n    );\n  }\n  console.log(\n    \"Run %cdeno task dev%c to start the project. %cCTRL-C%c to stop.\",\n    \"color: cyan\",\n    \"\",\n    \"color: cyan\",\n    \"\",\n  );\n  console.log();\n  console.log(\n    \"Stuck? Join our Discord %chttps://discord.gg/deno\",\n    \"color: cyan\",\n    \"\",\n  );\n  console.log();\n  console.log(\n    \"%cHappy hacking! 🦕\",\n    \"color: gray\",\n  );\n}\n\nasync function writeProjectFile(\n  projectDir: string,\n  pathname: string,\n  content:\n    | string\n    | Uint8Array\n    | ReadableStream<Uint8Array>\n    | Record<string, unknown>,\n) {\n  const filePath = path.join(\n    projectDir,\n    ...pathname.split(\"/\").filter(Boolean),\n  );\n  try {\n    await Deno.mkdir(\n      path.dirname(filePath),\n      { recursive: true },\n    );\n    if (typeof content === \"string\") {\n      let formatted = content;\n      if (!content.endsWith(\"\\n\\n\")) {\n        formatted += \"\\n\";\n      }\n      await Deno.writeTextFile(filePath, formatted);\n    } else if (\n      content instanceof Uint8Array || content instanceof ReadableStream\n    ) {\n      await Deno.writeFile(filePath, content);\n    } else {\n      await Deno.writeTextFile(\n        filePath,\n        JSON.stringify(content, null, 2) + \"\\n\",\n      );\n    }\n  } catch (err) {\n    if (!(err instanceof Deno.errors.AlreadyExists)) {\n      throw err;\n    }\n  }\n}\n\ninterface JsrMeta {\n  scope: string;\n  name: string;\n  latest: string | null;\n  versions: Record<string, unknown>;\n}\n\nasync function getLatestVersion(\n  pkg: string,\n  fallback: string,\n): Promise<string> {\n  // deno-lint-ignore no-explicit-any\n  if ((globalThis as any).INIT_TEST) {\n    return fallback;\n  }\n\n  try {\n    const res = await fetch(`https://jsr.io/${pkg}/meta.json`);\n    const json = (await res.json()) as JsrMeta;\n\n    if (json.latest !== null) {\n      return json.latest;\n    }\n\n    const versions = Object.keys(json.versions);\n    if (versions.length === 0) throw new Error(\"No versions\");\n\n    versions.sort((a, b) => {\n      const s1 = semver.parse(a);\n      const s2 = semver.parse(b);\n      return semver.compare(s1, s2);\n    });\n\n    return versions.at(-1)!;\n  } catch {\n    console.log(\n      `Could not fetch latest ${pkg} version. Falling back to: ${fallback}`,\n    );\n    return fallback;\n  }\n}\n"
  },
  {
    "path": "packages/init/src/init_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport {\n  CONFIRM_TAILWIND_MESSAGE,\n  CONFIRM_VITE_MESSAGE,\n  CONFIRM_VSCODE_MESSAGE,\n  HELP_TEXT,\n  initProject,\n} from \"./init.ts\";\nimport * as path from \"@std/path\";\nimport { getStdOutput, withBrowser } from \"../../fresh/tests/test_utils.tsx\";\nimport { waitForText } from \"../../fresh/tests/test_utils.tsx\";\nimport { withChildProcessServer } from \"../../fresh/tests/test_utils.tsx\";\nimport { withTmpDir as withTmpDirBase } from \"../../fresh/src/test_utils.ts\";\nimport { stub } from \"@std/testing/mock\";\n\n// deno-lint-ignore no-explicit-any\n(globalThis as any).INIT_TEST = true;\n\nconst testInitProject: typeof initProject = async (cwd, input, flags = {}) => {\n  return await initProject(cwd, input, { ...flags, skipInstall: true });\n};\n\nfunction stubPrompt(result: string) {\n  return stub(globalThis, \"prompt\", () => result);\n}\n\nfunction stubConfirm(steps: Record<string, boolean> = {}) {\n  return stub(\n    globalThis,\n    \"confirm\",\n    (message) => message ? steps[message] : false,\n  );\n}\n\nfunction stubLogs() {\n  return stub(\n    console,\n    \"log\",\n    () => undefined,\n  );\n}\n\nfunction withTmpDir(): Promise<{ dir: string } & AsyncDisposable> {\n  // Windows need temporary files in the repository root\n  if (Deno.build.os === \"windows\") {\n    const dir = path.join(import.meta.dirname!, \"..\", \"..\", \"..\", \"..\");\n    return withTmpDirBase({ dir, prefix: \"tmp_\" });\n  }\n  return withTmpDirBase();\n}\n\nasync function patchProject(dir: string): Promise<void> {\n  const jsonPath = path.join(dir, \"deno.json\");\n  const json = JSON.parse(await Deno.readTextFile(jsonPath));\n\n  const linkedDir = path.join(dir, \"_linked\");\n  try {\n    await Deno.mkdir(linkedDir, { recursive: true });\n  } catch (err) {\n    if (!(err instanceof Deno.errors.AlreadyExists)) {\n      throw err;\n    }\n  }\n\n  const ignore = (entry: Deno.DirEntry, _full: string) => {\n    return /^(\\.|tmp_)/.test(entry.name) ||\n      /_test\\.[tj]sx?$/.test(entry.name) ||\n      entry.name === \"tests\" || entry.name === \"test\";\n  };\n\n  await copyRecursive(\n    path.join(import.meta.dirname!, \"..\", \"..\", \"fresh\"),\n    path.join(linkedDir, \"fresh\"),\n    ignore,\n  );\n\n  await copyRecursive(\n    path.join(import.meta.dirname!, \"..\", \"..\", \"build-id\"),\n    path.join(linkedDir, \"build-id\"),\n    ignore,\n  );\n\n  json.workspace = [\"./_linked/*\"];\n  json.exclude = [...json.exclude ?? [], \"**/_linked/*\"];\n\n  // assert with this stricter rule, before adding it to initialized projects\n  json.lint.rules.include = [\"verbatim-module-syntax\"];\n\n  await Deno.writeTextFile(jsonPath, JSON.stringify(json, null, 2) + \"\\n\");\n  const installProc = await new Deno.Command(Deno.execPath(), {\n    cwd: dir,\n    args: [\"install\"],\n  }).output();\n\n  if (installProc.code !== 0) {\n    const { stderr, stdout } = getStdOutput(installProc);\n    // deno-lint-ignore no-console\n    console.log(stderr);\n    // deno-lint-ignore no-console\n    console.log(stdout);\n  }\n\n  expect(installProc.code).toEqual(0);\n}\n\nasync function copyRecursive(\n  from: string,\n  to: string,\n  ignore?: (entry: Deno.DirEntry, full: string) => boolean,\n): Promise<void> {\n  for await (const entry of Deno.readDir(from)) {\n    const source = path.join(from, entry.name);\n    const target = path.join(to, entry.name);\n\n    if (ignore && ignore(entry, source)) {\n      continue;\n    }\n\n    if (entry.isFile) {\n      try {\n        await Deno.mkdir(to, { recursive: true });\n      } catch (err) {\n        if (!(err instanceof Deno.errors.AlreadyExists)) {\n          throw err;\n        }\n      }\n      await Deno.copyFile(source, target);\n    } else if (entry.isDirectory) {\n      await copyRecursive(source, target, ignore);\n    }\n  }\n}\n\nasync function expectProjectFile(dir: string, pathname: string) {\n  const filePath = path.join(dir, ...pathname.split(\"/\").filter(Boolean));\n  const stat = await Deno.stat(filePath);\n  if (!stat.isFile) {\n    throw new Error(`Not a project file: ${filePath}`);\n  }\n}\n\nasync function expectNotProjectFile(dir: string, pathname: string) {\n  const filePath = path.join(dir, ...pathname.split(\"/\").filter(Boolean));\n  try {\n    const stat = await Deno.stat(filePath);\n    if (stat.isFile) {\n      throw new Error(`A project file but expected it not to be: ${filePath}`);\n    }\n  } catch (err) {\n    if (!(err instanceof Deno.errors.NotFound)) {\n      throw err;\n    }\n  }\n}\n\nasync function readProjectFile(dir: string, pathname: string): Promise<string> {\n  const filePath = path.join(dir, ...pathname.split(\"/\").filter(Boolean));\n  const content = await Deno.readTextFile(filePath);\n  return content;\n}\n\nDeno.test(\"init - show help\", async () => {\n  using logs = stubLogs();\n\n  await testInitProject(\"\", [], { help: true });\n  const args = logs.calls.flatMap((c) => c.args);\n  const out = args.join(\"\\n\");\n  await testInitProject(\"\", [], { h: true });\n\n  expect(out).toBe(HELP_TEXT);\n  expect(args).toEqual(logs.calls.flatMap((c) => c.args).slice(args.length));\n});\n\nDeno.test(\"init - new project\", async () => {\n  await using tmp = await withTmpDir();\n  using _promptStub = stubPrompt(\"fresh-init\");\n  using _confirmStub = stubConfirm();\n\n  await testInitProject(tmp.dir, [], { builder: true });\n});\n\nDeno.test(\"init - create project dir\", async () => {\n  await using tmp = await withTmpDir();\n  const dir = tmp.dir;\n  using _promptStub = stubPrompt(\"fresh-init\");\n  using _confirmStub = stubConfirm();\n  await testInitProject(dir, [], { builder: true });\n\n  const root = path.join(dir, \"fresh-init\");\n  await expectProjectFile(root, \"deno.json\");\n  await expectProjectFile(root, \"main.ts\");\n  await expectProjectFile(root, \"dev.ts\");\n  await expectProjectFile(root, \".gitignore\");\n  await expectProjectFile(root, \"static/styles.css\");\n});\n\nDeno.test(\"init - with tailwind\", async () => {\n  await using tmp = await withTmpDir();\n  const dir = tmp.dir;\n  using _promptStub = stubPrompt(\".\");\n  using _confirmStub = stubConfirm({\n    [CONFIRM_TAILWIND_MESSAGE]: true,\n  });\n  await testInitProject(dir, [], { builder: true });\n\n  const css = await readProjectFile(dir, \"static/styles.css\");\n  expect(css).toMatch(/@import \"tailwindcss\"/);\n\n  const main = await readProjectFile(dir, \"main.ts\");\n  const dev = await readProjectFile(dir, \"dev.ts\");\n  expect(main).not.toMatch(/tailwind/);\n  expect(dev).toMatch(/tailwind/);\n});\n\nDeno.test(\"init - with vscode\", async () => {\n  await using tmp = await withTmpDir();\n  const dir = tmp.dir;\n  using _promptStub = stubPrompt(\".\");\n  using _confirmStub = stubConfirm({\n    [CONFIRM_VSCODE_MESSAGE]: true,\n  });\n  await testInitProject(dir, [], { builder: true });\n\n  await expectProjectFile(dir, \".vscode/settings.json\");\n  await expectProjectFile(dir, \".vscode/extensions.json\");\n});\n\nDeno.test({\n  name: \"init - fmt, lint, and type check project\",\n  // Ignore this test on canary due to different formatting\n  // behaviours when the formatter changes.\n  ignore: Deno.version.deno.includes(\"+\"),\n  fn: async () => {\n    await using tmp = await withTmpDir();\n    const dir = tmp.dir;\n    using _promptStub = stubPrompt(\".\");\n    using _confirmStub = stubConfirm();\n    await testInitProject(dir, [], { builder: true });\n    await expectProjectFile(dir, \"main.ts\");\n    await expectProjectFile(dir, \"dev.ts\");\n\n    await patchProject(dir);\n\n    const check = await new Deno.Command(Deno.execPath(), {\n      args: [\"task\", \"check\"],\n      cwd: dir,\n      stderr: \"inherit\",\n      stdout: \"inherit\",\n    }).output();\n    expect(check.code).toEqual(0);\n  },\n});\n\nDeno.test(\n  \"init with tailwind - fmt, lint, and type check project\",\n  async () => {\n    await using tmp = await withTmpDir();\n    const dir = tmp.dir;\n    using _promptStub = stubPrompt(\".\");\n    using _confirmStub = stubConfirm({\n      [CONFIRM_TAILWIND_MESSAGE]: true,\n    });\n\n    await testInitProject(dir, [], { builder: true });\n    await expectProjectFile(dir, \"main.ts\");\n    await expectProjectFile(dir, \"dev.ts\");\n\n    await patchProject(dir);\n\n    const check = await new Deno.Command(Deno.execPath(), {\n      args: [\"task\", \"check\"],\n      cwd: dir,\n      stderr: \"inherit\",\n      stdout: \"inherit\",\n    }).output();\n    expect(check.code).toEqual(0);\n  },\n);\n\nDeno.test({\n  // TODO: For some reason this test is flaky in GitHub CI. It works when\n  // testing locally on windows though. Not sure what's going on.\n  ignore: Deno.build.os === \"windows\" && Deno.env.get(\"CI\") !== undefined,\n  name: \"init - can start dev server\",\n  fn: async () => {\n    await using tmp = await withTmpDir();\n    const dir = tmp.dir;\n    using _promptStub = stubPrompt(\".\");\n    using _confirmStub = stubConfirm();\n    await testInitProject(dir, [], { builder: true });\n    await expectProjectFile(dir, \"main.ts\");\n    await expectProjectFile(dir, \"dev.ts\");\n\n    await patchProject(dir);\n    await withChildProcessServer(\n      { cwd: dir, args: [\"task\", \"dev\"] },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(address);\n          await page.locator(\"#decrement\").click();\n          await waitForText(page, \"button + p\", \"2\");\n        });\n      },\n    );\n  },\n});\n\nDeno.test(\"init - can start built project\", async () => {\n  await using tmp = await withTmpDir();\n  const dir = tmp.dir;\n  using _promptStub = stubPrompt(\".\");\n  using _confirmStub = stubConfirm();\n  await testInitProject(dir, [], { builder: true });\n  await expectProjectFile(dir, \"main.ts\");\n  await expectProjectFile(dir, \"dev.ts\");\n\n  await patchProject(dir);\n\n  // Build\n  await new Deno.Command(Deno.execPath(), {\n    args: [\"task\", \"build\"],\n    stdin: \"null\",\n    stdout: \"piped\",\n    stderr: \"piped\",\n    cwd: dir,\n  }).output();\n\n  await withChildProcessServer(\n    { cwd: dir, args: [\"serve\", \"-A\", \"--port\", \"0\", \"_fresh/server.js\"] },\n    async (address) => {\n      await withBrowser(async (page) => {\n        await page.goto(address);\n        await page.locator(\"button\").click();\n        await waitForText(page, \"button + p\", \"2\");\n      });\n    },\n  );\n});\n\nDeno.test(\"init - errors on missing build cache in prod\", async () => {\n  await using tmp = await withTmpDir();\n  const dir = tmp.dir;\n  using _promptStub = stubPrompt(\".\");\n  using _confirmStub = stubConfirm();\n  await testInitProject(dir, [], { builder: true });\n  await expectProjectFile(dir, \"main.ts\");\n  await expectProjectFile(dir, \"dev.ts\");\n\n  await patchProject(dir);\n\n  const cp = await new Deno.Command(Deno.execPath(), {\n    args: [\"task\", \"start\"],\n    stdin: \"null\",\n    stdout: \"piped\",\n    stderr: \"piped\",\n    cwd: dir,\n  }).output();\n\n  const { stderr } = getStdOutput(cp);\n  expect(cp.code).toEqual(1);\n\n  expect(stderr).toMatch(/Module not found/);\n});\n\n// There is a peerDependency issue with links\nDeno.test.ignore(\"init - vite dev server\", async () => {\n  await using tmp = await withTmpDir();\n  const dir = tmp.dir;\n  using _promptStub = stubPrompt(\".\");\n  using _confirmStub = stubConfirm();\n  await testInitProject(dir, [], {});\n\n  await expectProjectFile(dir, \"vite.config.ts\");\n  await expectNotProjectFile(dir, \"dev.ts\");\n\n  await patchProject(dir);\n\n  await withChildProcessServer(\n    { cwd: dir, args: [\"task\", \"dev\"] },\n    async (address) => {\n      await withBrowser(async (page) => {\n        await page.goto(address);\n        await page.locator(\"#decrement\").click();\n        await waitForText(page, \"button + p\", \"2\");\n      });\n    },\n  );\n});\n\n// There is a peerDependency issue with links\nDeno.test.ignore(\"init - vite build\", async () => {\n  await using tmp = await withTmpDir();\n  const dir = tmp.dir;\n  using _promptStub = stubPrompt(\".\");\n  using _confirmStub = stubConfirm();\n  await testInitProject(dir, [], {});\n\n  await expectProjectFile(dir, \"vite.config.ts\");\n\n  await patchProject(dir);\n\n  // Build\n  await new Deno.Command(Deno.execPath(), {\n    args: [\"task\", \"build\"],\n    stdin: \"null\",\n    stdout: \"piped\",\n    stderr: \"piped\",\n    cwd: dir,\n  }).output();\n\n  await withChildProcessServer(\n    { cwd: dir, args: [\"serve\", \"-A\", \"--port\", \"0\", \"_fresh/server.js\"] },\n    async (address) => {\n      await withBrowser(async (page) => {\n        await page.goto(address);\n        await page.locator(\"button\").click();\n        await waitForText(page, \"button + p\", \"2\");\n      });\n    },\n  );\n});\n\nDeno.test(\"init - with vite\", async () => {\n  await using tmp = await withTmpDir();\n  const dir = tmp.dir;\n  using _promptStub = stubPrompt(\".\");\n  using _confirmStub = stubConfirm({\n    [CONFIRM_VITE_MESSAGE]: true,\n  });\n  await testInitProject(dir, [], {});\n\n  await expectProjectFile(dir, \"vite.config.ts\");\n  await expectNotProjectFile(dir, \"dev.ts\");\n});\n"
  },
  {
    "path": "packages/init/src/mod.ts",
    "content": "import { parseArgs } from \"@std/cli/parse-args\";\nimport { initProject } from \"./init.ts\";\nimport { InitError } from \"./init.ts\";\n\nconst flags = parseArgs(Deno.args, {\n  boolean: [\"force\", \"tailwind\", \"vscode\", \"docker\", \"help\", \"builder\"],\n  default: {\n    force: null,\n    tailwind: null,\n    vscode: null,\n    docker: null,\n    builder: null,\n  },\n  alias: {\n    help: \"h\",\n  },\n});\n\ntry {\n  await initProject(Deno.cwd(), flags._, flags);\n} catch (err) {\n  if (err instanceof InitError) {\n    Deno.exit(1);\n  }\n  throw err;\n}\n"
  },
  {
    "path": "packages/plugin-tailwindcss/README.md",
    "content": "# Tailwind CSS plugin for Fresh\n\nA Tailwind CSS plugin to use in Fresh.\n\n## Basic Usage\n\n```ts\n// dev.ts\nimport { Builder } from \"fresh/dev\";\nimport { app } from \"./main.ts\";\nimport { tailwind } from \"@fresh/plugin-tailwind\";\n\nconst builder = new Builder();\ntailwind(builder, app);\n\nif (Deno.args.includes(\"build\")) {\n  builder.build(app);\n} else {\n  builder.listen(app);\n}\n```\n\n## Option Configuration\n\n```ts\n// dev.ts\nimport { Builder } from \"fresh/dev\";\nimport { app } from \"./main.ts\";\nimport { tailwind } from \"@fresh/plugin-tailwind\";\n\nconst builder = new Builder();\ntailwind(builder, app, {\n  // Exclude certain files from processing\n  exclude: [\"/admin/**\", \"*.temp.css\"],\n\n  // Force optimization (defaults to production mode)\n  optimize: true,\n\n  // Exclude base styles\n  base: null,\n});\n\nif (Deno.args.includes(\"build\")) {\n  builder.build(app);\n} else {\n  builder.listen(app);\n}\n```\n\nTo learn more about Fresh go to\n[https://fresh.deno.dev/](https://fresh.deno.dev/).\n"
  },
  {
    "path": "packages/plugin-tailwindcss/deno.json",
    "content": "{\n  \"name\": \"@fresh/plugin-tailwind\",\n  \"version\": \"1.0.0\",\n  \"license\": \"MIT\",\n  \"exports\": \"./src/mod.ts\"\n}\n"
  },
  {
    "path": "packages/plugin-tailwindcss/src/mod.ts",
    "content": "import type { Builder } from \"fresh/dev\";\nimport twPostcss from \"@tailwindcss/postcss\";\nimport postcss from \"postcss\";\nimport type { TailwindPluginOptions } from \"./types.ts\";\n\n// Re-export types for public API\nexport type { TailwindPluginOptions } from \"./types.ts\";\n\nexport function tailwind(\n  builder: Builder,\n  options: TailwindPluginOptions = {},\n): void {\n  const { exclude, ...tailwindOptions } = options;\n  const instance = postcss(twPostcss({\n    optimize: builder.config.mode === \"production\",\n    ...tailwindOptions,\n  }));\n\n  builder.onTransformStaticFile(\n    { pluginName: \"tailwind\", filter: /\\.css$/, exclude },\n    async (args) => {\n      const res = await instance.process(args.text, {\n        from: args.path,\n      });\n      return {\n        content: res.content,\n        map: res.map?.toString(),\n      };\n    },\n  );\n}\n"
  },
  {
    "path": "packages/plugin-tailwindcss/src/types.ts",
    "content": "import type { OnTransformOptions } from \"fresh/dev\";\n\n// based on the original code, this type is used to define options for the Tailwind plugin\ntype PluginOptions = {\n  /**\n   * Base CSS to be included. Set to null to exclude base styles.\n   */\n  base?: string;\n  /**\n   * Enable or disable CSS optimization. Defaults to true if Fresh is in production mode and false otherwise.\n   * Can be a boolean or an object with minify options.\n   * @default app.config.mode === \"production\"\n   */\n  optimize?: boolean | {\n    minify?: boolean;\n  };\n};\n\nexport interface TailwindPluginOptions extends PluginOptions {\n  /** Exclude paths or globs that should not be processed */\n  exclude?: OnTransformOptions[\"exclude\"];\n}\n"
  },
  {
    "path": "packages/plugin-tailwindcss-v3/README.md",
    "content": "# Tailwind CSS v3 plugin for Fresh\n\nA Tailwind CSS v3 plugin to use in Fresh.\n\n> [info]: If you want to use latest tailwindcss use `@fresh/plugin-tailwindcss`\n> instead.\n\n## Basic Usage\n\n```ts\n// dev.ts\nimport { Builder } from \"fresh/dev\";\nimport { tailwind } from \"@fresh/plugin-tailwind-v3\";\n\nconst builder = new Builder();\ntailwind(builder);\n\nif (Deno.args.includes(\"build\")) {\n  builder.build();\n} else {\n  builder.listen(() => import(\"./main.ts\"));\n}\n```\n\n## Option Configuration\n\n```ts\ntailwind(builder, {\n  // Exclude certain files from processing\n  exclude: [\"/admin/**\", \"*.temp.css\"],\n\n  // Force optimization (defaults to production mode)\n  optimize: true,\n\n  // Exclude base styles\n  base: null,\n});\n```\n\nTo learn more about Fresh go to\n[https://fresh.deno.dev/](https://fresh.deno.dev/).\n"
  },
  {
    "path": "packages/plugin-tailwindcss-v3/deno.json",
    "content": "{\n  \"name\": \"@fresh/plugin-tailwind-v3\",\n  \"version\": \"1.0.1\",\n  \"license\": \"MIT\",\n  \"exports\": \"./src/mod.ts\",\n  \"imports\": {\n    \"autoprefixer\": \"npm:autoprefixer@^10.4.21\",\n    \"cssnano\": \"npm:cssnano@^6.1.2\",\n    \"postcss\": \"npm:postcss@^8.5.6\",\n    \"tailwindcss\": \"npm:tailwindcss@^3.4.17\"\n  }\n}\n"
  },
  {
    "path": "packages/plugin-tailwindcss-v3/src/mod.ts",
    "content": "import type { Builder, ResolvedBuildConfig } from \"fresh/dev\";\nimport tailwindCss, { type Config } from \"tailwindcss\";\nimport postcss from \"postcss\";\nimport cssnano from \"cssnano\";\nimport autoprefixer from \"autoprefixer\";\nimport * as path from \"@std/path\";\n\nexport interface AutoprefixerOptions {\n  /** environment for `Browserslist` */\n  env?: string;\n\n  /** should Autoprefixer use Visual Cascade, if CSS is uncompressed */\n  cascade?: boolean;\n\n  /** should Autoprefixer add prefixes. */\n  add?: boolean;\n\n  /** should Autoprefixer [remove outdated] prefixes */\n  remove?: boolean;\n\n  /** should Autoprefixer add prefixes for @supports parameters. */\n  supports?: boolean;\n\n  /** should Autoprefixer add prefixes for flexbox properties */\n  flexbox?: boolean | \"no-2009\";\n\n  /** should Autoprefixer add IE 10-11 prefixes for Grid Layout properties */\n  grid?: boolean | \"autoplace\" | \"no-autoplace\";\n\n  /** custom usage statistics for > 10% in my stats browsers query */\n  stats?: {\n    [browser: string]: {\n      [version: string]: number;\n    };\n  };\n\n  /**\n   * list of queries for target browsers.\n   * Try to not use it.\n   * The best practice is to use `.browserslistrc` config or `browserslist` key in `package.json`\n   * to share target browsers with Babel, ESLint and Stylelint\n   */\n  overrideBrowserslist?: string | string[];\n\n  /** do not raise error on unknown browser version in `Browserslist` config. */\n  ignoreUnknownVersions?: boolean;\n}\n\nexport interface TailwindPluginOptions {\n  autoprefixer?: AutoprefixerOptions;\n}\n\nexport function tailwind(\n  builder: Builder,\n  options: TailwindPluginOptions = {},\n): void {\n  const processor = initTailwind(builder.config, options);\n\n  builder.onTransformStaticFile(\n    { pluginName: \"tailwind\", filter: /\\.css$/ },\n    async (args) => {\n      const instance = await processor;\n      const res = await instance.process(args.text, {\n        from: args.path,\n      });\n      return {\n        content: res.content,\n        map: res.map?.toString(),\n      };\n    },\n  );\n}\n\nconst CONFIG_EXTENSIONS = [\"ts\", \"js\", \"mjs\"];\n\nasync function findTailwindConfigFile(directory: string): Promise<string> {\n  let dir = directory;\n  while (true) {\n    for (let i = 0; i < CONFIG_EXTENSIONS.length; i++) {\n      const ext = CONFIG_EXTENSIONS[i];\n      const filePath = path.join(dir, `tailwind.config.${ext}`);\n      try {\n        const stat = await Deno.stat(filePath);\n        if (stat.isFile) {\n          return filePath;\n        }\n      } catch (err) {\n        if (!(err instanceof Deno.errors.NotFound)) {\n          throw err;\n        }\n      }\n    }\n\n    const parent = path.dirname(dir);\n    if (parent === dir) {\n      throw new Error(\n        `Could not find a tailwind config file in the current directory or any parent directory.`,\n      );\n    }\n\n    dir = parent;\n  }\n}\n\nasync function initTailwind(\n  config: ResolvedBuildConfig,\n  options: TailwindPluginOptions,\n): Promise<postcss.Processor> {\n  const root = path.dirname(config.staticDir);\n\n  const configPath = await findTailwindConfigFile(root);\n  const url = path.toFileUrl(configPath).href;\n  const tailwindConfig = (await import(url)).default as Config;\n\n  if (!Array.isArray(tailwindConfig.content)) {\n    throw new Error(`Expected tailwind \"content\" option to be an array`);\n  }\n\n  // deno-lint-ignore no-explicit-any\n  tailwindConfig.content = tailwindConfig.content.map((pattern: any) => {\n    if (typeof pattern === \"string\") {\n      const relative = path.relative(Deno.cwd(), path.dirname(configPath));\n\n      if (!relative.startsWith(\"..\")) {\n        return path.join(relative, pattern);\n      }\n    }\n    return pattern;\n  });\n\n  // PostCSS types cause deep recursion\n  const plugins = [\n    // deno-lint-ignore no-explicit-any\n    tailwindCss(tailwindConfig) as any,\n    // deno-lint-ignore no-explicit-any\n    autoprefixer(options.autoprefixer) as any,\n  ];\n\n  if (config.mode === \"production\") {\n    plugins.push(cssnano());\n  }\n\n  return postcss(plugins);\n}\n"
  },
  {
    "path": "packages/plugin-vite/README.md",
    "content": "# Fresh vite plugin\n\nVite plugin for [Fresh](https://fresh.deno.dev).\n\n## Usage\n\n1. Run `deno install jsr:@fresh/plugin-vite npm:vite`\n2. Ensure that your `deno.json` has the `fresh` import mapping:\n\n```json\n{\n  \"imports\": {\n    \"fresh\": \"jsr:@fresh/core\"\n  }\n}\n```\n\n3. Update your `vite.config.ts` file:\n\n```diff\n  import { defineConfig } from \"vite\";\n+ import { fresh } from \"@fresh/plugin-vite\";\n  \n  export default defineConfig({\n    plugins: [\n+     fresh(),\n    ],\n  });\n```\n\n4. Update your deno tasks respectively:\n\n- Dev: `vite`\n- Build: `vite build`\n\nMore information\n[on the Fresh documentation](https://fresh.deno.dev/docs/advanced/vite) .\n"
  },
  {
    "path": "packages/plugin-vite/demo/assets/style.css",
    "content": "@import \"tailwindcss\";\n"
  },
  {
    "path": "packages/plugin-vite/demo/client.ts",
    "content": "import \"./assets/style.css\";\n"
  },
  {
    "path": "packages/plugin-vite/demo/components/CssModuleNonIsland.tsx",
    "content": "// @ts-ignore upstream issue https://github.com/denoland/deno/issues/30560\nimport styles from \"./CssModulesNonIsland.module.css\";\n\nexport function CssModulesNonIsland() {\n  return (\n    <div class=\"green\">\n      <h1 class={styles.root}>green text</h1>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/components/CssModulesNonIsland.module.css",
    "content": ".root {\n  color: green;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/fixtures/commonjs_mod.cjs",
    "content": "exports.value = \"ok\";\n"
  },
  {
    "path": "packages/plugin-vite/demo/fixtures/maxmind.cjs",
    "content": "\"use strict\";\n// deno-lint-ignore no-var\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n  return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst assert_1 = __importDefault(require(\"assert\"));\n(0, assert_1.default)(true);\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/Bar.tsx",
    "content": "import { useState } from \"preact/hooks\";\n\nexport function Bar() {\n  const [count, set] = useState(0);\n  return (\n    <div>\n      <h1>island asdf</h1>\n      <button type=\"button\" onClick={() => set((v) => v + 1)}>\n        update {count}\n      </button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/CssModules.module.css",
    "content": ".root {\n  color: red;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/CssModules.tsx",
    "content": "import { CssModulesNonIsland } from \"../components/CssModuleNonIsland.tsx\";\n// @ts-ignore upstream issue https://github.com/denoland/deno/issues/30560\nimport styles from \"./CssModules.module.css\";\nimport { CssModulesOther } from \"./CssModulesOther.tsx\";\n\nexport function CssModules() {\n  return (\n    <div class=\"red\">\n      <h1 class={styles.root}>red text</h1>\n      <CssModulesOther />\n      <CssModulesNonIsland />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/CssModulesOther.module.css",
    "content": ".root {\n  color: blue;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/CssModulesOther.tsx",
    "content": "// @ts-ignore upstream issue https://github.com/denoland/deno/issues/30560\nimport styles from \"./CssModulesOther.module.css\";\n\nexport function CssModulesOther() {\n  return (\n    <div class=\"blue\">\n      <h1 class={styles.root}>blue text</h1>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/Foo.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\n\nexport function Foo() {\n  const count = useSignal(0);\n  return (\n    <div>\n      <h1>island</h1>\n      <button type=\"button\" onClick={() => count.value++}>\n        update {count}\n      </button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/IslandNestedInner.tsx",
    "content": "import { useEffect, useState } from \"preact/hooks\";\n\nexport function IslandNestedInner() {\n  const [ready, set] = useState(false);\n\n  useEffect(() => {\n    set(true);\n  }, []);\n\n  return (\n    <div class={ready ? \"inner-ready\" : \"\"}>\n      <p>Inner</p>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/IslandNestedOuter.tsx",
    "content": "import { useEffect, useState } from \"preact/hooks\";\nimport { IslandNestedInner } from \"./IslandNestedInner.tsx\";\n\nexport function IslandNestedOuter() {\n  const [ready, set] = useState(false);\n\n  useEffect(() => {\n    set(true);\n  }, []);\n\n  return (\n    <div class={ready ? \"outer-ready\" : \"\"}>\n      <p>Outer</p>\n      <IslandNestedInner />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/tests/CounterHooks.tsx",
    "content": "import { useState } from \"preact/hooks\";\n\nexport function CounterHooks() {\n  const [count, set] = useState(0);\n\n  return (\n    <div class=\"counter-hooks\">\n      <button type=\"button\" onClick={() => set((v) => v + 1)}>\n        count: {count}\n      </button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/tests/EnvIsland.tsx",
    "content": "import { useEffect, useState } from \"preact/hooks\";\n\nexport function EnvIsland() {\n  const [ready, setReady] = useState(false);\n  useEffect(() => {\n    setReady(true);\n  }, []);\n\n  const deno = Deno.env.get(\"FRESH_PUBLIC_FOO\");\n  // deno-lint-ignore no-process-global\n  const nodeEnv = process.env.FRESH_PUBLIC_FOO;\n\n  return (\n    <div class={ready ? \"ready\" : \"\"}>\n      <pre>{JSON.stringify({ deno,nodeEnv},null,2)}</pre>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/tests/IslandAssets.tsx",
    "content": "import logo from \"../../assets/deno-logo.png\";\n\nexport function IslandAssets() {\n  return <img src={logo} alt=\"\" />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/tests/Mime.tsx",
    "content": "import mime from \"mime-db\";\nimport { useEffect, useState } from \"preact/hooks\";\n\nexport function MimeIsland() {\n  const [ready, setReady] = useState(false);\n  useEffect(() => {\n    // deno-lint-ignore no-console\n    console.log(mime);\n    setReady(true);\n  }, []);\n\n  return <h1 class={ready ? \"ready\" : \"\"}>mime</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/islands/tests/Ready.tsx",
    "content": "import { useEffect, useState } from \"preact/hooks\";\n\nexport function ReadyIsland() {\n  const [ready, set] = useState(false);\n\n  useEffect(() => {\n    set(true);\n  }, []);\n\n  return (\n    <div class={ready ? \"ready\" : \"not-ready\"}>\n      {ready ? \"ready\" : \"waiting...\"}\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/main.ts",
    "content": "import { App, staticFiles, trailingSlashes } from \"fresh\";\n\nexport const app = new App()\n  .use(staticFiles())\n  .use(trailingSlashes(\"never\"))\n  .get(\"/\", () => new Response(\"it works\"))\n  .fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/_error.tsx",
    "content": "import { define } from \"../utils.ts\";\n\nexport default define.page((props) => {\n  if (props.error instanceof Error) {\n    return <pre id=\"err\">{String(props.error?.stack)}</pre>;\n  }\n\n  return <pre id=\"err\">{String(props.error)}</pre>;\n});\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/about.tsx",
    "content": "import { Bar } from \"../islands/Bar.tsx\";\nimport { Foo } from \"../islands/Foo.tsx\";\n\nexport const handler = () => {\n  return { data: {} };\n};\n\nexport default function About() {\n  return (\n    <div>\n      <h1>Hello from Vite</h1>\n      <p>This page is the prod Fresh build and served by vite</p>\n      <Foo />\n      <Bar />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/index.tsx",
    "content": "export default function Index() {\n  return <h1>index page2</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/CssRoute.module.css",
    "content": ".root {\n  color: peachpuff;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/api/[id].tsx",
    "content": "export const handler = () => new Response(\"ok\");\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/assets.tsx",
    "content": "import logo from \"../../assets/deno-logo.png\";\n\nexport default function Page() {\n  return <img src={logo} alt=\"\" />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/build_id.tsx",
    "content": "import { BUILD_ID } from \"@fresh/build-id\";\n\nexport const handler = () => new Response(BUILD_ID);\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/commonjs.tsx",
    "content": "import { value } from \"../../fixtures/commonjs_mod.cjs\";\n\nexport default function Page() {\n  return <h1>{value}</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/css.tsx",
    "content": "// @ts-ignore upstream issue https://github.com/denoland/deno/issues/30560\nimport \"./css_styles.css\";\n\nexport default function Page() {\n  return (\n    <div>\n      <div class=\"route\">\n        <h1>red text</h1>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/css_modules.tsx",
    "content": "import { CssModules } from \"../../islands/CssModules.tsx\";\n// @ts-ignore upstream issue https://github.com/denoland/deno/issues/30560\nimport styles from \"./CssRoute.module.css\";\n\nexport default function Page() {\n  return (\n    <div>\n      <CssModules />\n      <div class=\"route\">\n        <h1 class={styles.root}>peachpuff text</h1>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/css_styles.css",
    "content": "h1 {\n  color: red;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/dep_json.tsx",
    "content": "import { value } from \"@marvinh-test/import-json\";\n\nexport const handler = () => Response.json(value);\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/env.tsx",
    "content": "import { EnvIsland } from \"../../islands/tests/EnvIsland.tsx\";\n\nexport default function Page() {\n  return <EnvIsland />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/env_files.tsx",
    "content": "export const handler = () => {\n  const json = {\n    MY_ENV: Deno.env.get(\"MY_ENV\"),\n    VITE_MY_ENV: Deno.env.get(\"VITE_MY_ENV\"),\n    MY_LOCAL_ENV: Deno.env.get(\"MY_LOCAL_ENV\"),\n    VITE_MY_LOCAL_ENV: Deno.env.get(\"VITE_MY_LOCAL_ENV\"),\n  };\n\n  return Response.json(json);\n};\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/feed.tsx",
    "content": "import * as feed from \"feed\";\n\nexport default function Page() {\n  // deno-lint-ignore no-console\n  console.log(feed);\n  return <h1>feed</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/ioredis.tsx",
    "content": "import * as ioredis from \"ioredis\";\n\nexport default function Page() {\n  // deno-lint-ignore no-console\n  console.log(ioredis);\n  return <h1>ioredis</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/island_assets.tsx",
    "content": "import { IslandAssets } from \"../../islands/tests/IslandAssets.tsx\";\n\nexport default function Page() {\n  return <IslandAssets />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/island_hooks.tsx",
    "content": "import { CounterHooks } from \"../../islands/tests/CounterHooks.tsx\";\n\nexport default function Page() {\n  return <CounterHooks />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/island_nested.tsx",
    "content": "import { IslandNestedOuter } from \"../../islands/IslandNestedOuter.tsx\";\n\nexport default function Page() {\n  return (\n    <div>\n      <IslandNestedOuter />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/it_works.tsx",
    "content": "export default function Page() {\n  return <h1>it works</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/jsx_namespace.tsx",
    "content": "export default function Page() {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      xml:space=\"preserve\"\n    >\n    </svg>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/maxmind.tsx",
    "content": "import * as maxmind from \"../../fixtures/maxmind.cjs\";\n\nexport default function Page() {\n  // deno-lint-ignore no-console\n  console.log(maxmind);\n  return <h1>maxmind</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/middlewares/_middleware.ts",
    "content": "import type { Middleware } from \"@fresh/core\";\n\nconst middleware1: Middleware<{ text: string }> = async (ctx) => {\n  ctx.state.text = \"A\";\n  return await ctx.next();\n};\n\nconst middleware2: Middleware<{ text: string }> = async (ctx) => {\n  ctx.state.text += \"B\";\n  return await ctx.next();\n};\n\nexport default [middleware1, middleware2];\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/middlewares/index.tsx",
    "content": "import type { HandlerFn } from \"@fresh/core\";\n\nexport const handler: HandlerFn<null, { text: string }> = (ctx) =>\n  new Response(ctx.state.text);\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/mime.tsx",
    "content": "import { MimeIsland } from \"../../islands/tests/Mime.tsx\";\n\nexport default function Page() {\n  return <MimeIsland />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/partial.tsx",
    "content": "import { Partial } from \"@fresh/core/runtime\";\nimport { ReadyIsland } from \"../../islands/tests/Ready.tsx\";\n\nexport default function Page() {\n  return (\n    <div f-client-nav>\n      <h1>Partial</h1>\n      <Partial name=\"partial-test\">\n        <a href=\"/tests/partial_insert\">click me</a>\n      </Partial>\n      <ReadyIsland />\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/partial_insert.tsx",
    "content": "import { Partial } from \"@fresh/core/runtime\";\nimport { CounterHooks } from \"../../islands/tests/CounterHooks.tsx\";\n\nexport default function Page() {\n  return (\n    <Partial name=\"partial-test\">\n      <CounterHooks />\n    </Partial>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/pg.tsx",
    "content": "import * as pg from \"pg\";\n\nexport default function Page() {\n  // deno-lint-ignore no-console\n  console.log(pg);\n  return <h1>pg</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/qs.tsx",
    "content": "import * as qs from \"qs\";\n\nexport default function Page() {\n  // deno-lint-ignore no-console\n  console.log(qs);\n  return <h1>qs</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/radix.tsx",
    "content": "import \"@radix-ui/themes/styles.css\";\nimport { Button, Theme } from \"@radix-ui/themes\";\n\nexport default function Page() {\n  return (\n    <Theme>\n      <Button>click me</Button>\n    </Theme>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/redis.tsx",
    "content": "import * as redis from \"redis\";\n\nexport default function Page() {\n  // deno-lint-ignore no-console\n  console.log(redis);\n  return <h1>redis</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/remote_island.tsx",
    "content": "import { RemoteIsland } from \"@marvinh-test/fresh-island\";\n\nexport default function Page() {\n  return <RemoteIsland />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/stripe.tsx",
    "content": "import * as stripe from \"stripe\";\n\nexport default function Page() {\n  // deno-lint-ignore no-console\n  console.log(stripe);\n  return <h1>stripe</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/supabase_pg.tsx",
    "content": "import * as supabase from \"@supabase/postgrest-js\";\n\nexport default function Page() {\n  // deno-lint-ignore no-console\n  console.log(supabase);\n  return <h1>supabase</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/tailwind.tsx",
    "content": "export default function Page() {\n  return <h1 class=\"text-red-500\">this should be red</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/demo/routes/tests/throw.tsx",
    "content": "import { define } from \"../../utils.ts\";\n\nexport const handler = define.handlers({\n  GET() {\n    throw new Error(\"FAIL\");\n  },\n});\n"
  },
  {
    "path": "packages/plugin-vite/demo/static/foo.txt",
    "content": "foo"
  },
  {
    "path": "packages/plugin-vite/demo/static/test_static/foo/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Document</title>\n  </head>\n  <body>\n    <h1>ok</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "packages/plugin-vite/demo/static/test_static/foo.txt",
    "content": "it works"
  },
  {
    "path": "packages/plugin-vite/demo/utils.ts",
    "content": "import { createDefine } from \"@fresh/core\";\n\n// deno-lint-ignore no-explicit-any\nexport const define = createDefine<any>();\n"
  },
  {
    "path": "packages/plugin-vite/demo/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\nimport inspect from \"vite-plugin-inspect\";\nimport tailwind from \"@tailwindcss/vite\";\n\nexport default defineConfig({\n  plugins: [\n    inspect(),\n    fresh({\n      islandSpecifiers: [\"@marvinh-test/fresh-island\"],\n    }),\n    tailwind(),\n  ],\n  future: \"warn\",\n});\n"
  },
  {
    "path": "packages/plugin-vite/deno.json",
    "content": "{\n  \"name\": \"@fresh/plugin-vite\",\n  \"version\": \"1.0.8\",\n  \"license\": \"MIT\",\n  \"exports\": {\n    \".\": \"./src/mod.ts\",\n    \"./client\": \"./src/client.ts\"\n  },\n  \"publish\": {\n    \"include\": [\"./src\", \"README.md\"]\n  },\n  \"compilerOptions\": {\n    \"jsx\": \"precompile\",\n    \"jsxImportSource\": \"preact\",\n    \"types\": [\"vite/client\"]\n  },\n  \"tasks\": {\n    \"demo\": \"vite demo\",\n    \"debug\": \"deno run -A --inspect-brk npm:vite demo\",\n    \"demo:build\": \"vite build demo\",\n    \"demo:start\": \"cd demo && deno serve -A _fresh/server.js\"\n  },\n  \"imports\": {\n    \"@babel/core\": \"npm:@babel/core@^7.28.0\",\n    \"@babel/preset-react\": \"npm:@babel/preset-react@^7.27.1\",\n    \"@deno/loader\": \"jsr:@deno/loader@^0.3.10\",\n    \"@marvinh-test/import-json\": \"jsr:@marvinh-test/import-json@^0.0.1\",\n    \"@remix-run/node-fetch-server\": \"npm:@remix-run/node-fetch-server@^0.12.0\",\n    \"@prefresh/vite\": \"npm:@prefresh/vite@^2.4.8\",\n    \"@radix-ui/themes\": \"npm:@radix-ui/themes@^3.2.1\",\n    \"@tailwindcss/vite\": \"npm:@tailwindcss/vite@^4.1.12\",\n    \"@types/babel__core\": \"npm:@types/babel__core@^7.20.5\",\n    \"@types/node\": \"npm:@types/node@^24.1.0\",\n    \"@types/qs\": \"npm:@types/qs@^6.14.0\",\n    \"feed\": \"npm:feed@^5.1.0\",\n    \"fresh\": \"jsr:@fresh/core@^2.0.0\",\n    \"ioredis\": \"npm:ioredis@^5.7.0\",\n    \"mime-db\": \"npm:mime-db@^1.54.0\",\n    \"pg\": \"npm:pg@^8.16.3\",\n    \"preact\": \"npm:preact@^10.26.9\",\n    \"qs\": \"npm:qs@^6.14.0\",\n    \"rollup-plugin-visualizer\": \"npm:rollup-plugin-visualizer@^6.0.3\",\n    \"stripe\": \"npm:stripe@^19.1.0\",\n    \"vite\": \"npm:vite@^7.1.4\",\n    \"vite-plugin-inspect\": \"npm:vite-plugin-inspect@^11.3.2\"\n  }\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/client.ts",
    "content": "/**\n * Used internally by the `@fresh/plugin-vite` to add HMR support\n * for Fresh running in the browser.\n *\n * **Do not** import this module directly.\n *\n * @module\n * @private\n */\n\nimport type { UpdatePayload } from \"vite\";\nimport { hashCode } from \"./shared.ts\";\n\nif (import.meta.hot) {\n  import.meta.hot.on(\"fresh:reload\", () => {\n    window.location.reload();\n  });\n\n  import.meta.hot.on(\"vite:beforeUpdate\", (module: UpdatePayload) => {\n    module.updates.forEach((update) => {\n      const moduleStyle = document.querySelector(\n        `[vite-module-id=\"${hashCode(update.acceptedPath)}\"]`,\n      );\n      if (moduleStyle) {\n        moduleStyle.remove();\n      }\n    });\n  });\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/mod.ts",
    "content": "import type { Plugin } from \"vite\";\nimport {\n  type FreshViteConfig,\n  pathWithRoot,\n  type ResolvedFreshViteConfig,\n} from \"./utils.ts\";\nimport { deno } from \"./plugins/deno.ts\";\nimport prefresh from \"@prefresh/vite\";\nimport { serverEntryPlugin } from \"./plugins/server_entry.ts\";\nimport { clientEntryPlugin } from \"./plugins/client_entry.ts\";\nimport { devServer } from \"./plugins/dev_server.ts\";\nimport { buildIdPlugin } from \"./plugins/build_id.ts\";\nimport { clientSnapshot } from \"./plugins/client_snapshot.ts\";\nimport { serverSnapshot } from \"./plugins/server_snapshot.ts\";\nimport { patches } from \"./plugins/patches.ts\";\nimport process from \"node:process\";\nimport {\n  specToName,\n  TEST_FILE_PATTERN,\n  UniqueNamer,\n  UPDATE_INTERVAL,\n  updateCheck,\n} from \"@fresh/core/internal-dev\";\nimport { checkImports } from \"./plugins/verify_imports.ts\";\nimport { isBuiltin } from \"node:module\";\nimport { load as stdLoadEnv } from \"@std/dotenv\";\nimport path from \"node:path\";\n\nexport type { FreshViteConfig };\nexport type {\n  ImportCheck,\n  ImportCheckDiagnostic,\n} from \"./plugins/verify_imports.ts\";\n\n/**\n * Fresh framework support for Vite.\n *\n * This plugin uses the Environments feature of Vite to build\n * both the server and client code for Fresh applications.\n *\n * @param config Fresh config options\n * @returns Vite plugin with Fresh support\n *\n * @example Basic usage\n * ```ts vite.config.ts\n * import { defineConfig } from \"vite\";\n * import { fresh } from \"@fresh/plugin-vite\";\n *\n * export default defineConfig({\n *   plugins: [\n *     fresh({ serverEntry: \"server.ts\" })\n *   ],\n * });\n * ```\n */\nexport function fresh(config?: FreshViteConfig): Plugin[] {\n  const fConfig: ResolvedFreshViteConfig = {\n    serverEntry: config?.serverEntry ?? \"main.ts\",\n    clientEntry: config?.clientEntry ?? \"client.ts\",\n    islandsDir: config?.islandsDir ?? \"islands\",\n    routeDir: config?.routeDir ?? \"routes\",\n    ignore: config?.ignore ?? [TEST_FILE_PATTERN],\n    islandSpecifiers: new Map(),\n    namer: new UniqueNamer(),\n    checkImports: config?.checkImports ?? [],\n  };\n\n  fConfig.checkImports.push((id, env) => {\n    if (env === \"client\") {\n      if (isBuiltin(id)) {\n        return {\n          type: \"error\",\n          message: \"Node built-in modules cannot be imported in the browser.\",\n          description:\n            \"This is an error in your application code or in one of its dependencies.\",\n        };\n      }\n    }\n  });\n\n  let isDev = false;\n\n  const plugins: Plugin[] = [\n    {\n      name: \"fresh\",\n      sharedDuringBuild: true,\n      config(config, env) {\n        isDev = env.command === \"serve\";\n\n        return {\n          esbuild: {\n            jsx: \"automatic\",\n            jsxImportSource: \"preact\",\n            jsxDev: env.command === \"serve\",\n          },\n          resolve: {\n            alias: {\n              \"react-dom/test-utils\": \"preact/test-utils\",\n              \"react-dom\": \"preact/compat\",\n              react: \"preact/compat\",\n            },\n            // Disallow externals, because it leads to duplicate\n            // modules with `preact` vs `npm:preact@*` in the server\n            // environment.\n            noExternal: true,\n          },\n          optimizeDeps: {\n            // Optimize deps somehow leads to duplicate modules or them\n            // being placed in the wrong chunks...\n            noDiscovery: true,\n          },\n\n          publicDir: pathWithRoot(\"static\", config.root),\n\n          builder: {\n            async buildApp(builder) {\n              // Build client env first\n              const clientEnv = builder.environments.client;\n              if (clientEnv !== undefined) {\n                await builder.build(clientEnv);\n              }\n\n              await Promise.all(\n                Object.values(builder.environments).filter((env) =>\n                  env !== clientEnv\n                ).map((env) => builder.build(env)),\n              );\n            },\n          },\n          environments: {\n            client: {\n              build: {\n                copyPublicDir: false,\n                manifest: true,\n\n                outDir: config.environments?.client?.build?.outDir ??\n                  (config.build?.outDir\n                    ? config.build.outDir + \"/client\"\n                    : null) ??\n                  \"_fresh/client\",\n                rollupOptions: {\n                  preserveEntrySignatures: \"strict\",\n                  input: {\n                    \"client-entry\": \"fresh:client-entry\",\n                  },\n                },\n              },\n            },\n            ssr: {\n              build: {\n                manifest: true,\n                emitAssets: true,\n                copyPublicDir: false,\n\n                outDir: config.environments?.ssr?.build?.outDir ??\n                  (config.build?.outDir\n                    ? config.build.outDir + \"/server\"\n                    : null) ??\n                  \"_fresh/server\",\n                rollupOptions: {\n                  onwarn(warning, handler) {\n                    // Ignore \"use client\"; warnings\n                    if (warning.code === \"MODULE_LEVEL_DIRECTIVE\") {\n                      return;\n                    }\n\n                    // Ignore optional export errors\n                    if (\n                      warning.code === \"MISSING_EXPORT\" &&\n                      warning.id?.startsWith(\"\\0fresh-route::\")\n                    ) {\n                      return;\n                    }\n\n                    // Ignore commonjs optional exports\n                    if (\n                      warning.code === \"MISSING_EXPORT\" &&\n                      warning.message.includes(\"__require\")\n                    ) {\n                      return;\n                    }\n\n                    // Ignore this warnings\n                    if (warning.code === \"THIS_IS_UNDEFINED\") {\n                      return;\n                    }\n\n                    // Ignore falsy source map errors\n                    if (warning.code === \"SOURCEMAP_ERROR\") {\n                      return;\n                    }\n\n                    return handler(warning);\n                  },\n                  input: {\n                    \"server-entry\": \"fresh:server_entry\",\n                  },\n                },\n              },\n            },\n          },\n        };\n      },\n      async configResolved(vConfig) {\n        // Run update check in background\n        updateCheck(UPDATE_INTERVAL).catch(() => {});\n\n        fConfig.islandsDir = pathWithRoot(fConfig.islandsDir, vConfig.root);\n        fConfig.routeDir = pathWithRoot(fConfig.routeDir, vConfig.root);\n\n        config?.islandSpecifiers?.map((spec) => {\n          const specName = specToName(spec);\n          const name = fConfig.namer.getUniqueName(specName);\n          fConfig.islandSpecifiers.set(spec, name);\n        });\n\n        const envDir = pathWithRoot(\n          vConfig.envDir || vConfig.root,\n          vConfig.root,\n        );\n\n        await loadEnvFile(path.join(envDir, \".env\"));\n        await loadEnvFile(path.join(envDir, \".env.local\"));\n        const mode = isDev ? \"development\" : \"production\";\n        await loadEnvFile(path.join(envDir, `.env.${mode}`));\n        await loadEnvFile(path.join(envDir, `.env.${mode}.local`));\n      },\n    },\n    serverEntryPlugin(fConfig),\n    patches(),\n    ...serverSnapshot(fConfig),\n    clientEntryPlugin(fConfig),\n    ...clientSnapshot(fConfig),\n    buildIdPlugin(),\n    ...devServer(),\n    prefresh({\n      include: [/\\.[cm]?[tj]sx?$/],\n      exclude: [/node_modules/, /[\\\\/]+deno[\\\\/]+npm[\\\\/]+/],\n      parserPlugins: [\n        \"importMeta\",\n        \"explicitResourceManagement\",\n        \"topLevelAwait\",\n      ],\n    }),\n    checkImports({ checks: fConfig.checkImports }),\n  ];\n\n  if (typeof process.versions.deno === \"string\") {\n    plugins.push(deno());\n  }\n\n  return plugins;\n}\n\nasync function loadEnvFile(envPath: string) {\n  try {\n    await stdLoadEnv({ envPath, export: true });\n  } catch {\n    // Ignore\n  }\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/build_id.ts",
    "content": "import type { Plugin } from \"vite\";\n\nexport function buildIdPlugin(): Plugin {\n  let buildId = \"\";\n\n  const regex = /^(jsr:)?@fresh\\/build-id(@.*)?$/;\n\n  return {\n    name: \"fresh:build-id\",\n    sharedDuringBuild: true,\n    async config(_, env) {\n      const isDev = env.command === \"serve\";\n\n      buildId = await getBuildId(isDev);\n\n      return {\n        define: {\n          BUILD_ID: JSON.stringify(buildId),\n        },\n      };\n    },\n    applyToEnvironment() {\n      return true;\n    },\n    resolveId(id) {\n      if (regex.test(id)) {\n        return `\\0fresh/build-id`;\n      }\n    },\n    load: {\n      filter: {\n        id: /\\0fresh\\/build-id/,\n      },\n      handler() {\n        return `export let BUILD_ID = ${JSON.stringify(buildId)};\nexport const DENO_DEPLOYMENT_ID = undefined;\nexport function setBuildId(id) {\n  BUILD_ID = id;\n}`;\n      },\n    },\n  };\n}\n\nexport async function getBuildId(dev: boolean): Promise<string> {\n  const gitRevision = Deno.env.get(\"DENO_DEPLOYMENT_ID\") ??\n    Deno.env.get(\"DENO_DEPLOY_BUILD_ID\") ??\n    Deno.env.get(\"GITHUB_SHA\") ??\n    Deno.env.get(\"CI_COMMIT_SHA\");\n  if (gitRevision !== undefined) {\n    return gitRevision.trim();\n  }\n\n  if (!dev) {\n    try {\n      const bin = Deno.build.os === \"windows\" ? \"git.exe\" : \"git\";\n      const res = await new Deno.Command(bin, { args: [\"rev-parse\", \"HEAD\"] })\n        .output();\n\n      return new TextDecoder().decode(res.stdout).trim();\n    } catch {\n      // ignore\n    }\n  }\n\n  const arr = new Uint32Array(1);\n  crypto.getRandomValues(arr);\n  return String(arr[0]).trim();\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/client_entry.ts",
    "content": "import type { Plugin } from \"vite\";\nimport { pathWithRoot, type ResolvedFreshViteConfig } from \"../utils.ts\";\n\nexport function clientEntryPlugin(options: ResolvedFreshViteConfig): Plugin {\n  const modName = \"fresh:client-entry\";\n  const modNameUser = \"fresh:client-entry-user\";\n\n  let clientEntry = \"\";\n  let isDev = false;\n\n  return {\n    name: \"fresh:client-entry\",\n    sharedDuringBuild: true,\n    config(_, env) {\n      isDev = env.command === \"serve\";\n    },\n    applyToEnvironment(env) {\n      return env.config.consumer === \"client\";\n    },\n    configResolved(config) {\n      clientEntry = pathWithRoot(options.clientEntry, config.root);\n    },\n    resolveId: {\n      filter: {\n        id: /(fresh:client-entry|fresh:client-entry-user|fresh:client-quirks)/,\n      },\n      handler(id) {\n        if (id === modName) {\n          return `\\0${modName}`;\n        } else if (id === \"fresh:client-quirks\") {\n          return \"@fresh/plugin-vite/client\";\n        } else if (id === modNameUser) {\n          return clientEntry;\n        }\n      },\n    },\n    load: {\n      filter: {\n        id: /\\0fresh:client-entry/,\n      },\n      async handler() {\n        let exists = false;\n        try {\n          const stat = await Deno.stat(clientEntry);\n          exists = stat.isFile;\n        } catch {\n          exists = false;\n        }\n\n        return `\n${isDev ? 'import \"preact/debug\"' : \"\"}\nexport * from \"fresh/runtime-client\";\n${exists ? `import \"fresh:client-entry-user\";` : \"\"}\nimport \"@fresh/plugin-vite/client\";`;\n      },\n    },\n  };\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/client_snapshot.ts",
    "content": "import type { Plugin, ViteDevServer } from \"vite\";\nimport { pathWithRoot, type ResolvedFreshViteConfig } from \"../utils.ts\";\nimport { crawlFsItem, specToName } from \"fresh/internal-dev\";\nimport * as path from \"@std/path\";\n\nexport function clientSnapshot(options: ResolvedFreshViteConfig): Plugin[] {\n  const modName = \"fresh:client-snapshot\";\n\n  const islands = new Set<string>();\n  let server: ViteDevServer | undefined;\n  let isDev = false;\n\n  const entryToIsland = new Map<string, string>();\n\n  return [\n    {\n      name: \"fresh:client-snapshot\",\n      sharedDuringBuild: true,\n      applyToEnvironment(env) {\n        return env.config.consumer === \"client\";\n      },\n\n      async config(cfg, env) {\n        isDev = env.command === \"serve\";\n\n        const cwd = Deno.cwd();\n\n        const result = await crawlFsItem({\n          islandDir: pathWithRoot(options.islandsDir, cfg.root ?? cwd),\n          routeDir: pathWithRoot(options.routeDir, cfg.root ?? cwd),\n          ignore: options.ignore,\n        });\n\n        const input: Record<string, string> = {};\n\n        if (isDev) {\n          input[\"client-snapshot\"] = \"fresh:client-snapshot\";\n        }\n\n        for (let i = 0; i < result.islands.length; i++) {\n          const filePath = result.islands[i];\n          islands.add(filePath);\n\n          if (!isDev) {\n            const specName = specToName(filePath);\n            const name = options.namer.getUniqueName(specName);\n\n            entryToIsland.set(name, filePath);\n            input[`fresh-island::${name}`] = `fresh-client-island::${name}`;\n          }\n        }\n\n        return {\n          environments: {\n            client: {\n              build: {\n                rollupOptions: {\n                  input,\n                },\n              },\n            },\n          },\n        };\n      },\n      configResolved(cfg) {\n        for (const [name, spec] of entryToIsland.entries()) {\n          const full = pathWithRoot(spec, cfg.root);\n          entryToIsland.set(name, full);\n        }\n      },\n      options(opts) {\n        options.islandSpecifiers.forEach((_name, spec) => {\n          islands.add(spec);\n\n          if (!isDev) {\n            const specName = specToName(spec);\n            const name = options.namer.getUniqueName(specName);\n            entryToIsland.set(name, spec);\n\n            // deno-lint-ignore no-explicit-any\n            (opts.input as any)[`fresh-island::${name}`] =\n              `fresh-client-island::${name}`;\n          }\n        });\n      },\n      configureServer(devServer) {\n        server = devServer;\n\n        server.watcher.on(\"add\", (filePath) => {\n          if (!isIslandPath(options, filePath)) return;\n\n          islands.add(filePath);\n\n          invalidateSnapshots(server!);\n        });\n        server.watcher.on(\"unlink\", (filePath) => {\n          if (!isIslandPath(options, filePath)) return;\n\n          islands.delete(filePath);\n\n          invalidateSnapshots(server!);\n        });\n      },\n      resolveId: {\n        filter: {\n          id: /fresh:client-snapshot/,\n        },\n        handler(id) {\n          if (id === modName) {\n            return `\\0${modName}`;\n          }\n        },\n      },\n      load: {\n        filter: {\n          id: /\\0fresh:client-snapshot/,\n        },\n        handler() {\n          const imports = Array.from(islands.keys()).map((file, i) => {\n            return `export const mod_${i} = await import(${\n              JSON.stringify(file)\n            });`;\n          }).join(\"\\n\");\n\n          if (isDev && server !== undefined) {\n            const mod = server.environments.ssr.moduleGraph.getModuleById(\n              \"\\0fresh:server-snapshot\",\n            );\n            if (mod) {\n              server.environments.ssr.moduleGraph.invalidateModule(mod);\n            }\n          }\n\n          return `${imports}\nif (import.meta.hot) {\n  import.meta.hot.accept(() => {\n    console.log(\"accepting client-snapshot\")\n  });\n}\n`;\n        },\n      },\n    },\n    {\n      name: \"fresh:client-island\",\n      sharedDuringBuild: true,\n      resolveId: {\n        filter: {\n          id: /^fresh-client-island::/,\n        },\n        handler(id) {\n          const name = id.slice(\"fresh-client-island::\".length);\n          const full = entryToIsland.get(name);\n          return full;\n        },\n      },\n    },\n  ];\n}\n\nfunction isIslandPath(\n  options: ResolvedFreshViteConfig,\n  filePath: string,\n): boolean {\n  const relIsland = path.relative(options.islandsDir, filePath);\n  if (!relIsland.startsWith(\"..\")) return true;\n\n  const relRoutes = path.relative(options.routeDir, filePath);\n\n  if (!relIsland.startsWith(\"..\") && relRoutes.includes(\"(_islands)\")) {\n    return true;\n  }\n  return false;\n}\n\nfunction invalidateSnapshots(server: ViteDevServer) {\n  const client = server.environments.client.moduleGraph.getModuleById(\n    \"\\0fresh:client-snapshot\",\n  );\n  if (client !== undefined) {\n    server.environments.client.moduleGraph.invalidateModule(client);\n  }\n\n  const ssr = server.environments.ssr.moduleGraph.getModuleById(\n    \"\\0fresh:server-snapshot\",\n  );\n  if (ssr !== undefined) {\n    server.environments.ssr.moduleGraph.invalidateModule(ssr);\n  }\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/deno.ts",
    "content": "import type { Plugin } from \"vite\";\nimport {\n  type Loader,\n  MediaType,\n  RequestedModuleType,\n  ResolutionMode,\n  Workspace,\n} from \"@deno/loader\";\nimport * as path from \"@std/path\";\nimport * as babel from \"@babel/core\";\nimport { httpAbsolute } from \"./patches/http_absolute.ts\";\nimport { JS_REG, JSX_REG } from \"../utils.ts\";\nimport { builtinModules } from \"node:module\";\n\n// @ts-ignore Workaround for https://github.com/denoland/deno/issues/30850\nconst { default: babelReact } = await import(\"@babel/preset-react\");\n\nconst BUILTINS = new Set(builtinModules);\n\ninterface DenoState {\n  type: RequestedModuleType;\n}\n\nexport function deno(): Plugin {\n  let ssrLoader: Loader;\n  let browserLoader: Loader;\n\n  let isDev = false;\n\n  return {\n    name: \"deno\",\n    sharedDuringBuild: true,\n    // We must be first to be able to resolve before the\n    // Vite's own`vite:resolve` plugin. It always treats bare\n    // specifiers as external during SSR.\n    enforce: \"pre\",\n    config(_, env) {\n      isDev = env.command === \"serve\";\n    },\n    async configResolved() {\n      // TODO: Pass conditions\n      ssrLoader = await new Workspace({\n        platform: \"node\",\n        cachedOnly: true,\n      }).createLoader();\n      browserLoader = await new Workspace({\n        platform: \"browser\",\n        preserveJsx: true,\n        cachedOnly: true,\n      })\n        .createLoader();\n    },\n    applyToEnvironment() {\n      return true;\n    },\n    async resolveId(id, importer, options) {\n      if (BUILTINS.has(id)) {\n        // `node:` prefix is not included in builtins list.\n        if (!id.startsWith(\"node:\")) {\n          id = `node:${id}`;\n        }\n        return {\n          id,\n          external: true,\n        };\n      }\n      const loader = this.environment.config.consumer === \"server\"\n        ? ssrLoader\n        : browserLoader;\n\n      const original = id;\n\n      let isHttp = false;\n      if (id.startsWith(\"deno-http::\")) {\n        isHttp = true;\n        id = id.slice(\"deno-http::\".length);\n      }\n\n      importer = isDenoSpecifier(importer)\n        ? parseDenoSpecifier(importer).specifier\n        : importer;\n\n      if (id.startsWith(\"/\") && importer && /^https?:\\/\\//g.test(importer)) {\n        const url = new URL(importer);\n        id = `${url.origin}${id}`;\n      }\n\n      // We still want to allow other plugins to participate in\n      // resolution, with us being in front due to `enforce: \"pre\"`.\n      // But we still want to ignore everything `vite:resolve` does\n      // because we're kinda replacing that plugin here.\n      const tmp = await this.resolve(id, importer, options);\n      if (tmp && tmp.resolvedBy !== \"vite:resolve\") {\n        if (tmp.external && !/^https?:\\/\\//.test(tmp.id)) {\n          return tmp;\n        }\n\n        // A plugin namespaced it, we should not attempt to resolve it.\n        if (tmp.id.startsWith(\"\\0\")) {\n          return tmp;\n        }\n\n        id = tmp.id;\n      }\n\n      // Plugins may return lower cased drive letters on windows\n      if (!isHttp && path.isAbsolute(id)) {\n        id = path.toFileUrl(path.normalize(id))\n          .href;\n      }\n\n      try {\n        // Ensure we're passing a valid importer that Deno understands\n        const denoImporter = importer && !importer.startsWith(\"\\0\")\n          ? importer\n          : undefined;\n\n        let resolved = await loader.resolve(\n          id,\n          denoImporter,\n          ResolutionMode.Import,\n        );\n\n        if (resolved.startsWith(\"node:\")) {\n          return {\n            id: resolved,\n            external: true,\n          };\n        }\n\n        if (original === resolved) {\n          return null;\n        }\n\n        const type = getDenoType(id, options.attributes.type ?? \"default\");\n        if (\n          type !== RequestedModuleType.Default ||\n          /^(https?|jsr|npm):/.test(resolved)\n        ) {\n          return toDenoSpecifier(resolved, type);\n        }\n\n        if (resolved.startsWith(\"file://\")) {\n          resolved = path.fromFileUrl(resolved);\n        }\n\n        return {\n          id: resolved,\n          meta: {\n            deno: {\n              type,\n            },\n          },\n        };\n      } catch {\n        // ignore\n      }\n    },\n    async load(id) {\n      const loader = this.environment.config.consumer === \"server\"\n        ? ssrLoader\n        : browserLoader;\n\n      if (isDenoSpecifier(id)) {\n        const { type, specifier } = parseDenoSpecifier(id);\n\n        const result = await loader.load(specifier, type);\n        if (result.kind === \"external\") {\n          return null;\n        }\n\n        const code = new TextDecoder().decode(result.code);\n\n        const maybeJsx = babelTransform({\n          ssr: this.environment.config.consumer === \"server\",\n          media: result.mediaType,\n          code,\n          id: specifier,\n          isDev,\n        });\n        if (maybeJsx !== null) {\n          return maybeJsx;\n        }\n\n        return {\n          code,\n        };\n      }\n\n      if (id.startsWith(\"\\0\")) {\n        id = id.slice(1);\n      }\n\n      const meta = this.getModuleInfo(id)?.meta.deno as\n        | DenoState\n        | undefined\n        | null;\n\n      if (meta === null || meta === undefined) return;\n\n      // Skip for non-js files like `.css`\n      if (\n        meta.type === RequestedModuleType.Default &&\n        !JS_REG.test(id)\n      ) {\n        return;\n      }\n\n      const url = path.toFileUrl(id);\n\n      const result = await loader.load(url.href, meta.type);\n      if (result.kind === \"external\") {\n        return null;\n      }\n\n      const code = new TextDecoder().decode(result.code);\n\n      const maybeJsx = babelTransform({\n        ssr: this.environment.config.consumer === \"server\",\n        media: result.mediaType,\n        id,\n        code,\n        isDev,\n      });\n      if (maybeJsx) {\n        return maybeJsx;\n      }\n\n      return {\n        code,\n      };\n    },\n    transform: {\n      filter: {\n        id: JSX_REG,\n      },\n      async handler(_, id) {\n        // This transform is a hack to be able to re-use Deno's precompile\n        // jsx transform.\n        if (this.environment.name === \"client\") {\n          return;\n        }\n\n        let actualId = id;\n        if (isDenoSpecifier(id)) {\n          const { specifier } = parseDenoSpecifier(id);\n          actualId = specifier;\n        }\n        actualId = actualId.replace(\"?commonjs-es-import\", \"\");\n\n        if (actualId.startsWith(\"\\0\")) {\n          actualId = actualId.slice(1);\n        }\n        if (path.isAbsolute(actualId)) {\n          actualId = path.toFileUrl(actualId).href;\n        }\n\n        const resolved = await ssrLoader.resolve(\n          actualId,\n          undefined,\n          ResolutionMode.Import,\n        );\n        const result = await ssrLoader.load(\n          resolved,\n          RequestedModuleType.Default,\n        );\n        if (result.kind === \"external\") {\n          return;\n        }\n\n        const code = new TextDecoder().decode(result.code);\n\n        return {\n          code,\n        };\n      },\n    },\n  };\n}\n\nfunction isJsMediaType(media: MediaType): boolean {\n  switch (media) {\n    case MediaType.JavaScript:\n    case MediaType.Jsx:\n    case MediaType.Mjs:\n    case MediaType.Cjs:\n    case MediaType.TypeScript:\n    case MediaType.Mts:\n    case MediaType.Cts:\n    case MediaType.Tsx:\n      return true;\n\n    case MediaType.Dts:\n    case MediaType.Dmts:\n    case MediaType.Dcts:\n    case MediaType.Css:\n    case MediaType.Json:\n    case MediaType.Html:\n    case MediaType.Sql:\n    case MediaType.Wasm:\n    case MediaType.SourceMap:\n    case MediaType.Unknown:\n      return false;\n  }\n}\n\nexport type DenoSpecifier = string & { __deno: string };\n\nfunction isDenoSpecifier(str: unknown): str is DenoSpecifier {\n  return typeof str === \"string\" && str.startsWith(\"\\0deno::\");\n}\n\nfunction toDenoSpecifier(spec: string, type: RequestedModuleType) {\n  return `\\0deno::${type}::${spec}`;\n}\n\nfunction parseDenoSpecifier(\n  spec: DenoSpecifier,\n): { type: RequestedModuleType; specifier: string } {\n  const match = spec.match(/^\\0deno::([^:]+)::(.*)$/)!;\n\n  let specifier = match[2];\n\n  const specMatch = specifier.match(/^(\\w+):\\/([^/].*)$/);\n  if (specMatch !== null) {\n    const protocol = specMatch[1];\n\n    let rest = specMatch[2];\n    if (protocol === \"file\") {\n      rest = \"/\" + rest;\n    }\n\n    specifier = `${protocol}://${rest}`;\n  }\n\n  if (path.isAbsolute(specifier)) {\n    specifier = path.toFileUrl(specifier).href;\n  }\n\n  return { type: +match[1], specifier };\n}\n\nfunction getDenoType(id: string, type: string): RequestedModuleType {\n  switch (type) {\n    case \"json\":\n      return RequestedModuleType.Json;\n    case \"bytes\":\n      return RequestedModuleType.Bytes;\n    case \"text\":\n      return RequestedModuleType.Text;\n    default:\n      if (id.endsWith(\".json\")) {\n        return RequestedModuleType.Json;\n      }\n      return RequestedModuleType.Default;\n  }\n}\n\nfunction babelTransform(\n  options: {\n    media: MediaType;\n    ssr: boolean;\n    code: string;\n    id: string;\n    isDev: boolean;\n  },\n) {\n  if (!isJsMediaType(options.media)) {\n    return null;\n  }\n\n  const { ssr, code, id, isDev } = options;\n\n  const presets: babel.PluginItem[] = [];\n  if (\n    !ssr && id.endsWith(\".tsx\") || id.endsWith(\".jsx\")\n  ) {\n    presets.push([babelReact, {\n      runtime: \"automatic\",\n      importSource: \"preact\",\n      development: isDev,\n      throwIfNamespace: false,\n    }]);\n  }\n\n  const url = URL.canParse(id) ? new URL(id) : null;\n\n  const result = babel.transformSync(code, {\n    filename: id,\n    babelrc: false,\n    sourceMaps: \"both\",\n    presets: presets,\n    plugins: [httpAbsolute(url)],\n    compact: false,\n  });\n\n  if (result !== null && result.code) {\n    return {\n      code: result.code,\n      map: result.map,\n    };\n  }\n\n  return null;\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/dev_server.ts",
    "content": "import type { DevEnvironment, Plugin } from \"vite\";\nimport * as path from \"@std/path\";\nimport { ASSET_CACHE_BUST_KEY } from \"fresh/internal\";\nimport { createRequest, sendResponse } from \"@remix-run/node-fetch-server\";\nimport { hashCode } from \"../shared.ts\";\n\nexport function devServer(): Plugin[] {\n  let publicDir = \"\";\n  return [\n    {\n      name: \"fresh:dev_server\",\n      sharedDuringBuild: true,\n      configResolved(config) {\n        publicDir = config.publicDir;\n      },\n      configureServer(server) {\n        const IGNORE_URLS = /^\\/(@(vite|fs|id)|\\.vite)\\//;\n\n        server.middlewares.use(async (nodeReq, nodeRes, next) => {\n          const serverCfg = server.config.server;\n\n          const protocol = serverCfg.https ? \"https\" : \"http\";\n          const host = serverCfg.host ? serverCfg.host : \"localhost\";\n          const port = serverCfg.port;\n          const url = new URL(\n            `${protocol}://${host}:${port}${nodeReq.url ?? \"/\"}`,\n          );\n\n          // Don't cache in dev\n          url.searchParams.delete(ASSET_CACHE_BUST_KEY);\n\n          // Check if it's a vite url\n          if (\n            IGNORE_URLS.test(url.pathname) ||\n            server.environments.client.moduleGraph.urlToModuleMap.has(\n              url.pathname,\n            ) ||\n            server.environments.ssr.moduleGraph.urlToModuleMap.has(\n              url.pathname,\n            ) ||\n            url.pathname === \"/.well-known/appspecific/com.chrome.devtools.json\"\n          ) {\n            return next();\n          }\n\n          // Check if it's a static file first\n          const staticFilePath = path.join(publicDir, url.pathname.slice(1));\n          try {\n            const stat = await Deno.stat(staticFilePath);\n            if (stat.isFile) {\n              return next();\n            }\n          } catch {\n            // Ignore\n          }\n\n          // Check if it's a static/index.html file\n          const staticFilePathIndex = path.join(\n            publicDir,\n            url.pathname.slice(1),\n            \"index.html\",\n          );\n          try {\n            const content = await Deno.readTextFile(staticFilePathIndex);\n            nodeRes.setHeader(\"Content-Type\", \"text/html; charset=utf-8\");\n            nodeRes.end(content);\n            return;\n          } catch {\n            // Ignore\n          }\n\n          try {\n            const mod = await server.ssrLoadModule(\"fresh:server_entry\");\n            const req = createRequest(nodeReq, nodeRes);\n            mod.setErrorInterceptor((err: unknown) => {\n              if (err instanceof Error) {\n                server.ssrFixStacktrace(err);\n              }\n            });\n\n            const res = (await mod.default.fetch(req)) as Response;\n\n            // Collect css eagerly to avoid FOUC. This is a workaround for\n            // Vite not supporting css natively. It's a bit hacky, but\n            // gets the job done.\n            if (\n              url.pathname !== \"/__inspect\" &&\n              res.headers.get(\"Content-Type\")?.includes(\"text/html\")\n            ) {\n              const collected = await collectCss(\n                \"fresh:client-entry\",\n                server.environments.client,\n              );\n\n              let html = await res.text();\n\n              const styles = collected.join(\"\\n\");\n              html = html.replace(\"</head>\", styles + \"</head>\");\n\n              const newRes = new Response(html, {\n                status: res.status,\n                headers: res.headers,\n              });\n              await sendResponse(nodeRes, newRes);\n              return;\n            }\n\n            await sendResponse(nodeRes, res);\n          } catch (err) {\n            if (err instanceof Error) {\n              server.ssrFixStacktrace(err);\n            }\n            return next(err);\n          }\n        });\n      },\n    },\n    {\n      name: \"fresh:server_hmr\",\n      applyToEnvironment(env) {\n        return env.config.consumer === \"server\";\n      },\n      hotUpdate(options) {\n        const clientMod = options.server.environments.client.moduleGraph\n          .getModulesByFile(options.file);\n\n        if (clientMod !== undefined) {\n          // Vite can do HMR here\n          return;\n        }\n\n        const ssrMod = options.server.environments.ssr.moduleGraph\n          .getModulesByFile(options.file);\n        if (ssrMod !== undefined) {\n          // SSR-only module. Might still be a route.\n          // TODO: Implement proper ssr hmr\n          options.server.hot.send(\"fresh:reload\");\n        }\n      },\n    },\n  ];\n}\n\nasync function collectCss(\n  id: string,\n  env: DevEnvironment,\n) {\n  const seen = new Set<string>();\n  const queue: string[] = [id];\n  const out: string[] = [];\n\n  let current: string | undefined;\n  while ((current = queue.pop()) !== undefined) {\n    if (seen.has(current)) continue;\n    seen.add(current);\n\n    let mod = env.moduleGraph.idToModuleMap.get(current);\n    if (mod === undefined || mod.transformResult === null) {\n      // During development assets are loaded lazily, so we need\n      // to trigger processing manually.\n      await env.fetchModule(current);\n      mod = env.moduleGraph.idToModuleMap.get(current) ??\n        env.moduleGraph.idToModuleMap.get(`\\0${current}`);\n\n      if (mod === undefined) continue;\n    }\n\n    if (\n      (current.endsWith(\".scss\") ||\n        current.endsWith(\".css\")) && mod.transformResult\n    ) {\n      // Since vite stores everything as a JS file we need to\n      // extract the CSS out of the JS\n      const match = mod.transformResult.code.match(\n        /__vite__css\\s+=\\s+(\"(?:\\\\\\\\|\\\\\"|[^\"])*\")/,\n      );\n\n      if (match !== null) {\n        const content = JSON.parse(match[1]);\n        out.push(\n          `<style type=\"text/css\" vite-module-id=\"${\n            hashCode(id)\n          }\">${content}</style>`,\n        );\n      }\n    }\n\n    mod.importedModules.forEach((m) => {\n      if (m.id === null) return;\n      queue.push(m.id);\n    });\n  }\n\n  return out;\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/code_eval.ts",
    "content": "import type { PluginObj, PluginPass, types } from \"@babel/core\";\n\nconst APPLY_PG_QUIRKS = \"applyPgQuirks\";\n\nexport function codeEvalPlugin(\n  env: \"server\" | \"client\",\n  mode: string,\n) {\n  return (\n    { types: t }: { types: typeof types },\n  ): PluginObj => {\n    return {\n      name: \"fresh-remove-polyfills\",\n      visitor: {\n        IfStatement: {\n          enter(path, state) {\n            const res = evaluateExpr(t, env, mode, path.node.test, state);\n            // Could not evaluate\n            if (res === null) return;\n            if (res) {\n              if (t.isBlockStatement(path.node.consequent)) {\n                path.replaceWithMultiple(path.node.consequent.body);\n              } else {\n                path.replaceWith(path.node.consequent);\n              }\n            } else if (path.node.alternate) {\n              if (t.isBlockStatement(path.node.alternate)) {\n                path.replaceWithMultiple(path.node.alternate.body);\n              } else {\n                path.replaceWith(path.node.alternate);\n              }\n            } else {\n              path.remove();\n            }\n          },\n        },\n      },\n    };\n  };\n}\n\nfunction evaluateExpr(\n  t: typeof types,\n  env: \"server\" | \"client\",\n  mode: string,\n  node: types.Node,\n  state: PluginPass,\n): boolean | null {\n  if (t.isLogicalExpression(node)) {\n    const left = evaluateExpr(t, env, mode, node.left, state);\n    if (left === null) return null;\n    if (left && node.operator === \"||\") return true;\n\n    const right = evaluateExpr(t, env, mode, node.right, state);\n    if (right === null) return null;\n\n    switch (node.operator) {\n      case \"||\":\n        return left || right;\n      case \"&&\":\n        return left && right;\n      case \"??\":\n        return left ?? right;\n      default:\n        return false;\n    }\n  } else if (t.isBinaryExpression(node)) {\n    // Check: typeof process == \"undefined\"\n    // Check: typeof process === \"undefined\"\n    // Check: typeof process != \"undefined\"\n    // Check: typeof process !== \"undefined\"\n    if (\n      t.isUnaryExpression(node.left) && node.left.operator === \"typeof\" &&\n      t.isIdentifier(node.left.argument) &&\n      node.left.argument.name === \"process\" && t.isStringLiteral(node.right) &&\n      node.right.value === \"undefined\"\n    ) {\n      if (node.operator === \"==\" || node.operator === \"===\") {\n        return env !== \"server\";\n      } else if (node.operator === \"!=\" || node.operator === \"!==\") {\n        return env === \"server\";\n      }\n    } else if (\n      // Workaround for npm:pg\n      // Check: typeof process.env.NODE_PG_FORCE_NATIVE !== \"undefined\"\n      t.isUnaryExpression(node.left) &&\n      t.isMemberExpression(node.left.argument) &&\n      t.isMemberExpression(node.left.argument.object) &&\n      t.isIdentifier(node.left.argument.object.object) &&\n      node.left.argument.object.object.name === \"process\" &&\n      t.isIdentifier(node.left.argument.object.property) &&\n      node.left.argument.object.property.name === \"env\" &&\n      t.isIdentifier(node.left.argument.property) &&\n      node.left.argument.property.name === \"NODE_PG_FORCE_NATIVE\" &&\n      t.isStringLiteral(node.right)\n    ) {\n      state.set(APPLY_PG_QUIRKS, true);\n      const left = typeof Deno.env.get(\"NODE_PG_FORCE_NATIVE\");\n      const right = node.right.value;\n\n      const result = applyBinExpr(left, right, node);\n      if (result !== null) return result;\n    } else if (\n      // Check: process.env.NODE_ENV == \"production\"\n      // Check: process.env.NODE_ENV === \"production\"\n      // Check: process.env.NODE_ENV != \"production\"\n      // Check: process.env.NODE_ENV !== \"production\"\n      // Check: process.env.NODE_ENV == \"development\"\n      // Check: process.env.NODE_ENV === \"development\"\n      // Check: process.env.NODE_ENV != \"development\"\n      // Check: process.env.NODE_ENV !== \"development\"\n      t.isMemberExpression(node.left) &&\n      t.isMemberExpression(node.left.object) &&\n      t.isIdentifier(node.left.object.object) &&\n      node.left.object.object.name === \"process\" &&\n      t.isIdentifier(node.left.object.property) &&\n      node.left.object.property.name === \"env\" &&\n      t.isIdentifier(node.left.property) &&\n      node.left.property.name === \"NODE_ENV\" &&\n      t.isStringLiteral(node.right)\n    ) {\n      const value = node.right.value;\n\n      const result = applyBinExpr(mode, value, node);\n      if (result !== null) return result;\n    } else if (\n      // Check: process.foo === \"bar\"\n      env === \"server\" && t.isMemberExpression(node.left) &&\n      t.isIdentifier(node.left.object) && node.left.object.name === \"process\" &&\n      t.isIdentifier(node.left.property) &&\n      !PROCESS_PROPERTIES.has(node.left.property.name)\n    ) {\n      return false;\n    }\n\n    return null;\n  } else if (\n    t.isMemberExpression(node) && t.isIdentifier(node.object) &&\n    node.object.name === \"process\" && t.isIdentifier(node.property)\n  ) {\n    return PROCESS_PROPERTIES.has(node.property.name);\n  } else if (t.isIdentifier(node) && node.name === \"useLegacyCrypto\") {\n    return false;\n  }\n\n  return null;\n}\n\nfunction applyBinExpr(\n  left: unknown,\n  right: unknown,\n  node: types.BinaryExpression,\n): boolean | null {\n  switch (node.operator) {\n    case \"==\":\n      return left == right;\n    case \"===\":\n      return left === right;\n    case \"!=\":\n      return left != right;\n    case \"!==\":\n      return left !== right;\n    case \"+\":\n    case \"-\":\n    case \"/\":\n    case \"%\":\n    case \"*\":\n    case \"**\":\n    case \"&\":\n    case \"|\":\n    case \">>\":\n    case \">>>\":\n    case \"<<\":\n    case \"^\":\n    case \"in\":\n    case \"instanceof\":\n    case \">\":\n    case \"<\":\n    case \">=\":\n    case \"<=\":\n    case \"|>\":\n      break;\n  }\n  return null;\n}\n\nconst PROCESS_PROPERTIES = new Set([\n  \"abort\",\n  \"allowedNodeEnvironmentFlags\",\n  \"arch\",\n  \"argv\",\n  \"argv0\",\n  \"availableMemory\",\n  \"channel\",\n  \"chdir\",\n  \"config\",\n  \"connected\",\n  \"constrainedMemory\",\n  \"cpuUsage\",\n  \"cwd\",\n  \"debugPort\",\n  \"disconnect\",\n  \"dlopen\",\n  \"emitWarning\",\n  \"env\",\n  \"execArgv\",\n  \"execPath\",\n  \"execve\",\n  \"exit\",\n  \"exitCode\",\n  \"features\",\n  \"finalization\",\n  \"getActiveResourcesInfo\",\n  \"getBuiltinModule\",\n  \"getegid\",\n  \"geteuid\",\n  \"getgid\",\n  \"getgroups\",\n  \"getuid\",\n  \"hasUncaughtExceptionCaptureCallback\",\n  \"hrtime\",\n  \"initgroups\",\n  \"kill\",\n  \"loadEnvFile\",\n  \"mainModule\",\n  \"memoryUsage\",\n  \"nextTick\",\n  \"noDeprecation\",\n  \"permission\",\n  \"pid\",\n  \"platform\",\n  \"ppid\",\n  \"ref\",\n  \"release\",\n  \"report\",\n  \"resourceUsage\",\n  \"send\",\n  \"setegid\",\n  \"seteuid\",\n  \"setgid\",\n  \"setgroups\",\n  \"setuid\",\n  \"setSourceMapsEnabled\",\n  \"setUncaughtExceptionCaptureCallback\",\n  \"sourceMapsEnabled\",\n  \"stderr\",\n  \"stdin\",\n  \"stdout\",\n  \"throwDeprecation\",\n  \"threadCpuUsage\",\n  \"title\",\n  \"traceDeprecation\",\n  \"umask\",\n  \"unref\",\n  \"uptime\",\n  \"version\",\n  \"versions\",\n]);\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/code_eval_test.ts",
    "content": "import { expect } from \"@std/expect/expect\";\nimport * as babel from \"@babel/core\";\nimport { codeEvalPlugin } from \"./code_eval.ts\";\n\nfunction runTest(\n  options: {\n    input: string;\n    expected: string;\n    env: \"client\" | \"server\";\n    mode: \"development\" | \"production\";\n  },\n) {\n  const res = babel.transformSync(options.input, {\n    filename: \"foo.js\",\n    babelrc: false,\n    plugins: [codeEvalPlugin(options.env, options.mode)],\n  });\n\n  const output = res?.code ?? \"\";\n  expect(output).toEqual(options.expected);\n}\n\nDeno.test(\"code eval - resolve runtime conditional detection\", () => {\n  runTest({\n    env: \"client\",\n    mode: \"development\",\n    input:\n      `if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {\n\tmodule.exports = require('./browser.js');\n} else {\n\tmodule.exports = require('./node.js');\n}`,\n    expected: `module.exports = require('./browser.js');`,\n  });\n\n  runTest({\n    env: \"client\",\n    mode: \"development\",\n    input: `if (typeof process !== 'undefined') {\n\tmodule.exports = require('./node.js');\n} else {\n\tmodule.exports = require('./browser.js');\n}`,\n    expected: `module.exports = require('./browser.js');`,\n  });\n\n  runTest({\n    env: \"server\",\n    mode: \"development\",\n    input: `if (typeof process === 'undefined') {\n      module.exports = require('./browser.js');\n      } else {\n        module.exports = require('./node.js');\n    }`,\n    expected: `module.exports = require('./node.js');`,\n  });\n\n  runTest({\n    env: \"server\",\n    mode: \"development\",\n    input: `if (typeof process !== 'undefined') {\n\tmodule.exports = require('./node.js');\n} else {\n\tmodule.exports = require('./browser.js');\n}`,\n    expected: `module.exports = require('./node.js');`,\n  });\n});\n\nDeno.test(\"code eval - resolve process.env.NODE_ENV\", () => {\n  runTest({\n    env: \"client\",\n    mode: \"development\",\n    input: `if (process.env.NODE_ENV === 'production') {\n  module.exports = require('./cjs/react.production.js');\n} else {\n  module.exports = require('./cjs/react.development.js');\n}`,\n    expected: `module.exports = require('./cjs/react.development.js');`,\n  });\n\n  runTest({\n    env: \"client\",\n    mode: \"production\",\n    input: `if (process.env.NODE_ENV === 'production') {\n  module.exports = require('./cjs/react.production.js');\n} else {\n  module.exports = require('./cjs/react.development.js');\n}`,\n    expected: `module.exports = require('./cjs/react.production.js');`,\n  });\n});\n\nDeno.test(\"code eval - npm:debug\", () => {\n  runTest({\n    env: \"server\",\n    mode: \"development\",\n    input:\n      `if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {\n\tmodule.exports = require('./browser.js');\n} else {\n\tmodule.exports = require('./node.js');\n}`,\n    expected: `module.exports = require('./node.js');`,\n  });\n});\n\nDeno.test(\"code eval - npm:pg\", () => {\n  runTest({\n    env: \"server\",\n    mode: \"development\",\n    input: `if (typeof process.env.NODE_PG_FORCE_NATIVE !== \"undefined\") {\n\tmodule.exports = require('./native.js');\n} else {\n\tmodule.exports = require('./normal.js');\n}`,\n    expected: `module.exports = require('./normal.js');`,\n  });\n});\n\nDeno.test(\"code eval - npm:pg #2\", () => {\n  runTest({\n    env: \"server\",\n    mode: \"development\",\n    input:\n      `const useLegacyCrypto = parseInt(process.versions && process.versions.node && process.versions.node.split('.')[0]) < 15\nif (useLegacyCrypto) {\n  // We are on an old version of Node.js that requires legacy crypto utilities.\n  module.exports = require('./utils-legacy')\n} else {\n  module.exports = require('./utils-webcrypto')\n}\n`,\n    expected:\n      `const useLegacyCrypto = parseInt(process.versions && process.versions.node && process.versions.node.split('.')[0]) < 15;\nmodule.exports = require('./utils-webcrypto');`,\n  });\n});\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/commonjs.ts",
    "content": "import type { NodePath, PluginObj, types } from \"@babel/core\";\nimport { builtinModules } from \"node:module\";\n\nconst BUILTINS = new Set(builtinModules);\n\nexport function cjsPlugin(\n  { types: t }: { types: typeof types },\n): PluginObj {\n  const HAS_ES_MODULE = \"esModule\";\n  const REQUIRE_CALLS = \"requireCalls\";\n  const ROOT_SCOPE = \"rootScope\";\n  const EXPORTED = \"exported\";\n  const EXPORTED_NAMESPACES = \"exported_namespaces\";\n  const ALIASED = \"aliased\";\n  const REEXPORT = \"re-export\";\n  const NEEDS_REQUIRE_IMPORT = \"needsRequireImport\";\n  const IS_ESM = \"isESM\";\n\n  return {\n    name: \"fresh-cjs-esm\",\n    pre(file) {\n      const filename = file.opts.filename;\n      if (filename) {\n        if (filename.endsWith(\".mjs\") || filename.endsWith(\".cts\")) {\n          this.set(IS_ESM, true);\n        } else if (filename.endsWith(\".cjs\") || filename.endsWith(\".cts\")) {\n          this.set(IS_ESM, false);\n        }\n      }\n    },\n    visitor: {\n      Program: {\n        enter(path, state) {\n          state.set(ROOT_SCOPE, path.scope);\n          state.set(EXPORTED, new Set<string>());\n          state.set(EXPORTED_NAMESPACES, new Set<string>());\n          state.set(REEXPORT, null);\n\n          path.traverse({\n            Import(_path, state) {\n              state.set(IS_ESM, true);\n            },\n            ImportDeclaration(_path, state) {\n              state.set(IS_ESM, true);\n            },\n            ExportAllDeclaration(_path, state) {\n              state.set(IS_ESM, true);\n            },\n            ExportDefaultDeclaration(_path, state) {\n              state.set(IS_ESM, true);\n            },\n            ExportNamedDeclaration(_path, state) {\n              state.set(IS_ESM, true);\n            },\n          }, state);\n        },\n        exit(path, state) {\n          const isESM = state.get(IS_ESM);\n          if (isESM) return;\n\n          const body = path.get(\"body\");\n          const requires = state.get(REQUIRE_CALLS);\n          if (requires !== undefined) {\n            for (let i = 0; i < requires.length; i++) {\n              const { specifier, id } = requires[i];\n              path.unshiftContainer(\n                \"body\",\n                t.importDeclaration(\n                  [t.importNamespaceSpecifier(id)],\n                  specifier,\n                ),\n              );\n            }\n          }\n\n          const reexport = state.get(REEXPORT);\n          const exported = state.get(EXPORTED);\n          const exportedNs = state.get(EXPORTED_NAMESPACES);\n          const needsRequireImport = state.get(NEEDS_REQUIRE_IMPORT);\n          const hasEsModule = state.get(HAS_ES_MODULE);\n\n          if (needsRequireImport) {\n            // Inject:\n            // ```ts\n            // import { createRequire } from \"node:module\";\n            // const require = createRequire(import.meta.url);\n            // ```\n            const id = t.identifier(\"createRequire\");\n            path.unshiftContainer(\n              \"body\",\n              t.variableDeclaration(\"const\", [\n                t.variableDeclarator(\n                  t.identifier(\"require\"),\n                  t.callExpression(t.identifier(\"createRequire\"), [\n                    t.memberExpression(\n                      t.metaProperty(\n                        t.identifier(\"import\"),\n                        t.identifier(\"meta\"),\n                      ),\n                      t.identifier(\"url\"),\n                    ),\n                  ]),\n                ),\n              ]),\n            );\n            path.unshiftContainer(\n              \"body\",\n              t.importDeclaration(\n                [t.importSpecifier(id, id)],\n                t.stringLiteral(\"node:module\"),\n              ),\n            );\n          }\n\n          if (reexport !== null) {\n            path.unshiftContainer(\n              \"body\",\n              t.exportAllDeclaration(t.cloneNode(reexport, true)),\n            );\n          }\n\n          const mappedNs: string[] = [];\n\n          for (const spec of exportedNs.values()) {\n            const id = path.scope.generateUidIdentifier(\"__ns\");\n            mappedNs.push(id.name);\n\n            path.unshiftContainer(\n              \"body\",\n              t.importDeclaration(\n                [t.importNamespaceSpecifier(id)],\n                t.stringLiteral(spec),\n              ),\n            );\n          }\n\n          if (exported.size > 0 || exportedNs.size > 0 || hasEsModule) {\n            path.unshiftContainer(\n              \"body\",\n              t.expressionStatement(\n                t.callExpression(\n                  t.memberExpression(\n                    t.identifier(\"Object\"),\n                    t.identifier(\"defineProperty\"),\n                  ),\n                  [\n                    t.identifier(\"exports\"),\n                    t.stringLiteral(\"__esModule\"),\n                    t.objectExpression([\n                      t.objectProperty(\n                        t.identifier(\"value\"),\n                        t.booleanLiteral(true),\n                      ),\n                    ]),\n                  ],\n                ),\n              ),\n            );\n            path.unshiftContainer(\n              \"body\",\n              t.expressionStatement(\n                t.callExpression(\n                  t.memberExpression(\n                    t.identifier(\"Object\"),\n                    t.identifier(\"defineProperty\"),\n                  ),\n                  [\n                    t.identifier(\"module\"),\n                    t.stringLiteral(\"exports\"),\n                    t.objectExpression([\n                      t.objectMethod(\n                        \"method\",\n                        t.identifier(\"get\"),\n                        [],\n                        t.blockStatement([\n                          t.returnStatement(t.identifier(\"exports\")),\n                        ]),\n                      ),\n                      t.objectMethod(\n                        \"method\",\n                        t.identifier(\"set\"),\n                        [t.identifier(\"value\")],\n                        t.blockStatement([\n                          t.expressionStatement(\n                            t.assignmentExpression(\n                              \"=\",\n                              t.identifier(\"exports\"),\n                              t.identifier(\"value\"),\n                            ),\n                          ),\n                        ]),\n                      ),\n                    ]),\n                  ],\n                ),\n              ),\n            );\n            path.unshiftContainer(\n              \"body\",\n              t.variableDeclaration(\"var\", [\n                t.variableDeclarator(\n                  t.identifier(\"exports\"),\n                  t.objectExpression([]),\n                ),\n                t.variableDeclarator(\n                  t.identifier(\"module\"),\n                  t.objectExpression([]),\n                ),\n              ]),\n            );\n          }\n\n          const exportNamed = new Map<string, string>();\n\n          const idExports: types.ExportSpecifier[] = [];\n          for (const name of exported) {\n            if (name === \"default\") {\n              exportNamed.set(name, name);\n              continue;\n            }\n\n            const id = path.scope.generateUidIdentifier(name);\n\n            exportNamed.set(id.name, name);\n\n            path.pushContainer(\n              \"body\",\n              t.variableDeclaration(\n                \"var\",\n                [t.variableDeclarator(\n                  id,\n                  t.memberExpression(\n                    t.identifier(\"exports\"),\n                    t.identifier(name),\n                  ),\n                )],\n              ),\n            );\n            idExports.push(\n              t.exportSpecifier(id, t.identifier(name)),\n            );\n          }\n\n          if (idExports.length > 0) {\n            path.pushContainer(\n              \"body\",\n              t.exportNamedDeclaration(null, idExports),\n            );\n          }\n\n          if (exportNamed.size > 0 || exportedNs.size > 0 || hasEsModule) {\n            const id = path.scope.generateUidIdentifier(\"__default\");\n\n            path.pushContainer(\n              \"body\",\n              t.variableDeclaration(\"const\", [\n                t.variableDeclarator(\n                  id,\n                  t.logicalExpression(\n                    \"??\",\n                    t.memberExpression(\n                      t.identifier(\"exports\"),\n                      t.identifier(\"default\"),\n                    ),\n                    t.identifier(\"exports\"),\n                  ),\n                ),\n              ]),\n            );\n\n            for (let i = 0; i < mappedNs.length; i++) {\n              const mapped = mappedNs[i];\n\n              const key = path.scope.generateUid(\"k\");\n              path.pushContainer(\n                \"body\",\n                t.forInStatement(\n                  t.variableDeclaration(\"var\", [\n                    t.variableDeclarator(t.identifier(key)),\n                  ]),\n                  t.identifier(mapped),\n                  t.ifStatement(\n                    t.logicalExpression(\n                      \"&&\",\n                      t.logicalExpression(\n                        \"&&\",\n                        t.binaryExpression(\n                          \"!==\",\n                          t.identifier(key),\n                          t.stringLiteral(\"default\"),\n                        ),\n                        t.binaryExpression(\n                          \"!==\",\n                          t.identifier(key),\n                          t.stringLiteral(\"__esModule\"),\n                        ),\n                      ),\n                      t.callExpression(\n                        t.memberExpression(\n                          t.memberExpression(\n                            t.memberExpression(\n                              t.identifier(\"Object\"),\n                              t.identifier(\"prototype\"),\n                            ),\n                            t.identifier(\"hasOwnProperty\"),\n                          ),\n                          t.identifier(\"call\"),\n                        ),\n                        [t.identifier(mapped), t.identifier(key)],\n                      ),\n                    ),\n                    t.expressionStatement(\n                      t.assignmentExpression(\n                        \"=\",\n                        t.memberExpression(\n                          t.cloneNode(id, true),\n                          t.identifier(key),\n                          true,\n                        ),\n                        t.memberExpression(\n                          t.identifier(mapped),\n                          t.identifier(key),\n                          true,\n                        ),\n                      ),\n                    ),\n                  ),\n                ),\n              );\n            }\n\n            path.pushContainer(\"body\", t.exportDefaultDeclaration(id));\n            path.pushContainer(\n              \"body\",\n              t.exportNamedDeclaration(\n                t.variableDeclaration(\"var\", [\n                  t.variableDeclarator(\n                    t.identifier(\"__require\"),\n                    t.identifier(\"exports\"),\n                  ),\n                ]),\n              ),\n            );\n          }\n\n          if (body.length === 0 && hasEsModule) {\n            path.pushContainer(\"body\", t.exportNamedDeclaration(null));\n          } else if (hasEsModule) {\n            path.pushContainer(\n              \"body\",\n              t.exportNamedDeclaration(\n                t.variableDeclaration(\n                  \"var\",\n                  [t.variableDeclarator(\n                    t.identifier(\"__esModule\"),\n                    t.memberExpression(\n                      t.identifier(\"exports\"),\n                      t.identifier(\"__esModule\"),\n                    ),\n                  )],\n                ),\n              ),\n            );\n          }\n        },\n      },\n      CallExpression(path, state) {\n        if (state.get(IS_ESM)) return;\n        const exported = state.get(EXPORTED);\n\n        if (isObjEsModuleFlag(t, path.node)) {\n          state.set(HAS_ES_MODULE, true);\n          return;\n        }\n\n        if (\n          t.isIdentifier(path.node.callee) &&\n          path.node.callee.name === \"require\"\n        ) {\n          const root = state.get(ROOT_SCOPE);\n          const id = root.generateUidIdentifier(\"mod\");\n\n          const mods = state.get(REQUIRE_CALLS) ?? [];\n          state.set(REQUIRE_CALLS, mods);\n\n          const source = path.node.arguments[0];\n          if (t.isStringLiteral(source)) {\n            // Check if we can hoist it or if we need to keep it.\n            let canImport = true;\n            let parent: NodePath | null = path.parentPath;\n            while (parent !== null) {\n              if (\n                t.isTryStatement(parent.node) || t.isIfStatement(parent.node) ||\n                t.isConditionalExpression(parent.node)\n              ) {\n                canImport = false;\n                break;\n              }\n              parent = parent.parentPath;\n            }\n\n            if (!canImport) {\n              state.set(NEEDS_REQUIRE_IMPORT, true);\n              return;\n            }\n\n            mods.push({\n              id,\n              specifier: t.cloneNode(path.node.arguments[0], true),\n            });\n\n            if (\n              path.parentPath?.isVariableDeclarator() &&\n                path.parentPath?.get(\"id\").isIdentifier() ||\n              path.parentPath?.isCallExpression()\n            ) {\n              // Vite json processing always adds a default property.\n              if (source.value.endsWith(\".json\")) {\n                path.replaceWith(\n                  t.logicalExpression(\n                    \"??\",\n                    t.memberExpression(\n                      t.cloneNode(id, true),\n                      t.identifier(\"default\"),\n                    ),\n                    t.cloneNode(id, true),\n                  ),\n                );\n              } else if (\n                path.parentPath?.isCallExpression() &&\n                t.isIdentifier(path.parentPath.node.callee) &&\n                path.parentPath.node.callee.name === \"__importDefault\"\n              ) {\n                if (isNodeBuiltin(source.value)) {\n                  path.replaceWith(t.objectExpression([\n                    t.objectProperty(\n                      t.identifier(\"__esModule\"),\n                      t.booleanLiteral(true),\n                    ),\n                    t.objectProperty(\n                      t.identifier(\"default\"),\n                      t.logicalExpression(\n                        \"??\",\n                        t.memberExpression(\n                          t.cloneNode(id, true),\n                          t.identifier(\"default\"),\n                        ),\n                        t.cloneNode(id, true),\n                      ),\n                    ),\n                  ]));\n                } else {\n                  path.replaceWith(t.cloneNode(id, true));\n                }\n              } else {\n                path.replaceWith(\n                  t.logicalExpression(\n                    \"??\",\n                    t.memberExpression(\n                      t.cloneNode(id, true),\n                      t.identifier(\"__require\"),\n                    ),\n                    t.logicalExpression(\n                      \"??\",\n                      t.memberExpression(\n                        t.cloneNode(id, true),\n                        t.identifier(\"default\"),\n                      ),\n                      t.cloneNode(id, true),\n                    ),\n                  ),\n                );\n              }\n              return;\n            }\n\n            path.replaceWith(t.cloneNode(id, true));\n          } else {\n            state.set(NEEDS_REQUIRE_IMPORT, true);\n          }\n        } else if (\n          t.isMemberExpression(path.node.callee) &&\n          t.isIdentifier(path.node.callee.object) &&\n          path.node.callee.object.name === \"Object\" &&\n          t.isIdentifier(path.node.callee.property) &&\n          path.node.callee.property.name === \"defineProperty\" &&\n          path.node.arguments.length > 0 &&\n          t.isIdentifier(path.node.arguments[0]) &&\n          path.node.arguments[0].name === \"exports\" &&\n          t.isStringLiteral(path.node.arguments[1])\n        ) {\n          const name = path.node.arguments[1].value;\n          exported.add(name);\n        }\n      },\n      EmptyStatement(path) {\n        path.remove();\n      },\n      MemberExpression: {\n        exit(path, state) {\n          if (state.get(IS_ESM)) return;\n          if (\n            t.isIdentifier(path.node.object) &&\n            path.node.object.name === \"exports\" &&\n            t.isIdentifier(path.node.property)\n          ) {\n            const name = t.cloneNode(path.node.property);\n\n            if (name.name === \"__esModule\") return;\n\n            state.get(EXPORTED).add(name.name);\n          }\n        },\n      },\n      ExpressionStatement: {\n        enter(path, state) {\n          if (state.get(IS_ESM)) return;\n          // Check: Object.defineProperty(module.exports) \"__esModule\" ...)\n          // Check: Object.defineProperty(exports) \"__esModule\" ...)\n          // Check: a({}, \"__esModule\", ...)\n          if (\n            t.isCallExpression(path.node.expression) &&\n            path.node.expression.arguments.length === 3 &&\n            t.isStringLiteral(path.node.expression.arguments[1]) &&\n            path.node.expression.arguments[1].value === \"__esModule\"\n          ) {\n            state.set(HAS_ES_MODULE, true);\n            return;\n          }\n\n          if (\n            t.isExpressionStatement(path.node) &&\n            t.isCallExpression(path.node.expression) &&\n            t.isIdentifier(path.node.expression.callee) &&\n            path.node.expression.callee.name === \"__exportStar\" &&\n            path.node.expression.arguments.length > 0 &&\n            t.isCallExpression(path.node.expression.arguments[0]) &&\n            t.isIdentifier(path.node.expression.arguments[0].callee) &&\n            path.node.expression.arguments[0].callee.name === \"require\" &&\n            t.isStringLiteral(path.node.expression.arguments[0].arguments[0])\n          ) {\n            const spec = t.cloneNode(\n              path.node.expression.arguments[0].arguments[0],\n              true,\n            );\n            state.get(EXPORTED_NAMESPACES).add(spec.value);\n            path.replaceWith(t.exportAllDeclaration(spec));\n          } else if (\n            t.isExpressionStatement(path.node) &&\n            t.isCallExpression(path.node.expression) &&\n            t.isFunctionExpression(path.node.expression.callee)\n          ) {\n            if (\n              path.node.expression.callee.params.length > 0 &&\n              t.isIdentifier(path.node.expression.callee.params[0])\n            ) {\n              const alias = path.node.expression.callee.params[0].name;\n              state.set(ALIASED, alias);\n            }\n          } else if (\n            // Check: Object.defineProperty(exports, \"foo\", { enumerable: true, get: function () { return foo; } });\n            t.isCallExpression(path.node.expression) &&\n            t.isMemberExpression(path.node.expression.callee) &&\n            t.isIdentifier(path.node.expression.callee.object) &&\n            path.node.expression.callee.object.name === \"Object\" &&\n            t.isIdentifier(path.node.expression.callee.property) &&\n            path.node.expression.callee.property.name === \"defineProperty\" &&\n            path.node.expression.arguments.length >= 2 &&\n            t.isIdentifier(path.node.expression.arguments[0]) &&\n            path.node.expression.arguments[0].name === \"exports\" &&\n            t.isStringLiteral(path.node.expression.arguments[1]) &&\n            t.isObjectExpression(path.node.expression.arguments[2])\n          ) {\n            const exported = path.node.expression.arguments[1].value;\n            const obj = path.node.expression.arguments[2];\n            for (let i = 0; i < obj.properties.length; i++) {\n              const prop = obj.properties[i];\n\n              if (\n                t.isObjectProperty(prop) && t.isIdentifier(prop.key) &&\n                prop.key.name === \"get\" && t.isFunctionExpression(prop.value) &&\n                t.isBlockStatement(prop.value.body) &&\n                prop.value.body.body.length === 1 &&\n                t.isReturnStatement(prop.value.body.body[0])\n              ) {\n                const expr = prop.value.body.body[0].argument;\n                if (expr !== null && expr !== undefined) {\n                  path.replaceWith(\n                    t.assignmentExpression(\n                      \"=\",\n                      t.memberExpression(\n                        t.identifier(\"exports\"),\n                        t.identifier(exported),\n                      ),\n                      t.cloneNode(expr, true),\n                    ),\n                  );\n                }\n              } else if (\n                t.isObjectMethod(prop) && t.isIdentifier(prop.key) &&\n                prop.key.name === \"get\" && t.isBlockStatement(prop.body) &&\n                prop.body.body.length === 1 &&\n                t.isReturnStatement(prop.body.body[0])\n              ) {\n                const expr = prop.body.body[0].argument;\n                if (expr !== null && expr !== undefined) {\n                  path.replaceWith(\n                    t.assignmentExpression(\n                      \"=\",\n                      t.memberExpression(\n                        t.identifier(\"exports\"),\n                        t.identifier(exported),\n                      ),\n                      t.cloneNode(expr, true),\n                    ),\n                  );\n                }\n              }\n            }\n          } else if (\n            // Check: module.exports = require(...)\n            t.isAssignmentExpression(path.node.expression) &&\n            t.isMemberExpression(path.node.expression.left) &&\n            t.isIdentifier(path.node.expression.left.object) &&\n            t.isIdentifier(path.node.expression.left.property) &&\n            path.node.expression.left.object.name === \"module\" &&\n            path.node.expression.left.property.name === \"exports\" &&\n            t.isCallExpression(path.node.expression.right) &&\n            t.isIdentifier(path.node.expression.right.callee) &&\n            path.node.expression.right.callee.name === \"require\" &&\n            path.node.expression.right.arguments.length === 1 &&\n            t.isStringLiteral(path.node.expression.right.arguments[0])\n          ) {\n            const source = path.node.expression.right.arguments[0];\n            state.set(REEXPORT, source);\n          } else {\n            let depth = 0;\n            let current = path.node.expression;\n\n            while (\n              t.isAssignmentExpression(current) &&\n              t.isMemberExpression(current.left) &&\n              t.isIdentifier(current.left.object) &&\n              current.left.object.name === \"exports\"\n            ) {\n              if (\n                t.isUnaryExpression(current.right) &&\n                current.right.operator === \"void\" &&\n                t.isNumericLiteral(current.right.argument) &&\n                current.right.argument.value === 0\n              ) {\n                if (depth > 0) {\n                  path.remove();\n                }\n\n                break;\n              }\n\n              depth++;\n              current = current.right;\n            }\n          }\n        },\n        exit(path, state) {\n          if (state.get(IS_ESM)) return;\n          const exported = state.get(EXPORTED);\n          const expr = path.get(\"expression\");\n\n          if (expr.isAssignmentExpression()) {\n            const left = expr.get(\"left\");\n\n            if (isEsModuleFlag(t, expr.node)) {\n              state.set(HAS_ES_MODULE, true);\n            } else if (left.isMemberExpression()) {\n              if (isModuleExports(t, left.node)) {\n                exported.add(\"default\");\n\n                if (t.isObjectExpression(expr.node.right)) {\n                  const properties = expr.node.right.properties;\n                  for (let i = 0; i < properties.length; i++) {\n                    const prop = properties[i];\n                    if (t.isObjectProperty(prop)) {\n                      if (t.isIdentifier(prop.key)) {\n                        if (prop.key.name === \"__esModule\") {\n                          continue;\n                        }\n\n                        exported.add(prop.key.name);\n                      }\n                    }\n                  }\n                }\n              } else {\n                const named = getExportsAssignName(t, left.node);\n                if (named === null) return;\n                exported.add(named);\n              }\n            }\n          } else if (expr.isCallExpression()) {\n            if (isObjEsModuleFlag(t, expr.node)) {\n              state.set(HAS_ES_MODULE, true);\n            }\n          }\n        },\n      },\n      VariableDeclaration(path) {\n        if (path.node.declarations.length === 0) {\n          path.remove();\n        }\n      },\n      ConditionalExpression(path, state) {\n        if (state.get(IS_ESM)) return;\n\n        if (\n          t.isBinaryExpression(path.node.test) &&\n          t.isUnaryExpression(path.node.test.left) &&\n          path.node.test.left.operator === \"typeof\" &&\n          t.isIdentifier(path.node.test.left.argument) &&\n          path.node.test.left.argument.name === \"exports\" &&\n          path.node.test.operator === \"===\"\n        ) {\n          path.replaceWith(t.cloneNode(path.node.alternate, true));\n        }\n      },\n      AssignmentExpression(path, state) {\n        if (state.get(IS_ESM)) return;\n\n        const exported = state.get(EXPORTED);\n        const aliased = state.get(ALIASED);\n        if (aliased === undefined) return;\n\n        if (\n          path.node.operator === \"=\" && t.isMemberExpression(path.node.left) &&\n          t.isIdentifier(path.node.left.object) &&\n          path.node.left.object.name === aliased &&\n          t.isIdentifier(path.node.left.property)\n        ) {\n          const name = path.node.left.property.name;\n          exported.add(name);\n        }\n      },\n    },\n  };\n}\n\nfunction isModuleExports(\n  t: typeof types,\n  node: types.MemberExpression,\n): boolean {\n  return t.isIdentifier(node.object) && node.object.name === \"module\" &&\n    t.isIdentifier(node.property) && node.property.name === \"exports\";\n}\n\nfunction getExportsAssignName(\n  t: typeof types,\n  node: types.MemberExpression,\n): string | null {\n  if (\n    (t.isMemberExpression(node.object) &&\n        isModuleExports(t, node.object) ||\n      t.isIdentifier(node.object) && node.object.name === \"exports\") &&\n    t.isIdentifier(node.property)\n  ) {\n    return node.property.name;\n  }\n\n  return null;\n}\n\n/**\n * Detect `exports.__esModule = true;`\n */\nfunction isEsModuleFlag(\n  t: typeof types,\n  node: types.AssignmentExpression,\n): boolean {\n  if (!t.isMemberExpression(node.left)) return false;\n\n  const { left, right } = node;\n  return (t.isMemberExpression(left.object) &&\n      isModuleExports(t, left.object) ||\n    t.isIdentifier(left.object) && left.object.name === \"exports\") &&\n    t.isIdentifier(left.property) && left.property.name === \"__esModule\" &&\n    t.isBooleanLiteral(right);\n}\n\n/**\n * Check for `Object.defineProperty(exports, '__esModule', { value: true })`\n */\nfunction isObjEsModuleFlag(\n  t: typeof types,\n  node: types.CallExpression,\n): boolean {\n  return node.arguments.length === 3 &&\n    t.isStringLiteral(node.arguments[1]) &&\n    node.arguments[1].value === \"__esModule\" &&\n    t.isObjectExpression(node.arguments[2]);\n}\n\nfunction isNodeBuiltin(specifier: string): boolean {\n  return BUILTINS.has(specifier) || (\n    specifier.startsWith(\"node:\")\n      ? BUILTINS.has(specifier.slice(\"node:\".length))\n      : BUILTINS.has(`node:${specifier}`)\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/commonjs_test.ts",
    "content": "import { expect } from \"@std/expect/expect\";\nimport * as babel from \"@babel/core\";\nimport { cjsPlugin } from \"../patches/commonjs.ts\";\n\nfunction runTest(\n  options: { input: string; expected: string; filename?: string },\n) {\n  const res = babel.transformSync(options.input, {\n    filename: options.filename ?? \"foo.js\",\n    babelrc: false,\n    plugins: [cjsPlugin],\n  });\n\n  const output = res?.code ?? \"\";\n  expect(output).toEqual(options.expected);\n}\n\nconst INIT = `var exports = {},\n  module = {};\nObject.defineProperty(module, \"exports\", {\n  get() {\n    return exports;\n  },\n  set(value) {\n    exports = value;\n  }\n});\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});`;\n\nconst DEFAULT_EXPORT = `const _default = exports.default ?? exports;`;\nconst DEFAULT_EXPORT_END = `export default _default;\nexport var __require = exports;`;\nconst IMPORT_REQUIRE = `import { createRequire } from \"node:module\";\nconst require = createRequire(import.meta.url);`;\nconst EXPORT_ES_MODULE = `export var __esModule = exports.__esModule;`;\n\nDeno.test(\"commonjs - module.exports default\", () => {\n  runTest({\n    input: `module.exports = async function () {};`,\n    expected: `${INIT}\nmodule.exports = async function () {};\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - module.exports default primitive\", () => {\n  runTest({\n    input: `module.exports = 42;`,\n    expected: `${INIT}\nmodule.exports = 42;\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - exports with default + named\", () => {\n  runTest({\n    input: `exports.__esModule = true;\nexports.default = 'x';\nexports.foo = 'foo';`,\n    expected: `${INIT}\nexports.__esModule = true;\nexports.default = 'x';\nexports.foo = 'foo';\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - module.exports with default + named\", () => {\n  runTest({\n    input: `module.exports.__esModule = true;\nmodule.exports.default = 'x';\nmodule.exports.foo = 'foo';`,\n    expected: `${INIT}\nmodule.exports.__esModule = true;\nmodule.exports.default = 'x';\nmodule.exports.foo = 'foo';\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - Object es module flag with named clash\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, '__esModule', { value: true });\nexports.foo = 'bar';\nconst foo = 'also bar';\n`,\n    expected: `${INIT}\nObject.defineProperty(exports, '__esModule', {\n  value: true\n});\nexports.foo = 'bar';\nconst foo = 'also bar';\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - Object es module flag with named + default\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, '__esModule', { value: true });\nexports.default = 'foo';\nexports.foo = 'bar';\n`,\n    expected: `${INIT}\nObject.defineProperty(exports, '__esModule', {\n  value: true\n});\nexports.default = 'foo';\nexports.foo = 'bar';\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - esModule flag only\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, \"__esModule\", { value: true });`,\n    expected: `${INIT}\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nconst _default = exports.default ?? exports;\nexport default _default;\nexport var __require = exports;\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - esModule flag only #2\", () => {\n  runTest({\n    input:\n      `Object.defineProperty(module.exports, \"__esModule\", { value: true });`,\n    expected: `${INIT}\nObject.defineProperty(module.exports, \"__esModule\", {\n  value: true\n});\nconst _default = exports.default ?? exports;\nexport default _default;\nexport var __require = exports;\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - esModule flag only minified #3\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, '__esModule', { value: !0 });`,\n    expected: `${INIT}\nObject.defineProperty(exports, '__esModule', {\n  value: !0\n});\nconst _default = exports.default ?? exports;\nexport default _default;\nexport var __require = exports;\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - exports only named\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, '__esModule', { value: true });\nexports.foo = 'bar';\nexports.bar = 'foo';\n`,\n    expected: `${INIT}\nObject.defineProperty(exports, '__esModule', {\n  value: true\n});\nexports.foo = 'bar';\nexports.bar = 'foo';\nvar _foo = exports.foo;\nvar _bar = exports.bar;\nexport { _foo as foo, _bar as bar };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - require\", () => {\n  runTest({\n    input: `var foo = require(\"tape\");\nconsole.log(foo);\n`,\n    expected: `import * as _mod from \"tape\";\nvar foo = _mod.__require ?? _mod.default ?? _mod;\nconsole.log(foo);`,\n  });\n});\n\nDeno.test(\"commonjs - require destructure\", () => {\n  runTest({\n    input: `var { foo } = require(\"tape\");\nconsole.log(foo);\n`,\n    expected: `import * as _mod from \"tape\";\nvar {\n  foo\n} = _mod;\nconsole.log(foo);`,\n  });\n});\n\nDeno.test(\"commonjs - require assign\", () => {\n  runTest({\n    input: `foo = require(\"tape\");\nconsole.log(foo);\n`,\n    expected: `import * as _mod from \"tape\";\nfoo = _mod;\nconsole.log(foo);`,\n  });\n});\n\nDeno.test(\"commonjs - require assign pattern\", () => {\n  runTest({\n    input: `foo = require(\"tape\");\nconsole.log(foo);\n`,\n    expected: `import * as _mod from \"tape\";\nfoo = _mod;\nconsole.log(foo);`,\n  });\n});\n\nDeno.test(\"commonjs - require function call\", () => {\n  runTest({\n    input: `var a = require('./a')()`,\n    expected: `import * as _mod from './a';\nvar a = (_mod.__require ?? _mod.default ?? _mod)();`,\n  });\n});\n\nDeno.test(\"commonjs - require var decls\", () => {\n  runTest({\n    input: `var a = require('./a'), b = 42;`,\n    expected: `import * as _mod from './a';\nvar a = _mod.__require ?? _mod.default ?? _mod,\n  b = 42;`,\n  });\n});\n\nDeno.test(\"commonjs - duplicate exports\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, \"__esModule\", { value: true });\nexports.trace = void 0;\nexports.trace = 'foo'`,\n    expected: `${INIT}\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.trace = void 0;\nexports.trace = 'foo';\nvar _trace = exports.trace;\nexport { _trace as trace };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - cleared exports\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, \"__esModule\", { value: true });\nexports.foo = exports.bar = void 0;\nexports.foo = 'foo'`,\n    expected: `${INIT}\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.foo = 'foo';\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - define exports\", () => {\n  runTest({\n    input: `var utils_1 = require(\"./bar\");\nObject.defineProperty(exports, \"foo\", { enumerable: true, get: function () { return utils_1.foo; } });`,\n    expected: `${INIT}\nimport * as _mod from \"./bar\";\nvar utils_1 = _mod.__require ?? _mod.default ?? _mod;\nexports.foo = utils_1.foo;\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - define exports #2\", () => {\n  runTest({\n    input: `var utils_1 = require(\"./bar\");\nObject.defineProperty(exports, \"foo\", { enumerable: true, get() { return utils_1.foo; } });`,\n    expected: `${INIT}\nimport * as _mod from \"./bar\";\nvar utils_1 = _mod.__require ?? _mod.default ?? _mod;\nexports.foo = utils_1.foo;\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - define exports #3\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, \"__esModule\", { value: true });\nexports._globalThis = void 0;\nexports._globalThis = typeof globalThis === 'object' ? globalThis : global;`,\n    expected: `${INIT}\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports._globalThis = void 0;\nexports._globalThis = typeof globalThis === 'object' ? globalThis : global;\nvar _globalThis = exports._globalThis;\nexport { _globalThis };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - named function\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, \"__esModule\", { value: true });\nfunction foo() {};\nexports.foo = foo;`,\n    expected: `${INIT}\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nfunction foo() {}\nexports.foo = foo;\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - detect esbuild shims\", () => {\n  runTest({\n    input: `__exportStar(require(\"./globalThis\"), exports);`,\n    expected: `${INIT}\nimport * as _ns from \"./globalThis\";\nexport * from \"./globalThis\";\n${DEFAULT_EXPORT}\nfor (var _k in _ns) if (_k !== \"default\" && _k !== \"__esModule\" && Object.prototype.hasOwnProperty.call(_ns, _k)) _default[_k] = _ns[_k];\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - exports.default\", () => {\n  runTest({\n    input: `exports.default = {}`,\n    expected: `${INIT}\nexports.default = {};\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - multiple same name\", () => {\n  runTest({\n    input: `exports.VERSION = void 0;\nexports.VERSION = '1.9.0';`,\n    expected: `${INIT}\nexports.VERSION = void 0;\nexports.VERSION = '1.9.0';\nvar _VERSION = exports.VERSION;\nexport { _VERSION as VERSION };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - export enum\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, \"__esModule\", { value: true });\nexports.DiagLogLevel = void 0;\nvar DiagLogLevel;\n(function (DiagLogLevel) {\n    DiagLogLevel[DiagLogLevel[\"ALL\"] = 9999] = \"ALL\";\n})(DiagLogLevel = exports.DiagLogLevel || (exports.DiagLogLevel = {}));`,\n    expected: `${INIT}\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.DiagLogLevel = void 0;\nvar DiagLogLevel;\n(function (DiagLogLevel) {\n  DiagLogLevel[DiagLogLevel[\"ALL\"] = 9999] = \"ALL\";\n})(DiagLogLevel = exports.DiagLogLevel || (exports.DiagLogLevel = {}));\nvar _DiagLogLevel = exports.DiagLogLevel;\nexport { _DiagLogLevel as DiagLogLevel };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - require\", () => {\n  runTest({\n    input: `module.exports = { __esModule: true, default: { foo: 'bar' }}`,\n    expected: `${INIT}\nmodule.exports = {\n  __esModule: true,\n  default: {\n    foo: 'bar'\n  }\n};\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - export default object\", () => {\n  runTest({\n    input: `Object.defineProperty(exports, '__esModule', { value: true });\nmodule.exports = { foo: 'bar' };\n`,\n    expected: `${INIT}\nObject.defineProperty(exports, '__esModule', {\n  value: true\n});\nmodule.exports = {\n  foo: 'bar'\n};\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - detect iife wrapper\", () => {\n  runTest({\n    input: `;(function (sax) {\n  sax.foo = \"foo\";\n})(typeof exports === 'undefined' ? this.sax = {} : exports);`,\n    expected: `${INIT}\n(function (sax) {\n  sax.foo = \"foo\";\n})(exports);\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - re-export\", () => {\n  runTest({\n    input:\n      `;var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {\n    if (k2 === undefined) k2 = k;\n    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\n}) : (function(o, m, k, k2) {\n    if (k2 === undefined) k2 = k;\n    o[k2] = m[k];\n}));\nvar __exportStar = (this && this.__exportStar) || function(m, exports) {\n    for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\n__exportStar(require(\"./node\"), exports);`,\n    expected: `${INIT}\nimport * as _ns from \"./node\";\nvar __createBinding = this && this.__createBinding || (Object.create ? function (o, m, k, k2) {\n  if (k2 === undefined) k2 = k;\n  Object.defineProperty(o, k2, {\n    enumerable: true,\n    get: function () {\n      return m[k];\n    }\n  });\n} : function (o, m, k, k2) {\n  if (k2 === undefined) k2 = k;\n  o[k2] = m[k];\n});\nvar __exportStar = this && this.__exportStar || function (m, exports) {\n  for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);\n};\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexport * from \"./node\";\n${DEFAULT_EXPORT}\nfor (var _k in _ns) if (_k !== \"default\" && _k !== \"__esModule\" && Object.prototype.hasOwnProperty.call(_ns, _k)) _default[_k] = _ns[_k];\n${DEFAULT_EXPORT_END}\n${EXPORT_ES_MODULE}`,\n  });\n});\n\nDeno.test(\"commonjs - assign module.exports\", () => {\n  runTest({\n    input: `module.exports = { foo: 1 };`,\n    expected: `${INIT}\nmodule.exports = {\n  foo: 1\n};\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - require non-analyzable arg\", () => {\n  runTest({\n    input: `const pkg = require(path.join(basedir, \"package.json\"))`,\n    expected: `import { createRequire } from \"node:module\";\nconst require = createRequire(import.meta.url);\nconst pkg = require(path.join(basedir, \"package.json\"));`,\n  });\n});\n\nDeno.test(\"commonjs - keep binding\", () => {\n  runTest({\n    input: `export var __createBinding = Object.create ? 1 : 2;`,\n    expected: `export var __createBinding = Object.create ? 1 : 2;`,\n  });\n});\n\nDeno.test(\"commonjs - require lazy import\", () => {\n  runTest({\n    input: `if (typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined') {\n  module.exports = new PG(require('./native'))\n} else {\n  module.exports = new PG(Client)\n\n  // lazy require native module...the native module may not have installed\n  Object.defineProperty(module.exports, 'native', {\n    configurable: true,\n    enumerable: false,\n    get() {\n      let native = null\n      try {\n        native = new PG(require('./native'))\n      } catch (err) {\n        if (err.code !== 'MODULE_NOT_FOUND') {\n          throw err\n        }\n      }\n\n      // overwrite module.exports.native so that getter is never called again\n      Object.defineProperty(module.exports, 'native', {\n        value: native,\n      })\n\n      return native\n    },\n  })\n}`,\n    expected: `${INIT}\n${IMPORT_REQUIRE}\nif (typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined') {\n  module.exports = new PG(require('./native'));\n} else {\n  module.exports = new PG(Client);\n\n  // lazy require native module...the native module may not have installed\n  Object.defineProperty(module.exports, 'native', {\n    configurable: true,\n    enumerable: false,\n    get() {\n      let native = null;\n      try {\n        native = new PG(require('./native'));\n      } catch (err) {\n        if (err.code !== 'MODULE_NOT_FOUND') {\n          throw err;\n        }\n      }\n\n      // overwrite module.exports.native so that getter is never called again\n      Object.defineProperty(module.exports, 'native', {\n        value: native\n      });\n      return native;\n    }\n  });\n}\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - wrapped iife binding\", () => {\n  runTest({\n    input: `\"production\" !== process.env.NODE_ENV && (function() {\n  exports.foo = 123\n})()`,\n    expected: `${INIT}\n\"production\" !== process.env.NODE_ENV && function () {\n  exports.foo = 123;\n}();\nvar _foo = exports.foo;\nexport { _foo as foo };\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - re-export #2\", () => {\n  runTest({\n    input: `module.exports = require(\"foo\");`,\n    expected: `${INIT}\nexport * from \"foo\";\nimport * as _mod from \"foo\";\nmodule.exports = _mod;\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}`,\n  });\n});\n\nDeno.test(\"commonjs - keep require() in .mjs\", () => {\n  runTest({\n    filename: \"foo.mjs\",\n    input: `try { require(\"foo\"); } catch {}`,\n    expected: `try {\n  require(\"foo\");\n} catch {}`,\n  });\n});\n\nDeno.test(\"commonjs - keep conditional require() in ESM file\", () => {\n  runTest({\n    filename: \"foo.mjs\",\n    input: `try { require(\"foo\"); } catch {};\nexport {};`,\n    expected: `try {\n  require(\"foo\");\n} catch {}\nexport {};`,\n  });\n});\n\nDeno.test(\"commonjs - CJS turned ESM module\", () => {\n  runTest({\n    filename: \"foo.mjs\",\n    input: `module.exports.create = confettiCannon;\nexport default module.exports;\nexport var create = module.exports.create;`,\n    expected: `module.exports.create = confettiCannon;\nexport default module.exports;\nexport var create = module.exports.create;`,\n  });\n});\n\nDeno.test(\"commonjs - minified __esModule\", () => {\n  runTest({\n    filename: \"foo.js\",\n    input: `\nconst m = module.exports;\nconst a = Object.defineProperty;\na(m, \"__esModule\", { value: !0 });`,\n    expected: `${INIT}\nconst m = module.exports;\nconst a = Object.defineProperty;\na(m, \"__esModule\", {\n  value: !0\n});\n${DEFAULT_EXPORT}\n${DEFAULT_EXPORT_END}\nexport var __esModule = exports.__esModule;`,\n  });\n});\n\nDeno.test(\"commonjs - esbuild __importDefault\", () => {\n  runTest({\n    input:\n      `var __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nconst node_events_1 = __importDefault(require(\"node:events\"));`,\n    expected: `import * as _mod from \"node:events\";\nvar __importDefault = this && this.__importDefault || function (mod) {\n  return mod && mod.__esModule ? mod : {\n    \"default\": mod\n  };\n};\nconst node_events_1 = __importDefault({\n  __esModule: true,\n  default: _mod.default ?? _mod\n});`,\n  });\n});\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/http_absolute.ts",
    "content": "import type {\n  NodePath,\n  PluginObj,\n  PluginPass,\n  types,\n  Visitor,\n} from \"@babel/core\";\n\nfunction maybeRewrite(\n  t: typeof types,\n  path: NodePath,\n  source: string,\n  url: URL,\n) {\n  if (source.startsWith(\"/\")) {\n    const s = `deno-http::${url.origin}${source}`;\n    path.replaceWith(t.stringLiteral(s));\n  } else if (/^https?:\\/\\//.test(source)) {\n    const s = `deno-http::${source}`;\n    path.replaceWith(t.stringLiteral(s));\n  }\n}\n\nexport function httpAbsolute(url: URL | null) {\n  return ({ types: t }: { types: typeof types }): PluginObj => {\n    const visitor: Visitor<PluginPass> = url !== null\n      ? {\n        ImportDeclaration(path) {\n          const source = path.node.source.value;\n          maybeRewrite(t, path.get(\"source\"), source, url);\n        },\n        ExportAllDeclaration(path) {\n          const source = path.node.source.value;\n          maybeRewrite(t, path.get(\"source\"), source, url);\n        },\n        ExportNamedDeclaration(path) {\n          if (!path.node.source) return;\n\n          const source = path.node.source.value;\n          // deno-lint-ignore no-explicit-any\n          maybeRewrite(t, path.get(\"source\") as any, source, url);\n        },\n        CallExpression(path) {\n          if (\n            t.isImport(path.node.callee) && path.node.arguments.length > 0 &&\n            t.isStringLiteral(path.node.arguments[0])\n          ) {\n            const source = path.node.arguments[0].value;\n            maybeRewrite(\n              t,\n              path\n                .get(\"arguments\")[0],\n              source,\n              url,\n            );\n          }\n        },\n      }\n      : {};\n\n    return {\n      name: \"fresh-http-absolute\",\n      visitor,\n    };\n  };\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/http_absolute_test.ts",
    "content": "import { expect } from \"@std/expect/expect\";\nimport * as babel from \"@babel/core\";\nimport { httpAbsolute } from \"./http_absolute.ts\";\n\nfunction runTest(options: { input: string; expected: string; url?: URL }) {\n  const res = babel.transformSync(options.input, {\n    filename: \"foo.js\",\n    babelrc: false,\n    plugins: [httpAbsolute(options.url ?? null)],\n  });\n\n  const output = res?.code ?? \"\";\n  expect(output).toEqual(options.expected);\n}\n\nDeno.test(\"http absolute - change import sources\", () => {\n  runTest({\n    input: `import foo from \"/foo.js\"`,\n    expected: `import foo from \"deno-http::http://localhost/foo.js\";`,\n    url: new URL(\"http://localhost\"),\n  });\n});\n\nDeno.test(\"http absolute - change export all sources\", () => {\n  runTest({\n    input: `export * as foo from \"/foo.js\"`,\n    expected: `export * as foo from \"deno-http::http://localhost/foo.js\";`,\n    url: new URL(\"http://localhost\"),\n  });\n});\n\nDeno.test(\"http absolute - change export sources\", () => {\n  runTest({\n    input: `export { foo } from \"/foo.js\"`,\n    expected: `export { foo } from \"deno-http::http://localhost/foo.js\";`,\n    url: new URL(\"http://localhost\"),\n  });\n});\n\nDeno.test(\"http absolute - change import()\", () => {\n  runTest({\n    input: `import(\"/foo.js\")`,\n    expected: `import(\"deno-http::http://localhost/foo.js\");`,\n    url: new URL(\"http://localhost\"),\n  });\n});\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/inline_env_vars.ts",
    "content": "import type { NodePath, PluginObj, types } from \"@babel/core\";\n\nexport function inlineEnvVarsPlugin(mode: string, env: Record<string, string>) {\n  const allowed = new Map<string, string>();\n  for (const [name, value] of Object.entries(env)) {\n    if (name.startsWith(\"FRESH_PUBLIC_\")) {\n      allowed.set(name, value);\n    }\n  }\n\n  allowed.set(\"NODE_ENV\", mode);\n\n  return (\n    { types: t }: { types: typeof types },\n  ): PluginObj => {\n    function replace(path: NodePath, name: string) {\n      if (allowed.has(name)) {\n        const value = allowed.get(name);\n\n        if (value !== undefined) {\n          path.replaceWith(t.stringLiteral(value));\n        } else {\n          path.replaceWith(t.identifier(\"undefined\"));\n        }\n      }\n    }\n\n    return {\n      name: \"fresh-env-var\",\n      visitor: {\n        MemberExpression(path) {\n          // Check: process.env.*\n          if (\n            t.isMemberExpression(path.node.object) &&\n            t.isIdentifier(path.node.object.object) &&\n            path.node.object.object.name === \"process\" &&\n            t.isIdentifier(path.node.object.property) &&\n            path.node.object.property.name === \"env\" &&\n            t.isIdentifier(path.node.property)\n          ) {\n            const name = path.node.property.name;\n            replace(path, name);\n          }\n\n          // Check: import.meta.env.*\n          if (\n            t.isIdentifier(path.node.property) &&\n            t.isMemberExpression(path.node.object) &&\n            t.isIdentifier(path.node.object.property) &&\n            path.node.object.property.name === \"env\" &&\n            t.isMetaProperty(path.node.object.object)\n          ) {\n            const name = path.node.property.name;\n            replace(path, name);\n          }\n        },\n        CallExpression(path) {\n          // Check: Deno.env.get(\"<string>\")\n          if (\n            t.isMemberExpression(path.node.callee) &&\n            t.isMemberExpression(path.node.callee.object) &&\n            t.isIdentifier(path.node.callee.object.object) &&\n            path.node.callee.object.object.name === \"Deno\" &&\n            t.isIdentifier(path.node.callee.object.property) &&\n            path.node.callee.object.property.name === \"env\" &&\n            t.isIdentifier(path.node.callee.property) &&\n            path.node.callee.property.name === \"get\" &&\n            path.node.arguments.length > 0 &&\n            t.isStringLiteral(path.node.arguments[0])\n          ) {\n            const name = path.node.arguments[0].value;\n            replace(path, name);\n          }\n        },\n      },\n    };\n  };\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/inline_env_vars_test.ts",
    "content": "import { expect } from \"@std/expect/expect\";\nimport * as babel from \"@babel/core\";\nimport { inlineEnvVarsPlugin } from \"./inline_env_vars.ts\";\n\nfunction runTest(\n  options: {\n    input: string;\n    expected: string;\n    mode?: string;\n    env?: Record<string, string>;\n  },\n) {\n  const res = babel.transformSync(options.input, {\n    filename: \"foo.js\",\n    babelrc: false,\n    plugins: [\n      inlineEnvVarsPlugin(options.mode ?? \"development\", options.env ?? {}),\n    ],\n  });\n\n  const output = res?.code ?? \"\";\n  expect(output).toEqual(options.expected);\n}\n\nDeno.test(\"env vars - inline NODE_ENV mode\", () => {\n  runTest({\n    input: `() => process.env.NODE_ENV`,\n    expected: `() => \"asdf\";`,\n    mode: \"asdf\",\n  });\n});\n\nDeno.test(\"env vars - inline custom process.env.*\", () => {\n  runTest({\n    input: `() => process.env.FRESH_PUBLIC_FOO`,\n    expected: `() => \"a\";`,\n    env: {\n      FRESH_PUBLIC_FOO: \"a\",\n    },\n  });\n});\n\nDeno.test(\"env vars - inline Deno.env.get()\", () => {\n  runTest({\n    input: `() => Deno.env.get(\"FRESH_PUBLIC_FOO\")`,\n    expected: `() => \"b\";`,\n    env: {\n      FRESH_PUBLIC_FOO: \"b\",\n    },\n  });\n});\n\nDeno.test(\"env vars - inline Deno.env.get(NODE_ENV)\", () => {\n  runTest({\n    input: `() => Deno.env.get(\"NODE_ENV\")`,\n    expected: `() => \"c\";`,\n    mode: \"c\",\n  });\n});\n\nDeno.test(\"env vars - inline const _ = Deno.env.get()\", () => {\n  runTest({\n    input: `const deno = Deno.env.get(\"FRESH_PUBLIC_FOO\");`,\n    expected: `const deno = \"test\";`,\n    env: {\n      FRESH_PUBLIC_FOO: \"test\",\n    },\n  });\n});\n\nDeno.test(\"env vars - inline import.meta.env.FRESH_PUBLIC_FOO\", () => {\n  runTest({\n    input: `() => import.meta.env.FRESH_PUBLIC_FOO;`,\n    expected: `() => \"test\";`,\n    env: {\n      FRESH_PUBLIC_FOO: \"test\",\n    },\n  });\n});\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/jsx_comment.ts",
    "content": "import type { PluginObj, types } from \"@babel/core\";\n\nexport function jsxComments(): PluginObj {\n  return {\n    name: \"fresh-jsx-comment\",\n    visitor: {\n      Program(path) {\n        const comments = (path.parent as types.File).comments;\n        if (comments) {\n          for (let i = 0; i < comments.length; i++) {\n            const comment = comments[i];\n\n            comment.value = comment.value.replaceAll(/@jsx\\w+\\s+[^\\s*]+/g, \"\");\n          }\n        }\n      },\n    },\n  };\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/jsx_comment_test.ts",
    "content": "import { expect } from \"@std/expect/expect\";\nimport * as babel from \"@babel/core\";\nimport { jsxComments } from \"./jsx_comment.ts\";\n\nfunction runTest(options: { input: string; expected: string }) {\n  const res = babel.transformSync(options.input, {\n    filename: \"foo.js\",\n    babelrc: false,\n    plugins: [jsxComments],\n  });\n\n  const output = res?.code ?? \"\";\n  expect(output).toEqual(options.expected);\n}\n\nDeno.test(\"jsx comments - import declaration\", () => {\n  runTest({\n    input:\n      `/** @jsxRuntime classic */ /** @jsxImportSource npm:preact@^10.27.0 */ /** @jsxImportSourceTypes npm:preact@^10.27.0 */ /** @jsxFactory React.createElement */ /** @jsxFragmentFactory React.Fragment */ foo;`,\n    expected: `/**  */ /**  */ /**  */ /**  */ /**  */foo;`,\n  });\n});\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/remove_polyfills.ts",
    "content": "import type { PluginObj, types } from \"@babel/core\";\n\nexport function removePolyfills(\n  { types: t }: { types: typeof types },\n): PluginObj {\n  return {\n    name: \"fresh-remove-polyfills\",\n    visitor: {\n      IfStatement(path) {\n        if (\n          t.isUnaryExpression(path.node.test) &&\n          path.node.test.operator === \"!\" &&\n          t.isMemberExpression(path.node.test.argument) &&\n          t.isIdentifier(path.node.test.argument.object) &&\n          ((path.node.test.argument.object.name === \"String\" &&\n            t.isIdentifier(path.node.test.argument.property) &&\n            path.node.test.argument.property.name === \"fromCodePoint\") ||\n            (path.node.test.argument.object.name === \"Object\" &&\n              t.isIdentifier(path.node.test.argument.property) &&\n              (path.node.test.argument.property.name === \"keys\" ||\n                path.node.test.argument.property.name === \"create\")))\n        ) {\n          if (path.node.alternate) {\n            path.replaceWith(t.cloneNode(path.node.alternate, true));\n          } else {\n            path.remove();\n          }\n        }\n      },\n    },\n  };\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches/remove_polyfills_test.ts",
    "content": "import { expect } from \"@std/expect/expect\";\nimport * as babel from \"@babel/core\";\nimport { removePolyfills } from \"./remove_polyfills.ts\";\n\nfunction runTest(options: { input: string; expected: string }) {\n  const res = babel.transformSync(options.input, {\n    filename: \"foo.js\",\n    babelrc: false,\n    plugins: [removePolyfills],\n  });\n\n  const output = res?.code ?? \"\";\n  expect(output).toEqual(options.expected);\n}\n\nDeno.test(\"remove polyfills - Object.keys\", () => {\n  runTest({\n    input: `if (!Object.keys) {\n    Object.keys = function (o) {\n    var a = []\n    for (var i in o) if (o.hasOwnProperty(i)) a.push(i)\n    return a\n  }\n}\n\nObject.keys({ foo: \"bar\" });`,\n    expected: `Object.keys({\n  foo: \"bar\"\n});`,\n  });\n});\n\nDeno.test(\"remove polyfills - Object.create\", () => {\n  runTest({\n    input: `if (!Object.create) {\n  Object.create = function (o) {\n    function F () {}\n    F.prototype = o\n    var newf = new F()\n    return newf\n  }\n}\n\nObject.create({});`,\n    expected: `Object.create({});`,\n  });\n});\n\nDeno.test(\"remove polyfills - keep alternate\", () => {\n  runTest({\n    input: `if (!Object.create) {\n  Object.create = function (o) {\n    function F () {}\n    F.prototype = o\n    var newf = new F()\n    return newf\n  }\n} else {\n  console.log(\"foo\")\n}`,\n    expected: `{\n  console.log(\"foo\");\n}`,\n  });\n});\n\nDeno.test(\"remove polyfills - String.fromCodePoint\", () => {\n  runTest({\n    input: `if (!String.fromCodePoint) {\n  foo\n}\n  \nString.fromCodePoint(42);`,\n    expected: `String.fromCodePoint(42);`,\n  });\n});\n\nDeno.test(\"remove polyfills - Keep unrelated unary statements\", () => {\n  runTest({\n    input: `if (+String.fromCodePoint) {\n  foo;\n}`,\n    expected: `if (+String.fromCodePoint) {\n  foo;\n}`,\n  });\n});\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/patches.ts",
    "content": "import type { Plugin } from \"vite\";\nimport * as babel from \"@babel/core\";\nimport { cjsPlugin } from \"./patches/commonjs.ts\";\nimport { jsxComments } from \"./patches/jsx_comment.ts\";\nimport { inlineEnvVarsPlugin } from \"./patches/inline_env_vars.ts\";\nimport { removePolyfills } from \"./patches/remove_polyfills.ts\";\nimport { JS_REG, JSX_REG } from \"../utils.ts\";\nimport { codeEvalPlugin } from \"./patches/code_eval.ts\";\n\n// @ts-ignore Workaround for https://github.com/denoland/deno/issues/30850\nconst { default: babelReact } = await import(\"@babel/preset-react\");\n\nexport function patches(): Plugin {\n  let isDev = false;\n\n  return {\n    name: \"fresh:patches\",\n    sharedDuringBuild: true,\n    config(_, env) {\n      isDev = env.command === \"serve\";\n    },\n    applyToEnvironment() {\n      return true;\n    },\n    transform: {\n      filter: {\n        id: JS_REG,\n      },\n      handler(code, id) {\n        const presets = [];\n        if (this.environment.config.consumer === \"client\" && JSX_REG.test(id)) {\n          presets.push([babelReact, {\n            runtime: \"automatic\",\n            importSource: \"preact\",\n            development: isDev,\n            throwIfNamespace: false,\n          }]);\n        }\n\n        const env = isDev ? \"development\" : \"production\";\n\n        const plugins: babel.PluginItem[] = [\n          codeEvalPlugin(this.environment.config.consumer, env),\n          cjsPlugin,\n          removePolyfills,\n          jsxComments,\n          inlineEnvVarsPlugin(env, Deno.env.toObject()),\n        ];\n\n        const res = babel.transformSync(code, {\n          filename: id,\n          babelrc: false,\n          compact: false,\n          plugins,\n          presets,\n          sourceMaps: \"both\",\n        });\n\n        if (res?.code) {\n          return {\n            code: res.code,\n            map: res.map,\n          };\n        }\n      },\n    },\n  };\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/server_entry.ts",
    "content": "import type { Manifest, Plugin } from \"vite\";\nimport {\n  generateServerEntry,\n  type PendingStaticFile,\n  prepareStaticFile,\n  writeCompiledEntry,\n} from \"fresh/internal-dev\";\nimport { pathWithRoot, type ResolvedFreshViteConfig } from \"../utils.ts\";\nimport * as path from \"@std/path\";\n\nexport function serverEntryPlugin(\n  options: ResolvedFreshViteConfig,\n): Plugin {\n  const modName = \"fresh:server_entry\";\n\n  let serverEntry = \"\";\n  let serverOutDir = \"\";\n  let clientOutDir = \"\";\n  let root = \"\";\n  let basePath = \"\";\n\n  let isDev = false;\n\n  const getAssetPath = (id: string): string => {\n    if (basePath === \"/\") {\n      return `/${id}`;\n    }\n    // Ensure basePath ends with / and construct the path manually to avoid platform-specific path issues\n    const normalizedBase = basePath.endsWith(\"/\") ? basePath : basePath + \"/\";\n    return normalizedBase + id;\n  };\n\n  return {\n    name: \"fresh:server_entry\",\n    sharedDuringBuild: true,\n    applyToEnvironment(env) {\n      return env.config.consumer === \"server\";\n    },\n    config(_, env) {\n      isDev = env.command === \"serve\";\n    },\n    configResolved(config) {\n      root = config.root;\n      basePath = config.base || \"/\";\n      if (basePath !== \"/\" && !basePath.endsWith(\"/\")) {\n        basePath += \"/\";\n      }\n      serverEntry = pathWithRoot(options.serverEntry, config.root);\n      serverOutDir = pathWithRoot(\n        config.environments.ssr.build.outDir,\n        config.root,\n      );\n      clientOutDir = pathWithRoot(\n        config.environments.client.build.outDir,\n        config.root,\n      );\n    },\n    resolveId: {\n      filter: {\n        id: /fresh:server_entry/,\n      },\n      handler(id) {\n        if (id === modName) {\n          return `\\0${modName}`;\n        }\n      },\n    },\n    load: {\n      filter: {\n        id: /\\0fresh:server_entry/,\n      },\n      handler() {\n        let code = generateServerEntry({\n          root: isDev ? path.relative(serverOutDir, root) : \"..\",\n          serverEntry: path.toFileUrl(serverEntry).href,\n          snapshotSpecifier: \"fresh:server-snapshot\",\n        });\n\n        code += `\n\nexport function registerStaticFile(prepared) {\n  snapshot.staticFiles.set(prepared.name, {\n    name: prepared.name,\n    contentType: prepared.contentType,\n    filePath: prepared.filePath\n  });\n}\n`;\n\n        if (isDev) {\n          code = `import \"preact/debug\";\nimport { setErrorInterceptor as internalErrorIntercept } from \"fresh/internal\";\n${code}\n\nexport function setErrorInterceptor(fn) {\n  internalErrorIntercept(app, fn);\n}\nif (import.meta.hot) import.meta.hot.accept();`;\n        }\n\n        return code;\n      },\n    },\n    async writeBundle(_options, bundle) {\n      const manifest = bundle[\".vite/manifest.json\"];\n\n      const staticFiles: PendingStaticFile[] = [];\n      if (\n        manifest && manifest.type === \"asset\" &&\n        typeof manifest.source === \"string\"\n      ) {\n        const json = JSON.parse(manifest.source) as Manifest;\n\n        for (const item of Object.values(json)) {\n          if (item.assets) {\n            for (let i = 0; i < item.assets.length; i++) {\n              const id = item.assets[i];\n\n              staticFiles.push({\n                filePath: path.join(serverOutDir, id),\n                hash: null,\n                pathname: getAssetPath(id),\n              });\n            }\n          }\n\n          if (item.css) {\n            for (let i = 0; i < item.css.length; i++) {\n              const id = item.css[i];\n\n              staticFiles.push({\n                filePath: path.join(serverOutDir, id),\n                hash: null,\n                pathname: getAssetPath(id),\n              });\n            }\n          }\n        }\n      }\n\n      const registered = await Promise.all(staticFiles.map(async (file) => {\n        const prepared = await prepareStaticFile(file, serverOutDir);\n        const rel = path.relative(serverOutDir, file.filePath);\n        const target = path.join(clientOutDir, rel);\n        await Deno.rename(file.filePath, target);\n\n        prepared.filePath = path.join(\"client\", prepared.filePath);\n\n        return `registerStaticFile(${JSON.stringify(prepared)});`;\n      }));\n\n      const outDir = path.dirname(serverOutDir);\n      await Deno.writeTextFile(\n        path.join(outDir, \"server.js\"),\n        `import server, { registerStaticFile } from \"./server/server-entry.mjs\";\n\n${registered.join(\"\\n\")}\n\nexport default {\n  fetch: server.fetch\n};\n`,\n      );\n\n      await writeCompiledEntry(outDir);\n    },\n  };\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/server_snapshot.ts",
    "content": "import type {\n  EnvironmentModuleNode,\n  Manifest,\n  Plugin,\n  ViteDevServer,\n} from \"vite\";\nimport {\n  crawlFsItem,\n  fsAdapter,\n  type FsRouteFileNoMod,\n  generateSnapshotServer,\n  type IslandModChunk,\n  pathToSpec,\n  type PendingStaticFile,\n  specToName,\n  UniqueNamer,\n} from \"fresh/internal-dev\";\nimport {\n  JS_REG,\n  pathWithRoot,\n  type ResolvedFreshViteConfig,\n} from \"../utils.ts\";\nimport * as path from \"@std/path\";\nimport { getBuildId } from \"./build_id.ts\";\n\nexport function serverSnapshot(options: ResolvedFreshViteConfig): Plugin[] {\n  const modName = \"fresh:server-snapshot\";\n\n  let isDev = false;\n  let server: ViteDevServer | undefined;\n\n  let clientOutDir = \"\";\n  let serverOutDir = \"\";\n  let root = \"\";\n  let publicDir = \"\";\n\n  const islands = new Map<string, { name: string; chunk: string | null }>();\n  const islandsByFile = new Set<string>();\n  const islandSpecByName = new Map<string, string>();\n  const routeNamer = new UniqueNamer();\n  const routeFileToName = new Map<string, string>();\n\n  // deno-lint-ignore no-explicit-any\n  const routes: Map<string, FsRouteFileNoMod<any>> = new Map();\n\n  return [\n    {\n      name: \"fresh:server-snapshot\",\n      sharedDuringBuild: true,\n      applyToEnvironment(env) {\n        return env.config.consumer === \"server\";\n      },\n      config(_, env) {\n        isDev = env.command === \"serve\";\n      },\n      configResolved(config) {\n        root = config.root;\n\n        publicDir = pathWithRoot(config.publicDir, config.root);\n        clientOutDir = pathWithRoot(\n          config.environments.client.build.outDir,\n          config.root,\n        );\n        serverOutDir = pathWithRoot(\n          config.environments.ssr.build.outDir,\n          config.root,\n        );\n\n        options.islandSpecifiers.forEach((name, spec) => {\n          islands.set(spec, { name, chunk: null });\n          islandSpecByName.set(name, spec);\n          // islandsByFile.add(spec);\n        });\n      },\n      configureServer(viteServer) {\n        server = viteServer;\n\n        viteServer.watcher.on(\"all\", (ev, filePath) => {\n          const { client, ssr } = viteServer.environments;\n\n          // We can't just check if it's in the client module graph\n          // because plugins like tailwindcss import _everything_.\n          // Instead, we walk up the module graph to see if the file\n          // is imported by an island or the client entry file.\n          const mods = client.moduleGraph.getModulesByFile(filePath);\n          if (mods !== undefined) {\n            const seen = new Set<EnvironmentModuleNode>();\n            const maybe = Array.from(mods);\n            for (let i = 0; i < maybe.length; i++) {\n              const mod = maybe[i];\n\n              const isIslandFile = walkUp(\n                mod,\n                (m) => m.file !== null && islandsByFile.has(m.file),\n                seen,\n              );\n\n              // No need to notify manually, vite takes care of this.\n              if (isIslandFile) return;\n            }\n          }\n\n          // Check for route files. We need to invalidate the snapshot if\n          // they are removed or added.\n          if (\n            (ev === \"add\" || ev === \"unlink\") &&\n            !/[\\\\/]+\\(_[^)]+\\)[\\\\/]+/.test(filePath)\n          ) {\n            const relRoutes = path.relative(options.routeDir, filePath);\n            if (!relRoutes.startsWith(\"..\")) {\n              const mod = ssr.moduleGraph.getModuleById(`\\0${modName}`);\n              if (mod !== undefined) {\n                // Clear state\n                islands.clear();\n                islandsByFile.clear();\n                islandSpecByName.clear();\n\n                ssr.moduleGraph.invalidateModule(mod);\n              }\n            }\n          }\n\n          // Finally, notify the client\n          viteServer.ws.send(\"fresh:reload\");\n        });\n      },\n      resolveId: {\n        filter: {\n          id: /fresh:server-snapshot/,\n        },\n        handler(id) {\n          if (id === modName) {\n            return `\\0${modName}`;\n          }\n        },\n      },\n      load: {\n        filter: {\n          id: /\\0fresh:server-snapshot/,\n        },\n\n        async handler() {\n          const result = await crawlFsItem({\n            islandDir: options.islandsDir,\n            routeDir: options.routeDir,\n            ignore: options.ignore,\n          });\n\n          for (let i = 0; i < result.islands.length; i++) {\n            const spec = result.islands[i];\n            const specName = specToName(spec);\n            const name = options.namer.getUniqueName(specName);\n\n            islands.set(spec, { name, chunk: null });\n            islandSpecByName.set(name, spec);\n            islandsByFile.add(spec);\n          }\n\n          for (let i = 0; i < result.routes.length; i++) {\n            const route = result.routes[i];\n            const name = routeNamer.getUniqueName(route.id);\n\n            routeFileToName.set(route.filePath, name);\n            routes.set(name, route);\n          }\n\n          const staticFiles: PendingStaticFile[] = [];\n          let islandMods: IslandModChunk[] = [];\n          let clientEntry = \"/@id/fresh:client-entry\";\n          let buildId = \"\";\n          const entryAssets: string[] = [];\n\n          if (isDev && server !== undefined) {\n            for (const id of islands.keys()) {\n              const mod = server.environments.client.moduleGraph.getModuleById(\n                id,\n              );\n              if (mod !== undefined) {\n                const def = islands.get(id);\n                if (def !== undefined) def.chunk = mod.url;\n              }\n            }\n\n            islandMods = Array.from(islands.entries()).map(([id, def]) => {\n              return {\n                name: def.name,\n                server: id,\n                browser: def.chunk ?? `/@id/fresh-island::${def.name}`,\n                css: [],\n              };\n            });\n          } else {\n            buildId = await getBuildId(false);\n            const manifest = JSON.parse(\n              await Deno.readTextFile(\n                path.join(clientOutDir, \".vite\", \"manifest.json\"),\n              ),\n            ) as Manifest;\n            const resolvedIslandSpecs = new Map<string, string>();\n\n            for (const spec of options.islandSpecifiers.keys()) {\n              const resolved = await this.resolve(spec);\n\n              if (resolved === null) continue;\n\n              const id = resolved.id.startsWith(\"\\0\")\n                ? resolved.id.slice(1)\n                : resolved.id;\n\n              resolvedIslandSpecs.set(id, spec);\n            }\n\n            const clientEntryName = \"client-entry\";\n\n            for (const chunk of Object.values(manifest)) {\n              if (chunk.name === clientEntryName) {\n                clientEntry = pathToSpec(clientOutDir, chunk.file);\n              }\n\n              staticFiles.push({\n                filePath: path.join(clientOutDir, chunk.file),\n                pathname: chunk.file,\n                hash: null,\n              });\n\n              if (chunk.css !== undefined) {\n                for (let i = 0; i < chunk.css.length; i++) {\n                  const id = chunk.css[i];\n\n                  const pathname = `/${id}`;\n\n                  staticFiles.push({\n                    filePath: path.join(clientOutDir, id),\n                    hash: null,\n                    pathname,\n                  });\n\n                  if (chunk.name === clientEntryName) {\n                    entryAssets.push(pathname);\n                  }\n                }\n              }\n\n              if (chunk.name?.startsWith(\"fresh-island__\")) {\n                const name = chunk.name.slice(\"fresh-island__\".length);\n                let serverPath = path.join(root, chunk.src ?? chunk.file);\n                const idx = chunk.src?.indexOf(\"deno::\") ?? -1;\n\n                if (idx > -1 && chunk.src) {\n                  const src = chunk.src\n                    .slice(idx)\n                    .replace(\n                      /(https?):\\/([^/])/,\n                      (_m, protocol, rest) => {\n                        return `${protocol}://${rest}`;\n                      },\n                    );\n                  serverPath = resolvedIslandSpecs.get(src)!;\n                }\n\n                let spec = pathToSpec(clientOutDir, chunk.file);\n\n                if (spec.startsWith(\"./\")) {\n                  spec = spec.slice(1);\n                }\n\n                const chunkCss = chunk.css?.map((id) => `/${id}`) ?? [];\n                islandMods.push({\n                  name,\n                  browser: spec,\n                  server: serverPath,\n                  css: chunkCss,\n                });\n              }\n            }\n\n            if (await fsAdapter.isDirectory(publicDir)) {\n              const entries = await fsAdapter.walk(\n                publicDir,\n                {\n                  followSymlinks: false,\n                  includeDirs: false,\n                  includeFiles: true,\n                  skip: options.ignore,\n                },\n              );\n\n              for await (const entry of entries) {\n                const relative = path.relative(publicDir, entry.path);\n                const filePath = path.join(clientOutDir, relative);\n\n                try {\n                  await Deno.mkdir(path.dirname(filePath), { recursive: true });\n                } catch (err) {\n                  if (!(err instanceof Deno.errors.AlreadyExists)) {\n                    throw err;\n                  }\n                }\n                await Deno.copyFile(entry.path, filePath);\n\n                staticFiles.push({\n                  filePath,\n                  hash: null,\n                  pathname: relative,\n                });\n\n                if (path.basename(relative) === \"index.html\") {\n                  const htmlRelative = path.relative(\n                    publicDir,\n                    path.dirname(entry.path),\n                  );\n\n                  staticFiles.push({\n                    filePath,\n                    hash: null,\n                    pathname: htmlRelative,\n                  });\n                }\n              }\n            }\n          }\n\n          const code = await generateSnapshotServer({\n            outDir: path.join(clientOutDir, \"..\"),\n            staticFiles,\n            buildId,\n            clientEntry,\n            entryAssets,\n            fsRoutesFiles: result.routes,\n            islands: islandMods,\n            writeSpecifier: (file) => {\n              const def = islands.get(file);\n              if (def) {\n                return `fresh-island::${def.name}`;\n              }\n              const routeDef = routeFileToName.get(file);\n              if (routeDef !== undefined) {\n                return `fresh-route::${routeDef}`;\n              }\n              return path.toFileUrl(file).href;\n            },\n          });\n\n          return code;\n        },\n      },\n      transform: {\n        filter: {\n          id: /\\.(css|less|sass|scss)(\\?.*)?$/,\n        },\n        handler(_code, id) {\n          if (server) {\n            const ssrGraph = server.environments.ssr.moduleGraph;\n            const mod = ssrGraph.getModuleById(id);\n            if (mod === undefined) return;\n\n            const snapshot = ssrGraph.getModuleById(\"\\0fresh:server-snapshot\");\n            if (snapshot === undefined) return;\n\n            const queue: EnvironmentModuleNode[] = [mod];\n            let item: EnvironmentModuleNode | undefined;\n            while ((item = queue.pop()) !== undefined) {\n              if (item.file !== null) {\n                const normalized = path.normalize(item.file);\n                const name = routeFileToName.get(normalized);\n\n                if (name !== undefined) {\n                  const route = routes.get(name);\n                  if (route !== undefined) {\n                    const mod = ssrGraph.getModuleById(id);\n                    if (mod !== undefined) {\n                      route.css.push(mod.url);\n                    }\n\n                    const routeMod = ssrGraph.getModuleById(\n                      `\\0fresh-route-css::${name}`,\n                    );\n                    if (routeMod !== undefined) {\n                      ssrGraph.invalidateModule(routeMod);\n                    }\n                  }\n                }\n              }\n\n              item.importers.forEach((importer) => queue.push(importer));\n            }\n          }\n        },\n      },\n    },\n    {\n      name: \"fresh:island-resolver\",\n      sharedDuringBuild: true,\n      resolveId: {\n        filter: {\n          id: /^fresh-island::.*/,\n        },\n        handler(id) {\n          let name = id.slice(\"fresh-island::\".length);\n\n          if (JS_REG.test(name)) {\n            name = name.slice(0, name.lastIndexOf(\".\"));\n          }\n\n          const spec = islandSpecByName.get(name);\n          if (spec !== undefined) return spec;\n        },\n      },\n    },\n    {\n      name: \"fresh:route-css\",\n      sharedDuringBuild: true,\n      resolveId: {\n        filter: {\n          id: /^(\\/@id\\/)?fresh-route-css::/,\n        },\n        handler(id) {\n          let name = id.startsWith(\"/@id/\")\n            ? id.slice(\"/@id/fresh-route-css::\".length)\n            : id.slice(\"fresh-route-css::\".length);\n\n          const idx = name.indexOf(\".module.\");\n          if (idx > -1) {\n            name = name.slice(0, idx);\n          }\n\n          return `\\0fresh-route-css::${name}`;\n        },\n      },\n      load: {\n        filter: {\n          id: /^\\0fresh-route-css::/,\n        },\n        handler(id) {\n          const name = id.slice(\"\\0fresh-route-css::\".length);\n\n          const route = routes.get(name);\n          if (route === undefined) return;\n\n          if (!isDev) {\n            return `export default [\"__FRESH_CSS_PLACEHOLDER__\"];`;\n          }\n\n          const imports = route.css.map((css) => `import \"${css}\";`).join(\"\\n\");\n          return `${imports}\nexport default ${JSON.stringify(route.css)}\n`;\n        },\n      },\n    },\n    {\n      name: \"fresh-route-css-build-ssr\",\n      sharedDuringBuild: true,\n      applyToEnvironment(env) {\n        return env.config.consumer === \"server\";\n      },\n      async writeBundle(_, bundle) {\n        const asset = bundle[\".vite/manifest.json\"];\n        if (asset.type === \"asset\") {\n          const manifest = JSON.parse(asset.source as string) as Manifest;\n\n          for (const info of Object.values(manifest)) {\n            if (info.name?.startsWith(\"_fresh-route___\")) {\n              const filePath = path.join(serverOutDir, info.file);\n              const content = await Deno.readTextFile(filePath);\n\n              const replaced = content.replace(\n                `[\"__FRESH_CSS_PLACEHOLDER__\"]`,\n                info.css\n                  ? JSON.stringify(info.css.map((css) => `/${css}`))\n                  : \"null\",\n              );\n\n              await Deno.writeTextFile(filePath, replaced);\n            }\n          }\n        }\n      },\n    },\n    {\n      name: \"fresh:route-resolver\",\n      sharedDuringBuild: true,\n      resolveId: {\n        filter: {\n          id: /^fresh-route::/,\n        },\n        handler(id) {\n          let name = id.slice(\"fresh-route::\".length);\n\n          if (JS_REG.test(name)) {\n            name = name.slice(0, name.lastIndexOf(\".\"));\n          }\n\n          return `\\0fresh-route::${name}`;\n        },\n      },\n      load: {\n        filter: {\n          id: /^\\0fresh-route::.*/,\n        },\n        handler(id) {\n          const name = id.slice(\"\\0fresh-route::\".length);\n\n          const route = routes.get(name);\n          if (route === undefined) return;\n\n          const fileUrl = path.toFileUrl(route.filePath).href;\n          const cssId = isDev\n            ? `/@id/fresh-route-css::${name}.module.css`\n            : `fresh-route-css::${name}.module.css`;\n\n          // For some reason doing `export * from \"foo\"` is broken\n          // in vite.\n          const code = `import * as mod from \"${fileUrl}\";\nimport routeCss from \"${cssId}\";\nexport const css = routeCss;\nexport const config = mod.config;\nexport const handler = mod.handler;\nexport const handlers = mod.handlers;\nexport default mod.default;\n`;\n\n          return { code };\n        },\n      },\n    },\n  ];\n}\n\nfunction walkUp(\n  mod: EnvironmentModuleNode,\n  fn: (mod: EnvironmentModuleNode) => boolean,\n  seen: Set<EnvironmentModuleNode>,\n): boolean {\n  if (seen.has(mod)) return false;\n\n  if (fn(mod)) return true;\n\n  const importers = Array.from(mod.importers);\n  for (let i = 0; i < importers.length; i++) {\n    const imp = importers[i];\n    if (walkUp(imp, fn, seen)) return true;\n  }\n\n  return false;\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/shims/object.entries/index.ts",
    "content": "// deno-lint-ignore no-explicit-any\nexport default function entries(obj: any) {\n  return Object.entries(obj);\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/shims/supports-color/index.ts",
    "content": "export interface ColorSupport {\n  level: 1 | 2 | 3;\n  hasBasic: boolean;\n  has256: boolean;\n  has16m: boolean;\n}\n\nfunction toLevel(depth: number): 1 | 2 | 3 {\n  if (depth === 1) return 1;\n  if (depth === 8) return 2;\n  if (depth === 24) return 3;\n  return 1;\n}\n\nfunction toSupport(depth: number): ColorSupport {\n  const level = toLevel(depth);\n  return {\n    level,\n    hasBasic: true,\n    has256: level >= 2,\n    has16m: level >= 3,\n  };\n}\n\nexport default {\n  // deno-lint-ignore no-process-global\n  stdout: toSupport(process.stdout.getColorDepth()),\n  // deno-lint-ignore no-process-global\n  stderr: toSupport(process.stderr.getColorDepth()),\n};\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/shims.ts",
    "content": "import type { Plugin } from \"vite\";\nimport * as path from \"@std/path\";\n\nconst SHIMS: Record<string, string> = {\n  \"object.entries\": path.join(\n    import.meta.dirname!,\n    \"shims\",\n    \"object.entries\",\n    \"index.ts\",\n  ),\n  \"supports-color\": path.join(\n    import.meta.dirname!,\n    \"shims\",\n    \"supports-color\",\n    \"index.ts\",\n  ),\n};\n\nexport function shims(): Plugin {\n  return {\n    name: \"fresh:shims\",\n    sharedDuringBuild: true,\n    resolveId: {\n      filter: {\n        id: /(object\\.entries|supports-color)/,\n      },\n      handler(id) {\n        const resolved = SHIMS[id];\n        if (resolved !== undefined) {\n          return resolved;\n        }\n      },\n    },\n  };\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/plugins/verify_imports.ts",
    "content": "import type { Plugin } from \"vite\";\nimport * as cl from \"@std/fmt/colors\";\nimport type { PluginContext } from \"rollup\";\nimport path from \"node:path\";\nimport { pathWithRoot } from \"../utils.ts\";\n\n/** A diagnostic message for an invalid import */\nexport interface ImportCheckDiagnostic {\n  type: \"warn\" | \"error\";\n  message: string;\n  description?: string;\n  hint?: string;\n}\n\n/** A check whether or not an import is valid or not for an environment */\nexport type ImportCheck = (\n  id: string,\n  env: string,\n) => ImportCheckDiagnostic | void;\n\nexport interface CheckImportOptions {\n  checks: ImportCheck[];\n}\n\nexport function checkImports(pluginOptions: CheckImportOptions): Plugin {\n  function check(\n    options: CheckImportOptions,\n    id: string,\n    env: \"server\" | \"client\",\n  ): ImportCheckDiagnostic | undefined {\n    for (let i = 0; i < options.checks.length; i++) {\n      const check = options.checks[i];\n\n      const result = check(id, env);\n      if (result) return result;\n    }\n  }\n\n  let root = \"\";\n  let isDev = false;\n\n  const seen = new Set<string>();\n\n  return {\n    name: \"fresh:check-imports\",\n    sharedDuringBuild: true,\n    enforce: \"pre\",\n    applyToEnvironment() {\n      return true;\n    },\n    config(_, env) {\n      isDev = env.command === \"serve\";\n    },\n    configResolved(config) {\n      root = pathWithRoot(config.root);\n    },\n    resolveId: {\n      filter: {\n        id: [\n          /^(?!\\0|[\\\\/]@fs[\\\\/]|fresh-island::|fresh:)/,\n          /[\\\\/]node_modules[\\\\/]/,\n        ],\n      },\n      async handler(id, importer) {\n        if (\n          importer &&\n          (importer.startsWith(\"\\0\") || importer.includes(\"node_modules\") ||\n            importer.includes(\"deno::\"))\n        ) {\n          return;\n        }\n\n        let result: ImportCheckDiagnostic | undefined;\n        if (id.startsWith(\".\")) {\n          const resolved = await this.resolve(id, importer);\n\n          if (resolved !== null) {\n            const key =\n              `${this.environment.config.consumer}::${resolved.id}::${importer}`;\n            if (!seen.has(key)) {\n              result = check(\n                pluginOptions,\n                resolved.id,\n                this.environment.config.consumer,\n              );\n            }\n\n            seen.add(key);\n          }\n        } else {\n          const key = `${this.environment.config.consumer}::${id}::${importer}`;\n          if (!seen.has(key)) {\n            result = check(pluginOptions, id, this.environment.config.consumer);\n          }\n          seen.add(key);\n        }\n\n        if (result) {\n          const label = result.type === \"warn\"\n            ? cl.inverse(cl.yellow(` WARN `))\n            : cl.inverse(cl.red(` ERROR `));\n\n          // deno-lint-ignore no-console\n          console.log();\n          // deno-lint-ignore no-console\n          console.log();\n          // deno-lint-ignore no-console\n          console.log(`${label} ${result.message}`);\n          // deno-lint-ignore no-console\n          console.log();\n\n          if (importer !== undefined) {\n            const ancestors = findAncestors(this, importer, isDev);\n\n            if (ancestors && ancestors.length > 0) {\n              // deno-lint-ignore no-console\n              console.log(\n                `The specifier ${cl.cyan(`\"${id}\"`)} was imported in:`,\n              );\n              ancestors.forEach((spec) => {\n                if (path.isAbsolute(spec)) {\n                  spec = path.relative(root, spec);\n                }\n                // deno-lint-ignore no-console\n                console.log(` - ${cl.cyan(spec)}`);\n              });\n\n              // deno-lint-ignore no-console\n              console.log();\n            }\n          }\n\n          if (result.hint) {\n            // deno-lint-ignore no-console\n            console.log(cl.bold(` hint: `) + result.hint);\n            // deno-lint-ignore no-console\n            console.log();\n          }\n\n          if (result.type === \"error\") {\n            this.error({\n              message: result.message,\n              id: importer,\n            });\n          } else {\n            this.warn({\n              message: result.message,\n              id: importer,\n            });\n          }\n        }\n      },\n    },\n  };\n}\n\nfunction findAncestors(\n  ctx: PluginContext,\n  id: string,\n  isDev: boolean,\n): string[] | null {\n  const mod = ctx.getModuleInfo(id);\n\n  if (mod === null) return null;\n\n  if (isDev || mod.importers.length === 0) {\n    return [id];\n  }\n\n  for (let i = 0; i < mod.importers.length; i++) {\n    const importer = mod.importers[i];\n\n    const result = findAncestors(ctx, importer, isDev);\n    if (result !== null) {\n      result.push(id);\n      return result;\n    }\n  }\n\n  return null;\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/shared.ts",
    "content": "export function hashCode(moduleId: string) {\n  let hash = 0,\n    i,\n    chr;\n  if (moduleId.length === 0) return hash;\n  for (i = 0; i < moduleId.length; i++) {\n    chr = moduleId.charCodeAt(i);\n    hash = (hash << 5) - hash + chr;\n    hash |= 0; // Convert to 32bit integer\n  }\n  return hash;\n}\n"
  },
  {
    "path": "packages/plugin-vite/src/utils.ts",
    "content": "import * as path from \"@std/path\";\nimport type { FsRouteFileNoMod, UniqueNamer } from \"fresh/internal-dev\";\nimport type { ImportCheck } from \"./plugins/verify_imports.ts\";\n\nexport const JS_REG = /\\.([tj]sx?|[mc]?[tj]s)(\\?.*)?$/;\nexport const JSX_REG = /\\.[tj]sx(\\?.*)?$/;\n\nexport function pathWithRoot(fileOrDir: string, root?: string): string {\n  if (path.isAbsolute(fileOrDir)) return fileOrDir;\n\n  if (root === undefined) {\n    return path.join(Deno.cwd(), fileOrDir);\n  }\n\n  if (path.isAbsolute(root)) return path.join(root, fileOrDir);\n\n  return path.join(Deno.cwd(), root, fileOrDir);\n}\n\nexport interface FreshState {\n  namer: UniqueNamer;\n  root: string;\n  serverEntry: string;\n  islandDir: string;\n  routeDir: string;\n  dev: boolean;\n  islands: Map<string, { name: string; chunk: string | null }>;\n  // deno-lint-ignore no-explicit-any\n  routes: FsRouteFileNoMod<any>[];\n  clientOutDir: string;\n  serverOutDir: string;\n}\n\nexport interface ClientSnapshot {\n  entry: string;\n}\n\n/** Configuration options for Fresh when using the Vite plugin */\nexport interface FreshViteConfig {\n  /** Path to main server entry file. Default: `main.ts` */\n  serverEntry?: string;\n  /** Path to main client entry file. Default: `client.ts` */\n  clientEntry?: string;\n  /** Path to islands directory. Default: `./islands` */\n  islandsDir?: string;\n  /** Path to routes directory. Default: `./routes` */\n  routeDir?: string;\n  /**\n   * Ignore file paths matching any of the provided regexes when\n   * crawling the islands and routes directories.\n   */\n  ignore?: RegExp[];\n  /**\n   * Treat these specifiers as island files. This is used to declare\n   * islands from remote packages.\n   */\n  islandSpecifiers?: string[];\n  /**\n   * A list of checks that will be performed for imports.\n   *\n   * Can be used to warn or error when certain imports exist in\n   * server or client code. Useful to enforce that some dependencies\n   * are not imported in Islands running in the browser.\n   */\n  checkImports?: ImportCheck[];\n}\n\nexport type ResolvedFreshViteConfig =\n  & Required<\n    Omit<FreshViteConfig, \"islandSpecifiers\">\n  >\n  & { islandSpecifiers: Map<string, string>; namer: UniqueNamer };\n"
  },
  {
    "path": "packages/plugin-vite/tests/build_test.ts",
    "content": "import { expect } from \"@std/expect\";\nimport {\n  waitFor,\n  waitForText,\n  withBrowser,\n} from \"../../fresh/tests/test_utils.tsx\";\nimport {\n  buildVite,\n  DEMO_DIR,\n  FIXTURE_DIR,\n  launchProd,\n  usingEnv,\n} from \"./test_utils.ts\";\nimport * as path from \"@std/path\";\n\nconst viteResult = await buildVite(DEMO_DIR);\n\nDeno.test({\n  name: \"vite build - launches\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        const res = await fetch(address);\n        const text = await res.text();\n        expect(text).toEqual(\"it works\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - creates compiled entry\",\n  fn: async () => {\n    const stat = await Deno.stat(\n      path.join(viteResult.tmp, \"_fresh\", \"compiled-entry.js\"),\n    );\n\n    expect(stat.isFile).toEqual(true);\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - serves static files\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        const res = await fetch(`${address}/test_static/foo.txt`);\n        const text = await res.text();\n        expect(text).toEqual(\"it works\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - loads islands\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}/tests/island_hooks`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          await waitForText(page, \"button\", \"count: 0\");\n\n          await page.locator(\"button\").click();\n          await waitForText(page, \"button\", \"count: 1\");\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - nested islands\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}/tests/island_nested`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          await page.locator(\".outer-ready\").wait();\n          await page.locator(\".inner-ready\").wait();\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - without static/ dir\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"no_static\");\n    await using res = await buildVite(fixture);\n\n    await launchProd(\n      { cwd: res.tmp },\n      async (address) => {\n        const res = await fetch(`${address}/ok`);\n        const text = await res.text();\n        expect(text).toEqual(\"ok\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - without islands/ dir\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"no_islands\");\n    await using res = await buildVite(fixture);\n\n    await launchProd(\n      { cwd: res.tmp },\n      async (address) => {\n        const res = await fetch(`${address}`);\n        const text = await res.text();\n        expect(text).toContain(\"ok\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - without routes/ dir\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"no_routes\");\n    await using res = await buildVite(fixture);\n\n    await launchProd(\n      { cwd: res.tmp },\n      async (address) => {\n        const res = await fetch(`${address}`);\n        const text = await res.text();\n        expect(text).toEqual(\"ok\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - load json inside npm package\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}/tests/mime`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          await page.locator(\".ready\").wait();\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - fetch static assets\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}/tests/assets`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          const url = await page.locator(\"img\").evaluate((el) =>\n            // deno-lint-ignore no-explicit-any\n            (el as any).src\n          );\n\n          const res = await fetch(url);\n          await res.body?.cancel();\n          expect(res.status).toEqual(200);\n          expect(res.headers.get(\"Content-Type\")).toEqual(\"image/png\");\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - tailwind no _app\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"tailwind_no_app\");\n    await using res = await buildVite(fixture);\n\n    await launchProd(\n      { cwd: res.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          const href = await page\n            .locator(\"link[rel='stylesheet']\")\n            .evaluate((el) => {\n              // deno-lint-ignore no-explicit-any\n              return (el as any).href;\n            });\n\n          expect(href).toMatch(/\\/assets\\/client-entry-.*\\.css(\\?.*)?$/);\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - tailwind _app\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"tailwind_app\");\n    await using res = await buildVite(fixture);\n\n    await launchProd(\n      { cwd: res.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          const href = await page\n            .locator(\"link[rel='stylesheet']\")\n            .evaluate((el) => {\n              // deno-lint-ignore no-explicit-any\n              return (el as any).href;\n            });\n\n          expect(href).toMatch(/\\/assets\\/client-entry-.*\\.css/);\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - partial island\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}/tests/partial`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          await page.locator(\".ready\").wait();\n          await page.locator(\"a\").click();\n          await page.locator(\".counter-hooks\").wait();\n\n          await page.locator(\".counter-hooks button\").click();\n          await waitForText(page, \".counter-hooks button\", \"count: 1\");\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - build ID uses env variables when set\",\n  fn: async () => {\n    const revision = \"test-commit-hash-123\";\n\n    // We're running on GitHub Actions, so GITHUB_SHA will always\n    // be set\n    Deno.env.delete(\"GITHUB_SHA\");\n\n    for (\n      const key of [\n        \"DENO_DEPLOYMENT_ID\",\n        \"GITHUB_SHA\",\n        \"CI_COMMIT_SHA\",\n        \"OTHER\",\n      ]\n    ) {\n      using _ = usingEnv(key, revision);\n      await using res = await buildVite(DEMO_DIR);\n\n      await launchProd(\n        { cwd: res.tmp },\n        async (address) => {\n          const res = await fetch(`${address}/tests/build_id`);\n          const text = await res.text();\n\n          if (key === \"OTHER\") {\n            expect(text).not.toEqual(revision);\n          } else {\n            expect(text).toEqual(revision);\n          }\n        },\n      );\n    }\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - import json from jsr dependency\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        const res = await fetch(`${address}/tests/dep_json`);\n        const json = await res.json();\n        expect(json.name).toEqual(\"@marvinh-test/import-json\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - import node:*\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        const res = await fetch(`${address}/tests/feed`);\n        await res.body?.cancel();\n        expect(res.status).toEqual(200);\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - css modules\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}/tests/css_modules`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          let color = await page\n            .locator(\".red > h1\")\n            // deno-lint-ignore no-explicit-any\n            .evaluate((el) => window.getComputedStyle(el as any).color);\n          expect(color).toEqual(\"rgb(255, 0, 0)\");\n\n          color = await page\n            .locator(\".green > h1\")\n            // deno-lint-ignore no-explicit-any\n            .evaluate((el) => window.getComputedStyle(el as any).color);\n          expect(color).toEqual(\"rgb(0, 128, 0)\");\n\n          color = await page\n            .locator(\".blue > h1\")\n            // deno-lint-ignore no-explicit-any\n            .evaluate((el) => window.getComputedStyle(el as any).color);\n          expect(color).toEqual(\"rgb(0, 0, 255)\");\n\n          // Route css\n          color = await page\n            .locator(\".route > h1\")\n            // deno-lint-ignore no-explicit-any\n            .evaluate((el) => window.getComputedStyle(el as any).color);\n          expect(color).toEqual(\"rgb(255, 218, 185)\");\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - route css import\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}/tests/css`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          await waitFor(async () => {\n            const color = await page\n              .locator(\"h1\")\n              // deno-lint-ignore no-explicit-any\n              .evaluate((el) => window.getComputedStyle(el as any).color);\n            expect(color).toEqual(\"rgb(255, 0, 0)\");\n            return true;\n          });\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - remote island\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"remote_island\");\n    await using res = await buildVite(fixture);\n\n    await launchProd(\n      { cwd: res.tmp },\n      async (address) => {\n        await withBrowser(async (page) => {\n          await page.goto(`${address}`, {\n            waitUntil: \"networkidle2\",\n          });\n\n          await page.locator(\".remote-island\").wait();\n          await page.locator(\".increment\").click();\n          await waitForText(page, \".result\", \"Count: 1\");\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - error on 'node:process' import\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"node_builtin\");\n\n    await expect(buildVite(fixture)).rejects.toThrow(\n      \"Node built-in modules cannot be imported in the browser\",\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - static index.html\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        const res = await fetch(`${address}/test_static/foo`);\n        const text = await res.text();\n        expect(text).toContain(\"<h1>ok</h1>\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - base path asset handling\",\n  fn: async () => {\n    await using res = await buildVite(DEMO_DIR, { base: \"/my-app/\" });\n\n    // Read the generated server.js to check asset paths\n    const serverJs = await Deno.readTextFile(\n      path.join(res.tmp, \"_fresh\", \"server.js\"),\n    );\n\n    // Asset paths should include the base path /my-app/\n    expect(serverJs).toContain('\"/my-app/assets/');\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - env files\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        const res = await fetch(`${address}/tests/env_files`);\n        const json = await res.json();\n        expect(json).toEqual({\n          MY_ENV: \"MY_ENV test value\",\n          VITE_MY_ENV: \"VITE_MY_ENV test value\",\n          MY_LOCAL_ENV: \"MY_LOCAL_ENV test value\",\n          VITE_MY_LOCAL_ENV: \"VITE_MY_LOCAL_ENV test value\",\n        });\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - support _middleware Array\",\n  fn: async () => {\n    await launchProd(\n      { cwd: viteResult.tmp },\n      async (address) => {\n        const res = await fetch(`${address}/tests/middlewares`);\n        const text = await res.text();\n        expect(text).toEqual(\"AB\");\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - island named after global object (Map)\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"island_global_name\");\n    await using res = await buildVite(fixture);\n\n    await launchProd(\n      { cwd: res.tmp },\n      async (address) => {\n        const response = await fetch(address);\n        const html = await response.text();\n\n        // Verify the fix: UniqueNamer prefixes \"Map\" with underscore to prevent shadowing\n        // Without the fix: import Map from \"...\" and boot({Map}, ...) - shadows global Map\n        // With the fix: import _Map_N from \"...\" and boot({_Map_N}, ...) - no shadowing\n        expect(html).toMatch(/import.*_Map(_\\d+)?.*from/);\n        expect(html).toMatch(/boot\\(\\s*\\{\\s*_Map(_\\d+)?\\s*\\}/);\n      },\n    );\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite build - excludes test files from routes\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"test_files_exclusion\");\n    await using res = await buildVite(fixture);\n\n    // Verify that test files in routes/ are not bundled\n    const serverAssetsDir = path.join(res.tmp, \"_fresh\", \"server\", \"assets\");\n\n    // List all compiled route files\n    const files: string[] = [];\n    for await (const entry of Deno.readDir(serverAssetsDir)) {\n      if (entry.isFile && entry.name.startsWith(\"_fresh-route\")) {\n        files.push(entry.name);\n      }\n    }\n\n    // Should have index route but NOT test files\n    expect(files.some((f) => f.includes(\"_fresh-route___index\"))).toBe(true);\n    expect(files.some((f) => f.includes(\"index_test\"))).toBe(false);\n    expect(files.some((f) => f.includes(\"foo.test\"))).toBe(false);\n\n    // Verify no test pattern files at all\n    // Note: files with \"_test\" or \".test\" in their name (not just a \"tests\" folder)\n    const testPatterns = [\"_test.\", \"_test-\", \".test.\"];\n    for (const file of files) {\n      for (const pattern of testPatterns) {\n        expect(file.includes(pattern)).toBe(false);\n      }\n    }\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/dev_server_test.ts",
    "content": "import * as path from \"@std/path\";\nimport { expect } from \"@std/expect\";\nimport {\n  waitFor,\n  waitForText,\n  withBrowser,\n} from \"../../fresh/tests/test_utils.tsx\";\nimport {\n  DEMO_DIR,\n  FIXTURE_DIR,\n  launchDevServer,\n  prepareDevServer,\n  spawnDevServer,\n  updateFile,\n  withDevServer,\n} from \"./test_utils.ts\";\n\nconst tmp = await prepareDevServer(DEMO_DIR);\nconst demoServer = await spawnDevServer(tmp.dir, {\n  FRESH_PUBLIC_FOO: \"foobar\",\n});\n\nDeno.test({\n  name: \"vite dev - launches\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/it_works`);\n    const text = await res.text();\n    expect(text).toContain(\"it works\");\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - serves static files\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/test_static/foo.txt`);\n    const text = await res.text();\n    expect(text).toContain(\"it works\");\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - loads islands\",\n  fn: async () => {\n    await withBrowser(async (page) => {\n      await page.goto(`${demoServer.address()}/tests/island_hooks`, {\n        waitUntil: \"networkidle2\",\n      });\n      await waitForText(page, \"button\", \"count: 0\");\n\n      await page.locator(\"button\").click();\n      await waitForText(page, \"button\", \"count: 1\");\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - starts without static/ dir\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"no_static\");\n    await withDevServer(fixture, async (address) => {\n      const res = await fetch(`${address}/`);\n      const text = await res.text();\n      expect(text).toContain(\"ok\");\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - starts without islands/ dir\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"no_islands\");\n    await withDevServer(fixture, async (address) => {\n      const res = await fetch(`${address}/`);\n      const text = await res.text();\n      expect(text).toContain(\"ok\");\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - starts without routes/ dir\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"no_routes\");\n    await withDevServer(fixture, async (address) => {\n      const res = await fetch(`${address}/`);\n      const text = await res.text();\n      expect(text).toContain(\"ok\");\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - can apply HMR to islands (hooks)\",\n  ignore: true, // Test is very flaky\n  fn: async () => {\n    await withBrowser(async (page) => {\n      await page.goto(`${demoServer.address()}/tests/island_hooks`, {\n        waitUntil: \"networkidle2\",\n      });\n      await waitForText(page, \"button\", \"count: 0\");\n      await page.locator(\"button\").click();\n      await waitForText(page, \"button\", \"count: 1\");\n\n      const island = path.join(\n        demoServer.dir,\n        \"islands\",\n        \"tests\",\n        \"CounterHooks.tsx\",\n      );\n      await using _ = await updateFile(\n        island,\n        (text) => text.replace(\"count:\", \"hmr:\"),\n      );\n\n      await waitForText(page, \"button\", \"hmr: 1\");\n      await page.locator(\"button\").click();\n      await waitForText(page, \"button\", \"hmr: 2\");\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - can import json in npm package\",\n  fn: async () => {\n    await withBrowser(async (page) => {\n      await page.goto(`${demoServer.address()}/tests/mime`, {\n        waitUntil: \"networkidle2\",\n      });\n      await page.locator(\".ready\").wait();\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - inline env vars\",\n  fn: async () => {\n    await withBrowser(async (page) => {\n      await page.goto(`${demoServer.address()}/tests/env`, {\n        waitUntil: \"networkidle2\",\n      });\n      await page.locator(\".ready\").wait();\n\n      const res = await page.locator(\"pre\").evaluate((el) =>\n        // deno-lint-ignore no-explicit-any\n        (el as any).textContent ?? \"\"\n      );\n\n      expect(JSON.parse(res)).toEqual({ deno: \"foobar\", nodeEnv: \"foobar\" });\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - serves imported assets\",\n  fn: async () => {\n    // Vite has an internal allowlist that is refreshed when\n    // pathnames are requested. It will discover valid static\n    // files imported in JS once it encounters them. Therefore\n    // we must request the URL that ultimately imports the\n    // asset first for it to work.\n    let res = await fetch(`${demoServer.address()}/tests/assets`);\n    await res.body?.cancel();\n\n    res = await fetch(`${demoServer.address()}/assets/deno-logo.png`);\n    expect(res.status).toEqual(200);\n    expect(res.headers.get(\"Content-Type\")).toEqual(\"image/png\");\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - tailwind no _app\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"tailwind_no_app\");\n    await withDevServer(fixture, async (address) => {\n      await withBrowser(async (page) => {\n        await page.goto(`${address}`, {\n          waitUntil: \"networkidle2\",\n        });\n\n        await page.locator(\"style[data-vite-dev-id$='style.css']\").wait();\n      });\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - tailwind _app\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"tailwind_app\");\n    await withDevServer(fixture, async (address) => {\n      await withBrowser(async (page) => {\n        await page.goto(`${address}`, {\n          waitUntil: \"networkidle2\",\n        });\n\n        await page.locator(\"style[data-vite-dev-id$='style.css']\").wait();\n      });\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - partial island\",\n  fn: async () => {\n    await withBrowser(async (page) => {\n      await page.goto(`${demoServer.address()}/tests/partial`, {\n        waitUntil: \"networkidle2\",\n      });\n\n      await page.locator(\".ready\").wait();\n      await page.locator(\"a\").click();\n      await page.locator(\".counter-hooks\").wait();\n\n      await page.locator(\".counter-hooks button\").click();\n      await waitForText(page, \".counter-hooks button\", \"count: 1\");\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - json from jsr dependency\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/dep_json`);\n    const json = await res.json();\n    expect(json.name).toEqual(\"@marvinh-test/import-json\");\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - import node:*\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/feed`);\n    await res.body?.cancel();\n    expect(res.status).toEqual(200);\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - css modules\",\n  fn: async () => {\n    await withBrowser(async (page) => {\n      await page.goto(`${demoServer.address()}/tests/css_modules`, {\n        waitUntil: \"networkidle2\",\n      });\n\n      await waitFor(async () => {\n        let color = await page\n          .locator(\".red > h1\")\n          // deno-lint-ignore no-explicit-any\n          .evaluate((el) => window.getComputedStyle(el as any).color);\n        expect(color).toEqual(\"rgb(255, 0, 0)\");\n\n        color = await page\n          .locator(\".green > h1\")\n          // deno-lint-ignore no-explicit-any\n          .evaluate((el) => window.getComputedStyle(el as any).color);\n        expect(color).toEqual(\"rgb(0, 128, 0)\");\n\n        color = await page\n          .locator(\".blue > h1\")\n          // deno-lint-ignore no-explicit-any\n          .evaluate((el) => window.getComputedStyle(el as any).color);\n        expect(color).toEqual(\"rgb(0, 0, 255)\");\n\n        // Route css\n        color = await page\n          .locator(\".route > h1\")\n          // deno-lint-ignore no-explicit-any\n          .evaluate((el) => window.getComputedStyle(el as any).color);\n        expect(color).toEqual(\"rgb(255, 218, 185)\");\n        return true;\n      });\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - route css import\",\n  fn: async () => {\n    await withBrowser(async (page) => {\n      await page.goto(`${demoServer.address()}/tests/css`, {\n        waitUntil: \"networkidle2\",\n      });\n\n      await waitFor(async () => {\n        const color = await page\n          .locator(\"h1\")\n          // deno-lint-ignore no-explicit-any\n          .evaluate((el) => window.getComputedStyle(el as any).color);\n        expect(color).toEqual(\"rgb(255, 0, 0)\");\n        return true;\n      });\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - nested islands\",\n  fn: async () => {\n    await withBrowser(async (page) => {\n      await page.goto(`${demoServer.address()}/tests/island_nested`, {\n        waitUntil: \"networkidle2\",\n      });\n\n      await page.locator(\".outer-ready\").wait();\n      await page.locator(\".inner-ready\").wait();\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - remote island\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"remote_island\");\n    await launchDevServer(fixture, async (address) => {\n      await withBrowser(async (page) => {\n        await page.goto(`${address}`, {\n          waitUntil: \"networkidle2\",\n        });\n\n        await page.locator(\".remote-island\").wait();\n        await page.locator(\".increment\").click();\n        await waitForText(page, \".result\", \"Count: 1\");\n      });\n    });\n  },\n  sanitizeResources: false,\n  sanitizeOps: false,\n});\n\nDeno.test({\n  name: \"vite dev - error on 'node:process' import\",\n  fn: async () => {\n    const fixture = path.join(FIXTURE_DIR, \"node_builtin\");\n\n    await launchDevServer(fixture, async (address) => {\n      let res = await fetch(`${address}`);\n      await res.body?.cancel();\n\n      res = await fetch(`${address}/@id/fresh-island::NodeIsland`);\n      await res.body?.cancel();\n\n      expect(res.status).toEqual(500);\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\n// issue: https://github.com/denoland/fresh/issues/3322\nDeno.test({\n  name: \"vite dev - allow routes looking like static paths\",\n  fn: async () => {\n    const res = await fetch(\n      `${demoServer.address()}/tests/api/@marvinh@infosec.exchange`,\n    );\n    const text = await res.text();\n    expect(text).toEqual(\"ok\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\n// issue: https://github.com/denoland/fresh/issues/3323\nDeno.test({\n  name: \"vite dev - npm:pg\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/pg`);\n    const text = await res.text();\n    expect(text).toContain(\"<h1>pg</h1>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - npm:ioredis\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/ioredis`);\n    const text = await res.text();\n    expect(text).toContain(\"<h1>ioredis</h1>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - redis\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/redis`);\n    const text = await res.text();\n    expect(text).toContain(\"<h1>redis</h1>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - @supabase/postgres-js\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/supabase_pg`);\n    const text = await res.text();\n    expect(text).toContain(\"<h1>supabase</h1>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - radix\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/radix`);\n    const text = await res.text();\n    expect(text).toContain(\"click me</button>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - qs\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/qs`);\n    const text = await res.text();\n    expect(text).toContain(\"<h1>qs</h1>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - stripe\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/stripe`);\n    const text = await res.text();\n    expect(text).toContain(\"<h1>stripe</h1>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - static index.html\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/test_static/foo`);\n    const text = await res.text();\n    expect(text).toContain(\"<h1>ok</h1>\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - load .env files\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/env_files`);\n    const json = await res.json();\n    expect(json).toEqual({\n      MY_ENV: \"MY_ENV test value\",\n      VITE_MY_ENV: \"VITE_MY_ENV test value\",\n      MY_LOCAL_ENV: \"MY_LOCAL_ENV test value\",\n      VITE_MY_LOCAL_ENV: \"VITE_MY_LOCAL_ENV test value\",\n    });\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - support _middleware Array\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/middlewares`);\n    const text = await res.text();\n    expect(text).toEqual(\"AB\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - support jsx namespace\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/jsx_namespace`);\n    const text = await res.text();\n    expect(text).toContain(`xml:space=\"preserve\"`);\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n\nDeno.test({\n  name: \"vite dev - source mapped stack traces\",\n  fn: async () => {\n    const res = await fetch(`${demoServer.address()}/tests/throw`);\n    const text = await res.text();\n    expect(text).toContain(\"throw.tsx:5:11\");\n  },\n  sanitizeOps: false,\n  sanitizeResources: false,\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/deno_global_island/islands/Foo.tsx",
    "content": "export function Foo() {\n  return <h1>{Deno.cwd()}</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/deno_global_island/main.ts",
    "content": "import { App, staticFiles } from \"@fresh/core\";\n\nexport const app = new App()\n  .use(staticFiles())\n  .fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/deno_global_island/routes/index.tsx",
    "content": "import { Foo } from \"../islands/Foo.tsx\";\n\nexport default function Hello() {\n  return <Foo />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/deno_global_island/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [fresh()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/deno_global_ssr/main.ts",
    "content": "import { App, staticFiles } from \"@fresh/core\";\n\nexport const app = new App()\n  .use(staticFiles())\n  .fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/deno_global_ssr/routes/index.tsx",
    "content": "export default function Hello() {\n  return <h1>{Deno.cwd()}</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/deno_global_ssr/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [fresh()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/island_global_name/deno.json",
    "content": "{\n  \"imports\": {\n    \"fresh\": \"jsr:@fresh/core\",\n    \"@preact/signals\": \"npm:@preact/signals@^1.3.0\",\n    \"preact\": \"npm:preact@^10.26.1\",\n    \"preact/\": \"npm:/preact@^10.26.1/\"\n  }\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/island_global_name/islands/Map.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport { useEffect } from \"preact/hooks\";\n\nexport default function Map() {\n  const ready = useSignal(false);\n  const count = useSignal(0);\n\n  useEffect(() => {\n    ready.value = true;\n  }, []);\n\n  return (\n    <div class={ready.value ? \"ready\" : \"\"}>\n      <p class=\"output\">{count.value}</p>\n      <button\n        type=\"button\"\n        class=\"increment\"\n        onClick={() => count.value = count.peek() + 1}\n      >\n        increment\n      </button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/island_global_name/main.ts",
    "content": "import { App } from \"fresh\";\n\nexport const app = new App().fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/island_global_name/routes/index.tsx",
    "content": "import Map from \"../islands/Map.tsx\";\n\nexport default function Page() {\n  return <Map />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/island_global_name/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [fresh()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/no_islands/main.ts",
    "content": "import { App } from \"@fresh/core\";\n\nexport const app = new App().fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/no_islands/routes/index.tsx",
    "content": "export default function Hello() {\n  return <h1>ok</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/no_islands/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [fresh()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/no_routes/main.ts",
    "content": "import { App } from \"@fresh/core\";\n\nexport const app = new App().get(\"/\", () => new Response(\"ok\"));\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/no_routes/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [fresh()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/no_static/main.ts",
    "content": "import { App } from \"@fresh/core\";\n\nexport const app = new App().get(\"/ok\", () => new Response(\"ok\")).fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/no_static/routes/index.tsx",
    "content": "export default function Hello() {\n  return <h1>ok</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/no_static/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [fresh()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/node_builtin/islands/NodeIsland.tsx",
    "content": "import process from \"node:process\";\n\nexport function NodeIsland() {\n  return <h1>{process.version}</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/node_builtin/main.ts",
    "content": "import { App, staticFiles } from \"@fresh/core\";\n\nexport const app = new App()\n  .use(staticFiles())\n  .fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/node_builtin/routes/index.tsx",
    "content": "import { NodeIsland } from \"../islands/NodeIsland.tsx\";\n\nexport default function Hello() {\n  return <NodeIsland />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/node_builtin/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [fresh()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/remote_island/main.ts",
    "content": "import { App, staticFiles } from \"@fresh/core\";\n\nexport const app = new App()\n  .use(staticFiles())\n  .fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/remote_island/routes/index.tsx",
    "content": "import { RemoteIsland } from \"@marvinh-test/fresh-island\";\n\nexport default function Hello() {\n  return <RemoteIsland />;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/remote_island/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [fresh({\n    islandSpecifiers: [\"@marvinh-test/fresh-island\"],\n  })],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_app/assets/style.css",
    "content": "@import \"tailwindcss\";\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_app/client.ts",
    "content": "import \"./assets/style.css\";\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_app/main.ts",
    "content": "import { App } from \"@fresh/core\";\n\nexport const app = new App().fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_app/routes/_app.tsx",
    "content": "import type { PageProps } from \"@fresh/core\";\n\nexport default function App(props: PageProps) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charset=\"utf-8\" />\n      </head>\n      <body>\n        <props.Component />\n      </body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_app/routes/index.tsx",
    "content": "export default function Hello() {\n  return <h1 class=\"text-red-500\">ok</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_app/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\nimport tailwindcss from \"@tailwindcss/vite\";\n\nexport default defineConfig({\n  plugins: [fresh(), tailwindcss()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_no_app/assets/style.css",
    "content": "@import \"tailwindcss\";\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_no_app/client.ts",
    "content": "import \"./assets/style.css\";\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_no_app/main.ts",
    "content": "import { App } from \"@fresh/core\";\n\nexport const app = new App().fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_no_app/routes/index.tsx",
    "content": "export default function Hello() {\n  return <h1 class=\"text-red-500\">ok</h1>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/tailwind_no_app/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\nimport tailwindcss from \"@tailwindcss/vite\";\n\nexport default defineConfig({\n  plugins: [fresh(), tailwindcss()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/test_files_exclusion/deno.json",
    "content": "{\n  \"imports\": {\n    \"fresh\": \"jsr:@fresh/core@^2.0.0\",\n    \"@fresh/plugin-vite\": \"jsr:@fresh/plugin-vite@^1.0.0\",\n    \"preact\": \"npm:preact@^10.27.2\",\n    \"preact/\": \"npm:/preact@^10.27.2/\"\n  }\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/test_files_exclusion/main.ts",
    "content": "import { App } from \"fresh\";\n\nexport const app = new App().fsRoutes();\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/test_files_exclusion/routes/foo.test.ts",
    "content": "// This test file should NOT be bundled into production\nexport const handler = () => {\n  return new Response(\"This should never be in production\");\n};\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/test_files_exclusion/routes/index.tsx",
    "content": "export default function Home() {\n  return <div>ok</div>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/test_files_exclusion/routes/index_test.tsx",
    "content": "// This test file should NOT be bundled into production\nexport default function TestRoute() {\n  return <div>This should never be in production</div>;\n}\n"
  },
  {
    "path": "packages/plugin-vite/tests/fixtures/test_files_exclusion/vite.config.ts",
    "content": "import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [fresh()],\n});\n"
  },
  {
    "path": "packages/plugin-vite/tests/test_utils.ts",
    "content": "import { createBuilder } from \"vite\";\nimport * as path from \"@std/path\";\nimport { walk } from \"@std/fs/walk\";\nimport { withTmpDir } from \"../../fresh/src/test_utils.ts\";\nimport { withChildProcessServer } from \"../../fresh/tests/test_utils.tsx\";\n\nexport const DEMO_DIR = path.join(import.meta.dirname!, \"..\", \"demo\");\nexport const FIXTURE_DIR = path.join(import.meta.dirname!, \"fixtures\");\n\nexport async function updateFile(\n  filePath: string,\n  fn: (text: string) => string | Promise<string>,\n) {\n  const original = await Deno.readTextFile(filePath);\n  const result = await fn(original);\n  await Deno.writeTextFile(filePath, result);\n\n  return {\n    async [Symbol.asyncDispose]() {\n      await Deno.writeTextFile(filePath, original);\n    },\n  };\n}\n\nasync function copyDir(from: string, to: string) {\n  const entries = walk(from, {\n    includeFiles: true,\n    includeDirs: false,\n    skip: [/([\\\\/]+(_fresh|node_modules|vendor)[\\\\/]+|[\\\\/]+vite\\.config\\.ts)/],\n  });\n\n  for await (const entry of entries) {\n    if (entry.isFile) {\n      const relative = path.relative(from, entry.path);\n      const target = path.join(to, relative);\n\n      try {\n        await Deno.mkdir(path.dirname(target), { recursive: true });\n      } catch (err) {\n        if (!(err instanceof Deno.errors.NotFound)) {\n          throw err;\n        }\n      }\n\n      await Deno.copyFile(entry.path, target);\n    }\n  }\n}\n\nexport async function prepareDevServer(fixtureDir: string) {\n  const tmp = await withTmpDir({\n    dir: path.join(import.meta.dirname!, \"..\"),\n    prefix: \"tmp_vite_\",\n  });\n\n  await copyDir(fixtureDir, tmp.dir);\n\n  await Deno.writeTextFile(\n    path.join(tmp.dir, \"vite.config.ts\"),\n    `import { defineConfig } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\n\nexport default defineConfig({\n  plugins: [\n    fresh(),\n  ],\n});\n`,\n  );\n\n  return tmp;\n}\n\nexport async function launchDevServer(\n  dir: string,\n  fn: (address: string, dir: string) => void | Promise<void>,\n  env: Record<string, string> = {},\n) {\n  await withChildProcessServer(\n    {\n      cwd: dir,\n      args: [\"run\", \"-A\", \"--cached-only\", \"npm:vite\", \"--port\", \"0\"],\n      env,\n    },\n    async (address) => await fn(address, dir),\n  );\n}\n\nexport async function spawnDevServer(\n  dir: string,\n  env: Record<string, string> = {},\n) {\n  const boot = Promise.withResolvers<void>();\n  const p = Promise.withResolvers<void>();\n\n  let serverAddress = \"\";\n\n  const server = withChildProcessServer(\n    {\n      cwd: dir,\n      args: [\"run\", \"-A\", \"--cached-only\", \"npm:vite\", \"--port\", \"0\"],\n      env,\n    },\n    async (address) => {\n      serverAddress = address;\n      boot.resolve();\n      await p.promise;\n    },\n  );\n\n  await boot.promise;\n\n  return {\n    dir,\n    promise: server,\n    address: () => {\n      return serverAddress;\n    },\n    async [Symbol.asyncDispose]() {\n      await p.resolve();\n    },\n  };\n}\n\nexport async function withDevServer(\n  fixtureDir: string,\n  fn: (address: string, dir: string) => void | Promise<void>,\n  env: Record<string, string> = {},\n) {\n  await using tmp = await prepareDevServer(fixtureDir);\n  await launchDevServer(tmp.dir, fn, env);\n}\n\nexport async function buildVite(\n  fixtureDir: string,\n  options?: { base?: string },\n) {\n  const tmp = await withTmpDir({\n    dir: path.join(import.meta.dirname!, \"..\"),\n    prefix: \"tmp_vite_\",\n  });\n\n  const builder = await createBuilder({\n    logLevel: \"error\",\n    root: fixtureDir,\n    base: options?.base,\n    build: {\n      emptyOutDir: true,\n    },\n    environments: {\n      ssr: {\n        build: {\n          outDir: path.join(tmp.dir, \"_fresh\", \"server\"),\n        },\n      },\n      client: {\n        build: {\n          outDir: path.join(tmp.dir, \"_fresh\", \"client\"),\n        },\n      },\n    },\n  });\n  await builder.buildApp();\n\n  return {\n    tmp: tmp.dir,\n    async [Symbol.asyncDispose]() {\n      return await tmp[Symbol.asyncDispose]();\n    },\n  };\n}\n\nexport function usingEnv(name: string, value: string) {\n  const prev = Deno.env.get(name);\n  Deno.env.set(name, value);\n  return {\n    [Symbol.dispose]: () => {\n      if (prev === undefined) {\n        Deno.env.delete(name);\n      } else {\n        Deno.env.set(name, prev);\n      }\n    },\n  };\n}\n\nexport interface ProdOptions {\n  cwd: string;\n  args?: string[];\n  bin?: string;\n  env?: Record<string, string>;\n}\n\nexport async function launchProd(\n  options: ProdOptions,\n  fn: (address: string) => void | Promise<void>,\n) {\n  return await withChildProcessServer(\n    {\n      cwd: options.cwd,\n      args: options.args ??\n        [\"serve\", \"-A\", \"--cached-only\", \"--port\", \"0\", \"_fresh/server.js\"],\n    },\n    fn,\n  );\n}\n"
  },
  {
    "path": "packages/update/README.md",
    "content": "# Update an existing Fresh project.\n\nThis is a CLI tool can be used to upgrade an existing Fresh project. To do so,\nrun this command:\n\n```sh\ndeno run -Ar jsr:@fresh/update\n```\n\nGo to [https://fresh.deno.dev/](https://fresh.deno.dev/) for more information\nabout Fresh.\n"
  },
  {
    "path": "packages/update/deno.json",
    "content": "{\n  \"name\": \"@fresh/update\",\n  \"version\": \"2.2.1\",\n  \"license\": \"MIT\",\n  \"exports\": \"./src/mod.ts\",\n  \"exclude\": [\"**/tmp/*\"],\n  \"publish\": {\n    \"include\": [\n      \"src/**/*.ts\",\n      \"deno.json\",\n      \"README.md\"\n    ],\n    \"exclude\": [\"**/*_test.*\", \"*.todo\"]\n  }\n}\n"
  },
  {
    "path": "packages/update/src/mod.ts",
    "content": "import { parseArgs } from \"@std/cli/parse-args\";\nimport * as path from \"@std/path\";\nimport { ensureMinDenoVersion, error } from \"./utils.ts\";\nimport { updateProject } from \"./update.ts\";\nimport * as colors from \"@std/fmt/colors\";\n\nconst MIN_DENO_VERSION = \"1.43.1\";\n\nconst HELP = `@fresh/update\n\nUpdate a Fresh project. This updates dependencies and optionally performs code\nmods to update a project's source code to the latest recommended patterns.\n\nTo upgrade a project in the current directory, run:\n  deno run -A jsr:@fresh/update .\n\nUSAGE:\n    @fresh/update [DIRECTORY]\n`;\n\nensureMinDenoVersion(MIN_DENO_VERSION);\n\nconst flags = parseArgs(Deno.args, {});\n\n// deno-lint-ignore no-console\nconsole.log(colors.bgRgb8(\n  colors.rgb8(\" 🍋 Fresh Updater \", 0),\n  121,\n));\n// deno-lint-ignore no-console\nconsole.log();\n// deno-lint-ignore no-console\nconsole.log(\n  colors.italic(\n    \"Note: Breaking changes may require additional manual updates.\",\n  ),\n);\n// deno-lint-ignore no-console\nconsole.log();\n\nlet unresolvedDirectory = Deno.args[0];\nif (flags._.length !== 1) {\n  const userInput = prompt(\"Where is the project directory?\", \".\");\n  if (!userInput) {\n    error(HELP);\n  }\n\n  unresolvedDirectory = userInput;\n}\n\nconst dir = path.resolve(unresolvedDirectory);\nawait updateProject(dir);\n"
  },
  {
    "path": "packages/update/src/update.ts",
    "content": "import * as path from \"@std/path\";\nimport * as JSONC from \"@std/jsonc\";\nimport * as tsmorph from \"ts-morph\";\nimport * as colors from \"@std/fmt/colors\";\nimport { ProgressBar } from \"@std/cli/unstable-progress-bar\";\n\nexport const SyntaxKind = tsmorph.ts.SyntaxKind;\n\nexport const FRESH_VERSION = \"2.2.1\";\nexport const PREACT_VERSION = \"10.28.3\";\nexport const PREACT_SIGNALS_VERSION = \"2.7.1\";\n\n// Function to filter out node_modules and vendor directories from logs\nconst HIDE_FILES = /[\\\\/]+(node_modules|vendor)[\\\\/]+/;\nexport interface DenoJson {\n  lock?: boolean;\n  tasks?: Record<string, string>;\n  name?: string;\n  version?: string;\n  imports?: Record<string, string>;\n}\n\nasync function format(filePath: string) {\n  const command = new Deno.Command(Deno.execPath(), {\n    args: [\"fmt\", filePath],\n  });\n  await command.output();\n}\n\nasync function writeFormatted(filePath: string, content: string) {\n  await Deno.writeTextFile(filePath, content);\n  await format(filePath);\n}\n\nasync function updateDenoJson(\n  dir: string,\n  fn: (json: DenoJson) => void | Promise<void>,\n): Promise<void> {\n  let filePath = path.join(dir, \"deno.json\");\n  try {\n    const config = JSON.parse(await Deno.readTextFile(filePath)) as DenoJson;\n    await fn(config);\n    await writeFormatted(filePath, JSON.stringify(config));\n    return;\n  } catch (err) {\n    if (!(err instanceof Deno.errors.NotFound)) {\n      throw err;\n    }\n  }\n\n  filePath = path.join(dir, \"deno.jsonc\");\n  try {\n    const config = JSONC.parse(await Deno.readTextFile(filePath)) as DenoJson;\n    await fn(config);\n    await writeFormatted(filePath, JSON.stringify(config));\n    return;\n  } catch (err) {\n    if (!(err instanceof Deno.errors.NotFound)) {\n      throw err;\n    }\n  }\n\n  throw new Error(`Could not find deno.json or deno.jsonc in: ${dir}`);\n}\n\nexport interface ImportState {\n  core: Set<string>;\n  runtime: Set<string>;\n  compat: Set<string>;\n  httpError: number;\n}\n\nconst compat = new Set([\n  \"defineApp\",\n  \"defineLayout\",\n  \"defineRoute\",\n  \"AppProps\",\n  \"ErrorPageProps\",\n  \"Handler\",\n  \"Handlers\",\n  \"LayoutProps\",\n  \"RouteContext\",\n  \"UnknownPageProps\",\n]);\n\nexport async function updateProject(dir: string) {\n  // add initial log\n  // deno-lint-ignore no-console\n  console.log(colors.blue(\"🚀 Starting Fresh 1 to Fresh 2 migration...\"));\n\n  // deno-lint-ignore no-console\n  console.log(colors.yellow(\"📝 Updating configuration files...\"));\n\n  // Update config\n  await updateDenoJson(dir, (config) => {\n    if (config.imports !== null && typeof config.imports !== \"object\") {\n      config.imports = {};\n    }\n\n    config.imports[\"fresh\"] = `jsr:@fresh/core@^${FRESH_VERSION}`;\n    config.imports[\"preact\"] = `npm:preact@^${PREACT_VERSION}`;\n    config.imports[\"@preact/signals\"] =\n      `npm:@preact/signals@^${PREACT_SIGNALS_VERSION}`;\n    delete config.imports[\"$fresh/\"];\n    delete config.imports[\"@preact/signals-core\"];\n    delete config.imports[\"preact-render-to-string\"];\n\n    // We should always use a lockfile going forwards\n    if (\"lock\" in config) {\n      delete config.lock;\n    }\n\n    // Update Fresh 1.x tasks\n    const tasks = config.tasks;\n    if (tasks !== undefined) {\n      if (tasks.manifest === \"deno task cli manifest $(pwd)\") {\n        delete tasks.manifest;\n      }\n\n      if (\n        tasks.cli ===\n          \"echo \\\"import '\\\\$fresh/src/dev/cli.ts'\\\" | deno run --unstable -A -\" ||\n        tasks.cli ===\n          \"echo \\\"import '$fresh/src/dev/cli.ts'\\\" | deno run --unstable -A -\"\n      ) {\n        delete tasks.cli;\n      }\n\n      if (tasks.update === \"deno run -A -r https://fresh.deno.dev/update .\") {\n        tasks.update = \"deno run -A -r jsr:@fresh/update .\";\n      }\n\n      if (\n        tasks.check ===\n          \"deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx\"\n      ) {\n        tasks.check = \"deno fmt --check && deno lint && deno check\";\n      }\n\n      if (tasks.preview === \"deno run -A main.ts\") {\n        tasks.preview = \"deno serve -A _fresh/server.js\";\n      }\n    }\n  });\n\n  // add completion log\n  // deno-lint-ignore no-console\n  console.log(colors.green(\"✅ Configuration updated successfully\"));\n\n  // Delete fresh.gen.ts if it exists (no longer needed in Fresh 2.0)\n  const freshGenPath = path.join(dir, \"fresh.gen.ts\");\n  try {\n    await Deno.remove(freshGenPath);\n    // deno-lint-ignore no-console\n    console.log(colors.cyan(\"🗑️ Deleted fresh.gen.ts (no longer needed)\"));\n  } catch (err) {\n    if (!(err instanceof Deno.errors.NotFound)) {\n      // deno-lint-ignore no-console\n      console.error(\n        `Could not delete fresh.gen.ts: ${\n          err instanceof Error ? err.message : String(err)\n        }`,\n      );\n    }\n    // If file doesn't exist, that's fine - nothing to do\n  }\n\n  // deno-lint-ignore no-console\n  console.log(colors.cyan(\"🔍 Scanning for source files...\"));\n\n  // Update routes folder\n  const project = new tsmorph.Project();\n  const sfs = project.addSourceFilesAtPaths(\n    path.join(dir, \"**\", \"*.{js,jsx,ts,tsx}\"),\n  );\n\n  // Filter out node_modules and vendor files for user display\n  const userFiles = sfs.filter((sf) => !HIDE_FILES.test(sf.getFilePath()));\n\n  // deno-lint-ignore no-console\n  console.log(colors.cyan(`📁 Found ${userFiles.length} files to process`));\n\n  if (sfs.length === 0) {\n    // deno-lint-ignore no-console\n    console.log(colors.green(\"🎉 Migration completed successfully!\"));\n    return;\n  }\n\n  // deno-lint-ignore no-console\n  console.log(colors.yellow(\"🔄 Processing files...\"));\n\n  // Track progress with a single counter\n  let completedFiles = 0;\n  const modifiedFilesList: string[] = [];\n\n  // Create a progress bar\n  const bar = new ProgressBar({\n    max: sfs.length,\n    formatter(x) {\n      return `[${x.styledTime}] [${x.progressBar}] [${x.value}/${x.max} files]`;\n    },\n  });\n\n  // process files sequentially to show proper progress\n  await Promise.all(sfs.map(async (sourceFile) => {\n    try {\n      const wasModified = await updateFile(sourceFile);\n      if (wasModified) {\n        modifiedFilesList.push(sourceFile.getFilePath());\n      }\n\n      return wasModified;\n    } catch (err) {\n      // deno-lint-ignore no-console\n      console.error(`Could not process ${sourceFile.getFilePath()}`);\n      throw err;\n    } finally {\n      completedFiles++;\n      bar.value = completedFiles;\n    }\n  }));\n\n  // Clear the progress line and add a newline\n  await bar.stop();\n\n  // Filter modified files to show only user files\n  const modifiedFilesToShow = modifiedFilesList.filter((filePath) =>\n    !HIDE_FILES.test(filePath)\n  );\n\n  // add migration summary\n  // deno-lint-ignore no-console\n  console.log(\"\\n\" + colors.bold(\"📊 Migration Summary:\"));\n  // deno-lint-ignore no-console\n  console.log(`   Total files processed: ${userFiles.length}`);\n  // deno-lint-ignore no-console\n  console.log(`   Successfully modified: ${modifiedFilesToShow.length}`);\n  // deno-lint-ignore no-console\n  console.log(\n    `   Unmodified (no changes needed): ${\n      userFiles.length - modifiedFilesToShow.length\n    }`,\n  );\n\n  // Display modified files list (filtered)\n  if (modifiedFilesToShow.length > 0) {\n    // deno-lint-ignore no-console\n    console.log(\"\\n\" + colors.bold(\"📝 Modified Files:\"));\n    modifiedFilesToShow.forEach((filePath) => {\n      // show relative path\n      let relativePath = path.relative(dir, filePath);\n      // Ensure consistent path separators for logging\n      relativePath = relativePath.replace(/\\\\/g, \"/\");\n      // deno-lint-ignore no-console\n      console.log(colors.green(`   ✓ ${relativePath}`));\n    });\n  }\n\n  // deno-lint-ignore no-console\n  console.log(\"\\n\" + colors.green(\"🎉 Migration completed successfully!\"));\n}\n\nasync function updateFile(sourceFile: tsmorph.SourceFile): Promise<boolean> {\n  // keep original text for comparison\n  const originalText = sourceFile.getFullText();\n\n  const newImports: ImportState = {\n    core: new Set(),\n    runtime: new Set(),\n    compat: new Set(),\n    httpError: 0,\n  };\n\n  const text = sourceFile.getFullText()\n    .replaceAll(\"/** @jsx h */\\n\", \"\")\n    .replaceAll(\"/** @jsxFrag Fragment */\\n\", \"\")\n    .replaceAll('/// <reference no-default-lib=\"true\" />\\n', \"\")\n    .replaceAll('/// <reference lib=\"dom\" />\\n', \"\")\n    .replaceAll('/// <reference lib=\"dom.iterable\" />\\n', \"\")\n    .replaceAll('/// <reference lib=\"dom.asynciterable\" />\\n', \"\")\n    .replaceAll('/// <reference lib=\"deno.ns\" />\\n', \"\");\n  sourceFile.replaceWithText(text);\n\n  if (\n    sourceFile.getFilePath().includes(\"/routes/\") &&\n    !sourceFile.getDirectoryPath().includes(\"/(_\")\n  ) {\n    for (const [name, decl] of sourceFile.getExportedDeclarations()) {\n      if (name === \"handler\") {\n        const node = decl[0];\n        if (node.isKind(SyntaxKind.VariableDeclaration)) {\n          const init = node.getInitializer();\n          if (\n            init !== undefined &&\n            init.isKind(SyntaxKind.ObjectLiteralExpression)\n          ) {\n            for (const property of init.getProperties()) {\n              if (property.isKind(SyntaxKind.MethodDeclaration)) {\n                const name = property.getName();\n                if (\n                  name === \"GET\" || name === \"POST\" || name === \"PATCH\" ||\n                  name === \"PUT\" || name === \"DELETE\"\n                ) {\n                  const body = property.getBody();\n                  if (body !== undefined) {\n                    const stmts = body.getDescendantStatements();\n                    rewriteCtxMethods(newImports, stmts);\n                  }\n\n                  maybePrependReqVar(property, newImports, true);\n                }\n              } else if (property.isKind(SyntaxKind.PropertyAssignment)) {\n                const init = property.getInitializer();\n                if (\n                  init !== undefined &&\n                  (init.isKind(SyntaxKind.ArrowFunction) ||\n                    init.isKind(SyntaxKind.FunctionExpression))\n                ) {\n                  const body = init.getBody();\n                  if (body !== undefined) {\n                    const stmts = body.getDescendantStatements();\n                    rewriteCtxMethods(newImports, stmts);\n                  }\n\n                  maybePrependReqVar(init, newImports, true);\n                }\n              }\n            }\n          }\n        } else if (node.isKind(SyntaxKind.FunctionDeclaration)) {\n          const body = node.getBody();\n          if (body !== undefined) {\n            const stmts = body.getDescendantStatements();\n            rewriteCtxMethods(newImports, stmts);\n          }\n\n          maybePrependReqVar(node, newImports, false);\n        }\n      } else if (name === \"default\" && decl.length > 0) {\n        const caller = decl[0];\n        if (caller.isKind(SyntaxKind.CallExpression)) {\n          const expr = caller.getExpression();\n          if (expr.isKind(SyntaxKind.Identifier)) {\n            const text = expr.getText();\n            if (\n              text === \"defineApp\" || text === \"defineLayout\" ||\n              text === \"defineRoute\"\n            ) {\n              const args = caller.getArguments();\n              if (args.length > 0) {\n                const first = args[0];\n                if (\n                  first.isKind(SyntaxKind.ArrowFunction) ||\n                  first.isKind(SyntaxKind.FunctionExpression)\n                ) {\n                  const body = first.getBody();\n                  if (body !== undefined) {\n                    const stmts = body.getDescendantStatements();\n                    rewriteCtxMethods(newImports, stmts);\n                  }\n\n                  maybePrependReqVar(first, newImports, false);\n                }\n              }\n            }\n          }\n        } else if (caller.isKind(SyntaxKind.FunctionDeclaration)) {\n          const body = caller.getBody();\n          if (body !== undefined) {\n            const stmts = body.getDescendantStatements();\n            rewriteCtxMethods(newImports, stmts);\n          }\n\n          maybePrependReqVar(caller, newImports, false);\n        }\n      }\n    }\n  }\n\n  let hasCoreImport = false;\n  let hasRuntimeImport = false;\n  for (const d of sourceFile.getImportDeclarations()) {\n    const specifier = d.getModuleSpecifierValue();\n    if (specifier === \"preact\") {\n      for (const n of d.getNamedImports()) {\n        const name = n.getName();\n        if (name === \"h\" || name === \"Fragment\") n.remove();\n      }\n\n      removeEmptyImport(d);\n    } else if (specifier === \"$fresh/server.ts\") {\n      hasCoreImport = true;\n      d.setModuleSpecifier(\"fresh\");\n\n      for (const n of d.getNamedImports()) {\n        const name = n.getName();\n        newImports.core.delete(name);\n        if (compat.has(name)) {\n          n.remove();\n          newImports.compat.add(name);\n        }\n      }\n      if (newImports.core.size > 0) {\n        newImports.core.forEach((name) => {\n          d.addNamedImport(name);\n        });\n      }\n\n      removeEmptyImport(d);\n    } else if (specifier === \"$fresh/runtime.ts\") {\n      hasRuntimeImport = true;\n      d.setModuleSpecifier(\"fresh/runtime\");\n\n      for (const n of d.getNamedImports()) {\n        const name = n.getName();\n        newImports.runtime.delete(name);\n      }\n      if (newImports.runtime.size > 0) {\n        newImports.runtime.forEach((name) => {\n          d.addNamedImport(name);\n        });\n      }\n\n      removeEmptyImport(d);\n    }\n  }\n\n  if (!hasCoreImport && newImports.core.size > 0) {\n    sourceFile.addImportDeclaration({\n      moduleSpecifier: \"fresh\",\n      namedImports: Array.from(newImports.core),\n    });\n  }\n  if (!hasRuntimeImport && newImports.runtime.size > 0) {\n    sourceFile.addImportDeclaration({\n      moduleSpecifier: \"fresh/runtime\",\n      namedImports: Array.from(newImports.runtime),\n    });\n  }\n  if (newImports.compat.size > 0) {\n    sourceFile.addImportDeclaration({\n      moduleSpecifier: \"fresh/compat\",\n      namedImports: Array.from(newImports.compat),\n    });\n  }\n  if (newImports.httpError > 0) {\n    sourceFile.addImportDeclaration({\n      moduleSpecifier: \"fresh\",\n      namedImports: [\"HttpError\"],\n    });\n  }\n\n  await sourceFile.save();\n  await format(sourceFile.getFilePath());\n\n  // Check if the file was actually modified\n  const finalText = sourceFile.getFullText();\n  return originalText !== finalText;\n}\n\nfunction removeEmptyImport(d: tsmorph.ImportDeclaration) {\n  if (\n    d.getNamedImports().length === 0 &&\n    d.getNamespaceImport() === undefined &&\n    d.getDefaultImport() === undefined\n  ) {\n    d.remove();\n  }\n}\n\nfunction maybePrependReqVar(\n  method:\n    | tsmorph.MethodDeclaration\n    | tsmorph.FunctionDeclaration\n    | tsmorph.FunctionExpression\n    | tsmorph.ArrowFunction,\n  newImports: ImportState,\n  hasInferredTypes: boolean,\n) {\n  let hasRequestVar = false;\n  const params = method.getParameters();\n  if (params.length > 0) {\n    const paramName = params[0].getName();\n\n    // Add explicit types if the user did that\n    if (hasInferredTypes && params[0].getTypeNode()) {\n      hasInferredTypes = false;\n    }\n\n    hasRequestVar = params.length > 1 || paramName === \"req\";\n    if (hasRequestVar || paramName === \"_req\") {\n      if (hasRequestVar && params.length === 1) {\n        params[0].replaceWithText(\"ctx\");\n        if (!hasInferredTypes) {\n          newImports.core.add(\"FreshContext\");\n          params[0].setType(\"FreshContext\");\n        }\n      } else {\n        params[0].remove();\n\n        // Use proper type\n        if (params.length > 1) {\n          const initType = params[1].getTypeNode()?.getText();\n          if (initType !== undefined && initType === \"RouteContext\") {\n            newImports.core.add(\"FreshContext\");\n            params[1].setType(\"FreshContext\");\n          }\n        }\n      }\n    }\n    const maybeObjBinding = params.length > 1\n      ? params[1].getNameNode()\n      : undefined;\n\n    if (method.isKind(SyntaxKind.ArrowFunction)) {\n      const body = method.getBody();\n      if (!body.isKind(SyntaxKind.Block)) {\n        // deno-lint-ignore no-console\n        console.warn(`Cannot transform arrow function`);\n        return;\n      }\n    }\n\n    if (\n      (maybeObjBinding === undefined ||\n        !maybeObjBinding.isKind(SyntaxKind.ObjectBindingPattern)) &&\n      hasRequestVar &&\n      !paramName.startsWith(\"_\")\n    ) {\n      method.insertVariableStatement(0, {\n        declarationKind: tsmorph.VariableDeclarationKind.Const,\n        declarations: [{\n          name: paramName,\n          initializer: \"ctx.req\",\n        }],\n      });\n    }\n\n    if (\n      maybeObjBinding !== undefined &&\n      maybeObjBinding.isKind(SyntaxKind.ObjectBindingPattern)\n    ) {\n      const bindings = maybeObjBinding.getElements();\n      if (bindings.length > 0) {\n        let needsRemoteAddr = false;\n        for (let i = 0; i < bindings.length; i++) {\n          const binding = bindings[i];\n          const name = binding.getName();\n          if (name === \"remoteAddr\") {\n            binding.replaceWithText(\"info\");\n            needsRemoteAddr = true;\n          }\n        }\n        if (hasRequestVar && !paramName.startsWith(\"_\")) {\n          const txt = maybeObjBinding.getFullText().slice(0, -2);\n          maybeObjBinding.replaceWithText(txt + \", req }\");\n        }\n\n        if (needsRemoteAddr) {\n          method.insertVariableStatement(0, {\n            declarationKind: tsmorph.VariableDeclarationKind.Const,\n            declarations: [{\n              name: \"remoteAddr\",\n              initializer: \"info.remoteAddr\",\n            }],\n          });\n        }\n      }\n    }\n  }\n}\n\nfunction rewriteCtxMethods(\n  importState: ImportState,\n  nodes: (tsmorph.Node<tsmorph.ts.Node>)[],\n) {\n  for (let i = 0; i < nodes.length; i++) {\n    const node = nodes[i];\n\n    if (node.wasForgotten()) continue;\n\n    if (\n      node.isKind(SyntaxKind.ExpressionStatement) &&\n      node.getText() === \"ctx.renderNotFound();\"\n    ) {\n      importState.httpError++;\n      node.replaceWithText(\"throw new HttpError(404);\");\n      continue;\n    }\n\n    if (node.isKind(SyntaxKind.PropertyAccessExpression)) {\n      rewriteCtxMemberName(node);\n    } else if (node.isKind(SyntaxKind.ReturnStatement)) {\n      if (node.getText() === \"return ctx.renderNotFound();\") {\n        importState.httpError++;\n        node.replaceWithText(`throw new HttpError(404)`);\n        continue;\n      } else {\n        const expr = node.getExpression();\n        if (expr !== undefined) {\n          rewriteCtxMethods(importState, [expr]);\n        }\n      }\n    } else if (node.isKind(SyntaxKind.VariableStatement)) {\n      const decls = node.getDeclarations();\n      for (let i = 0; i < decls.length; i++) {\n        const decl = decls[i];\n        const init = decl.getInitializer();\n        if (init !== undefined) {\n          rewriteCtxMethods(importState, [init]);\n        }\n      }\n    } else if (\n      node.isKind(SyntaxKind.ExpressionStatement) ||\n      node.isKind(SyntaxKind.AwaitExpression) ||\n      node.isKind(SyntaxKind.CallExpression)\n    ) {\n      const expr = node.getExpression();\n      rewriteCtxMethods(importState, [expr]);\n    } else if (node.isKind(SyntaxKind.BinaryExpression)) {\n      rewriteCtxMethods(importState, [node.getLeft()]);\n      rewriteCtxMethods(importState, [node.getRight()]);\n    } else if (\n      !node.isKind(SyntaxKind.ExpressionStatement) &&\n      node.getKindName().endsWith(\"Statement\")\n    ) {\n      const inner = node.getDescendantStatements();\n      rewriteCtxMethods(importState, inner);\n    }\n  }\n}\n\nfunction rewriteCtxMemberName(\n  node: tsmorph.PropertyAccessExpression,\n) {\n  const children = node.getChildren();\n  if (children.length === 0) return;\n\n  if (\n    node.getExpression().getText() === \"ctx\" &&\n    node.getName() === \"remoteAddr\"\n  ) {\n    node.getExpression().replaceWithText(\"ctx.info.remoteAddr\");\n  } else if (children[0].isKind(SyntaxKind.PropertyAccessExpression)) {\n    rewriteCtxMemberName(children[0]);\n  }\n}\n"
  },
  {
    "path": "packages/update/src/update_test.ts",
    "content": "import * as path from \"@std/path\";\nimport {\n  FRESH_VERSION,\n  PREACT_SIGNALS_VERSION,\n  PREACT_VERSION,\n  updateProject,\n} from \"./update.ts\";\nimport { expect } from \"@std/expect\";\nimport { spy, type SpyCall } from \"@std/testing/mock\";\nimport { walk } from \"@std/fs/walk\";\nimport { withTmpDir, writeFiles } from \"../../fresh/src/test_utils.ts\";\n\nasync function readFiles(dir: string): Promise<Record<string, string>> {\n  const files: Record<string, string> = {};\n\n  for await (\n    const entry of walk(dir, { includeDirs: false, includeFiles: true })\n  ) {\n    const pathname = path.relative(dir, entry.path);\n    const content = await Deno.readTextFile(entry.path);\n    files[`/${pathname.replaceAll(/[\\\\]+/g, \"/\")}`] = content.trim();\n  }\n\n  return files;\n}\n\nDeno.test(\"update - remove JSX pragma import\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{}`,\n    \"/routes/index.tsx\": `import { h, Fragment } from \"preact\";\n/** @jsx h */\n/** @jsxFrag Fragment */\nexport default function Foo() {\n  return null;\n}`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(files[\"/routes/index.tsx\"])\n    .toEqual(`export default function Foo() {\n  return null;\n}`);\n});\n\nDeno.test(\"update - 1.x project deno.json\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{}`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(JSON.parse(files[\"/deno.json\"]))\n    .toEqual({\n      imports: {\n        \"fresh\": `jsr:@fresh/core@^${FRESH_VERSION}`,\n        \"@preact/signals\": `npm:@preact/signals@^${PREACT_SIGNALS_VERSION}`,\n        \"preact\": `npm:preact@^${PREACT_VERSION}`,\n      },\n    });\n});\n\nDeno.test(\"update - 1.x project deno.json with imports\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{\n        \"imports\": {\n          \"$fresh/\": \"foo\"\n        }\n      }`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(JSON.parse(files[\"/deno.json\"]))\n    .toEqual({\n      imports: {\n        \"fresh\": `jsr:@fresh/core@^${FRESH_VERSION}`,\n        \"@preact/signals\": `npm:@preact/signals@^${PREACT_SIGNALS_VERSION}`,\n        \"preact\": `npm:preact@^${PREACT_VERSION}`,\n      },\n    });\n});\n\nDeno.test(\"update - 1.x project deno.json tasks + lock\", async () => {\n  await using tmp = await withTmpDir();\n  await writeFiles(tmp.dir, {\n    \"/deno.json\": `{\n      \"lock\": false,\n      \"tasks\": {\n        \"check\": \"deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx\",\n        \"cli\": \"echo \\\\\"import '$fresh/src/dev/cli.ts'\\\\\" | deno run --unstable -A -\",\n        \"manifest\": \"deno task cli manifest $(pwd)\",\n        \"start\": \"deno run -A --watch=static/,routes/ dev.ts\",\n        \"build\": \"deno run -A dev.ts build\",\n        \"preview\": \"deno run -A main.ts\",\n        \"update\": \"deno run -A -r https://fresh.deno.dev/update .\"\n      }\n    }`,\n  });\n\n  await updateProject(tmp.dir);\n  const files = await readFiles(tmp.dir);\n\n  const updated = JSON.parse(files[\"/deno.json\"]);\n  expect(updated.lock).toEqual(undefined);\n  expect(updated.tasks)\n    .toEqual({\n      build: \"deno run -A dev.ts build\",\n      check: \"deno fmt --check && deno lint && deno check\",\n      preview: \"deno serve -A _fresh/server.js\",\n      start: \"deno run -A --watch=static/,routes/ dev.ts\",\n      update: \"deno run -A -r jsr:@fresh/update .\",\n    });\n});\n\nDeno.test(\"update - 1.x project middlewares\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": \"{}\",\n    \"/routes/_middleware.ts\": `import { FreshContext } from \"$fresh/server.ts\";\n\ninterface State {\n  data: string;\n}\n\nexport async function handler(\n  req: Request,\n  ctx: FreshContext<State>,\n) {\n  ctx.state.data = \"myData\";\n  ctx.state.url = req.url;\n  const resp = await ctx.next();\n  resp.headers.set(\"server\", \"fresh server\");\n  return resp;\n}`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(files[\"/routes/_middleware.ts\"])\n    .toEqual(`import { FreshContext } from \"fresh\";\n\ninterface State {\n  data: string;\n}\n\nexport async function handler(\n  ctx: FreshContext<State>,\n) {\n  const req = ctx.req;\n\n  ctx.state.data = \"myData\";\n  ctx.state.url = req.url;\n  const resp = await ctx.next();\n  resp.headers.set(\"server\", \"fresh server\");\n  return resp;\n}`);\n});\n\nDeno.test(\"update - 1.x project middlewares one arg\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": \"{}\",\n    \"/routes/_middleware.ts\": `export async function handler(req: Request) {\n  return new Response(\"hello world from: \" + req.url);\n}`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(files[\"/routes/_middleware.ts\"])\n    .toEqual(`import { FreshContext } from \"fresh\";\n\nexport async function handler(ctx: FreshContext) {\n  const req = ctx.req;\n\n  return new Response(\"hello world from: \" + req.url);\n}`);\n});\n\nDeno.test(\"update - 1.x update '$fresh/*' imports\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{}`,\n    \"/routes/index.tsx\": `import { PageProps } from \"$fresh/server.ts\";\nexport default function Foo(props: PageProps) {\n  return null;\n}`,\n    \"/routes/foo.tsx\": `import { asset, Head } from \"$fresh/runtime.ts\";`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(files[\"/routes/index.tsx\"])\n    .toEqual(`import { PageProps } from \"fresh\";\nexport default function Foo(props: PageProps) {\n  return null;\n}`);\n  expect(files[\"/routes/foo.tsx\"])\n    .toEqual(`import { asset, Head } from \"fresh/runtime\";`);\n});\n\nDeno.test(\"update - 1.x update handler signature\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{}`,\n    \"/routes/index.tsx\": `import { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(req, ctx) {},\n  async POST(req, ctx) {},\n  async PATCH(req, ctx) {},\n  async PUT(req, ctx) {},\n  async DELETE(req, ctx) {},\n};`,\n    \"/routes/foo.tsx\": `import { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(_req, ctx) {},\n  async POST(_req, ctx) {},\n  async PATCH(_req, ctx) {},\n  async PUT(_req, ctx) {},\n  async DELETE(_req, ctx) {},\n};`,\n    \"/routes/name.tsx\": `import { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(request, ctx) {},\n  async POST(request, ctx) {},\n  async PATCH(request, ctx) {},\n  async PUT(request, ctx) {},\n  async DELETE(request, ctx) {},\n};`,\n    \"/routes/name-unused.tsx\": `import { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(_request, ctx) {},\n  async POST(_request, ctx) {},\n  async PATCH(_request, ctx) {},\n  async PUT(_request, ctx) {},\n  async DELETE(_request, ctx) {},\n};`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(files[\"/routes/index.tsx\"])\n    .toEqual(`import { Handlers } from \"fresh/compat\";\n\nexport const handler: Handlers = {\n  async GET(ctx) {\n    const req = ctx.req;\n  },\n  async POST(ctx) {\n    const req = ctx.req;\n  },\n  async PATCH(ctx) {\n    const req = ctx.req;\n  },\n  async PUT(ctx) {\n    const req = ctx.req;\n  },\n  async DELETE(ctx) {\n    const req = ctx.req;\n  },\n};`);\n  expect(files[\"/routes/foo.tsx\"])\n    .toEqual(`import { Handlers } from \"fresh/compat\";\n\nexport const handler: Handlers = {\n  async GET(ctx) {},\n  async POST(ctx) {},\n  async PATCH(ctx) {},\n  async PUT(ctx) {},\n  async DELETE(ctx) {},\n};`);\n\n  expect(files[\"/routes/name.tsx\"])\n    .toEqual(`import { Handlers } from \"fresh/compat\";\n\nexport const handler: Handlers = {\n  async GET(ctx) {\n    const request = ctx.req;\n  },\n  async POST(ctx) {\n    const request = ctx.req;\n  },\n  async PATCH(ctx) {\n    const request = ctx.req;\n  },\n  async PUT(ctx) {\n    const request = ctx.req;\n  },\n  async DELETE(ctx) {\n    const request = ctx.req;\n  },\n};`);\n  expect(files[\"/routes/name-unused.tsx\"])\n    .toEqual(`import { Handlers } from \"fresh/compat\";\n\nexport const handler: Handlers = {\n  async GET(ctx) {},\n  async POST(ctx) {},\n  async PATCH(ctx) {},\n  async PUT(ctx) {},\n  async DELETE(ctx) {},\n};`);\n});\n\nDeno.test(\n  \"update - 1.x update handler signature method one arg\",\n  async () => {\n    await using _tmp = await withTmpDir();\n    const dir = _tmp.dir;\n    await writeFiles(dir, {\n      \"/deno.json\": `{}`,\n      \"/routes/index.tsx\": `export const handler: Handlers = {\n  GET(req) {\n    return Response.redirect(req.url);\n  },\n};`,\n    });\n    await updateProject(dir);\n    const files = await readFiles(dir);\n    expect(files[\"/routes/index.tsx\"])\n      .toEqual(`export const handler: Handlers = {\n  GET(ctx) {\n    const req = ctx.req;\n\n    return Response.redirect(req.url);\n  },\n};`);\n  },\n);\n\nDeno.test.ignore(\n  \"update - 1.x update handler signature variable\",\n  async () => {\n    await using _tmp = await withTmpDir();\n    const dir = _tmp.dir;\n    await writeFiles(dir, {\n      \"/deno.json\": `{}`,\n      \"/routes/index.tsx\": `export const handler: Handlers = {\n  GET: (req) => Response.redirect(req.url)\n};`,\n      \"/routes/foo.tsx\": `export const handler: Handlers = {\n  GET: (req, ctx) => Response.redirect(req.url),\n};`,\n    });\n    await updateProject(dir);\n    const files = await readFiles(dir);\n    expect(files[\"/routes/index.tsx\"])\n      .toEqual(`export const handler: Handlers = {\n  GET: (ctx) => {\n    const req = ctx.req;\n\n    return Response.redirect(req.url);\n  },\n};`);\n    expect(files[\"/routes/foo.tsx\"])\n      .toEqual(`export const handler: Handlers = {\n  GET: (ctx) => {\n    const req = ctx.req;\n\n    return Response.redirect(req.url);\n  },\n};`);\n  },\n);\n\nDeno.test(\n  \"update - 1.x update handler signature non-inferred\",\n  async () => {\n    await using _tmp = await withTmpDir();\n    const dir = _tmp.dir;\n    await writeFiles(dir, {\n      \"/deno.json\": `{}`,\n      \"/routes/index.tsx\": `export const handler = {\n  GET(req: Request){\n    return Response.redirect(req.url);\n  }\n};`,\n    });\n    await updateProject(dir);\n    const files = await readFiles(dir);\n    expect(files[\"/routes/index.tsx\"])\n      .toEqual(`import { FreshContext } from \"fresh\";\n\nexport const handler = {\n  GET(ctx: FreshContext) {\n    const req = ctx.req;\n\n    return Response.redirect(req.url);\n  },\n};`);\n  },\n);\n\nDeno.test(\n  \"update - 1.x update handler signature with destructure\",\n  async () => {\n    await using _tmp = await withTmpDir();\n    const dir = _tmp.dir;\n    await writeFiles(dir, {\n      \"/deno.json\": `{}`,\n      \"/routes/index.tsx\": `import { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(req, { params, render, remoteAddr }) {},\n  async POST(req, { params, render, remoteAddr }) {},\n  async PATCH(req, { params, render, remoteAddr }) {},\n  async PUT(req, { params, render, remoteAddr }) {},\n  async DELETE(req, { params, render, remoteAddr }) {},\n};`,\n    });\n    await updateProject(dir);\n    const files = await readFiles(dir);\n    expect(files[\"/routes/index.tsx\"])\n      .toEqual(`import { Handlers } from \"fresh/compat\";\n\nexport const handler: Handlers = {\n  async GET({ params, render, info, req }) {\n    const remoteAddr = info.remoteAddr;\n  },\n  async POST({ params, render, info, req }) {\n    const remoteAddr = info.remoteAddr;\n  },\n  async PATCH({ params, render, info, req }) {\n    const remoteAddr = info.remoteAddr;\n  },\n  async PUT({ params, render, info, req }) {\n    const remoteAddr = info.remoteAddr;\n  },\n  async DELETE({ params, render, info, req }) {\n    const remoteAddr = info.remoteAddr;\n  },\n};`);\n  },\n);\n\nDeno.test(\"update - 1.x update define* handler signatures\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{}`,\n    \"/routes/_app.tsx\": `import { defineApp } from \"$fresh/server.ts\";\nexport default defineApp(async (req, ctx) => {\n  return null;\n});`,\n    \"/routes/_layout.tsx\": `import { defineLayout } from \"$fresh/server.ts\";\nexport default defineLayout(async (req, ctx) => {\n  return null;\n});`,\n    \"/routes/foo.tsx\": `import { defineRoute } from \"$fresh/server.ts\";\nexport default defineRoute(async (req, ctx) => {\n  return null;\n});`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(files[\"/routes/_app.tsx\"])\n    .toEqual(`import { defineApp } from \"fresh/compat\";\n\nexport default defineApp(async (ctx) => {\n  const req = ctx.req;\n\n  return null;\n});`);\n  expect(files[\"/routes/_layout.tsx\"])\n    .toEqual(`import { defineLayout } from \"fresh/compat\";\n\nexport default defineLayout(async (ctx) => {\n  const req = ctx.req;\n\n  return null;\n});`);\n  expect(files[\"/routes/foo.tsx\"])\n    .toEqual(`import { defineRoute } from \"fresh/compat\";\n\nexport default defineRoute(async (ctx) => {\n  const req = ctx.req;\n\n  return null;\n});`);\n});\n\nDeno.test(\n  \"update - 1.x update component signature async\",\n  async () => {\n    await using _tmp = await withTmpDir();\n    const dir = _tmp.dir;\n    await writeFiles(dir, {\n      \"/deno.json\": `{}`,\n      \"/routes/index.tsx\":\n        `export default async function Index(req: Request, ctx: RouteContext) {\n  if (true) {\n    return ctx.renderNotFound();\n  }\n  if (\"foo\" === \"foo\" as any) {\n    ctx.renderNotFound();\n    return ctx.renderNotFound();\n  }\n  return new Response(req.url);\n}`,\n    });\n    await updateProject(dir);\n    const files = await readFiles(dir);\n    expect(files[\"/routes/index.tsx\"])\n      .toEqual(`import { FreshContext } from \"fresh\";\nimport { HttpError } from \"fresh\";\n\nexport default async function Index(ctx: FreshContext) {\n  const req = ctx.req;\n\n  if (true) {\n    throw new HttpError(404);\n  }\n  if (\"foo\" === \"foo\" as any) {\n    throw new HttpError(404);\n    throw new HttpError(404);\n  }\n  return new Response(req.url);\n}`);\n  },\n);\n\nDeno.test.ignore(\n  \"update - 1.x ctx.renderNotFound() -> throw new HttpError(404)\",\n  async () => {\n    await using _tmp = await withTmpDir();\n    const dir = _tmp.dir;\n    await writeFiles(dir, {\n      \"/deno.json\": `{}`,\n      \"/routes/index.tsx\": `import { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(_req, ctx) {\n    return ctx.renderNotFound();\n  },\n};`,\n      \"/routes/foo.tsx\": `export const handler = (ctx) => {\n  return ctx.renderNotFound();\n}`,\n    });\n\n    await updateProject(dir);\n    const files = await readFiles(dir);\n\n    expect(files[\"/routes/index.tsx\"])\n      .toEqual(`import { Handlers } from \"fresh\";\n\nexport const handler: Handlers = {\n  async GET(ctx) {\n    throw HttpError(404);\n  },\n};`);\n\n    expect(files[\"/routes/foo.tsx\"])\n      .toEqual(`export const handler = (ctx) => {\n  throw HttpError(404);\n};`);\n  },\n);\n\nDeno.test.ignore(\n  \"update - 1.x ctx.remoteAddr -> ctx.info.remoteAddr\",\n  async () => {\n    await using _tmp = await withTmpDir();\n    const dir = _tmp.dir;\n    await writeFiles(dir, {\n      \"/deno.json\": `{}`,\n      \"/routes/index.tsx\": `import { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(_req, ctx) {\n    let msg = ctx.remoteAddr.transport === \"tcp\" ? \"ok\" : \"not ok\";\n    msg += typeof ctx.renderNotFound === \"function\";\n    return new Response(msg);\n  },\n};`,\n    });\n\n    await updateProject(dir);\n    const files = await readFiles(dir);\n\n    expect(files[\"/routes/index.tsx\"])\n      .toEqual(`import { Handlers } from \"fresh\";\n\nexport const handler: Handlers = {\n  async GET(ctx) {\n    let msg = ctx.info.remoteAddr.transport === \"tcp\" ? \"ok\" : \"not ok\";\n    msg += typeof ctx.throw === \"function\";\n    return new Response(msg);\n  },\n};`);\n  },\n);\n\nDeno.test.ignore(\"update - 1.x destructured ctx members\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{}`,\n    \"/routes/index.tsx\": `import { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async GET(_req, { url, renderNotFound, remoteAddr }) {\n    if (true) {\n      return new Response(!!remoteAddr ? \"ok\" : \"not ok\");\n    } else {\n      console.log(url.href);\n      return renderNotFound();\n    }\n  },\n};`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(files[\"/routes/index.tsx\"])\n    .toEqual(`import { Handlers } from \"fresh\";\n\nexport const handler: Handlers = {\n  async GET({ url, throw, info }) {\n    const renderNotFound = () => throw(404);\n    const remoteAddr = info.remoteAddr;\n\n    if (true) {\n      return new Response(!!remoteAddr ? \"ok\" : \"not ok\");\n    } else {\n      console.log(url.href);\n      return renderNotFound();\n    }\n  },\n};`);\n});\n\nDeno.test(\"update - 1.x remove reference comments\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{}`,\n    \"/routes/main.ts\": `/// <reference no-default-lib=\"true\" />\n/// <reference lib=\"dom\" />\n/// <reference lib=\"dom.iterable\" />\n/// <reference lib=\"dom.asynciterable\" />\n/// <reference lib=\"deno.ns\" />\n`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(files[\"/routes/main.ts\"]).toEqual(\"\");\n});\n\nDeno.test(\"update - island files\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{}`,\n    \"/islands/foo.tsx\": `import { IS_BROWSER } from \"$fresh/runtime.ts\";`,\n  });\n\n  await updateProject(dir);\n  const files = await readFiles(dir);\n\n  expect(files[\"/islands/foo.tsx\"]).toEqual(\n    `import { IS_BROWSER } from \"fresh/runtime\";`,\n  );\n});\n\nDeno.test(\"update - ignores node_modules and vendor in logs\", async () => {\n  await using _tmp = await withTmpDir();\n  const dir = _tmp.dir;\n  await writeFiles(dir, {\n    \"/deno.json\": `{}`,\n    \"/routes/index.tsx\": `import { PageProps } from \"$fresh/server.ts\";\nexport default function Foo(props: PageProps) {\n  return null;\n}`,\n    \"/node_modules/foo/bar.ts\":\n      `import { IS_BROWSER } from \"$fresh/runtime.ts\";`,\n    \"/vendor/foo/bar.ts\": `import { IS_BROWSER } from \"$fresh/runtime.ts\";`,\n  });\n\n  const consoleLogSpy = spy(console, \"log\");\n\n  try {\n    await updateProject(dir);\n  } finally {\n    consoleLogSpy.restore();\n  }\n\n  const files = await readFiles(dir);\n\n  expect(files[\"/node_modules/foo/bar.ts\"]).toEqual(\n    `import { IS_BROWSER } from \"fresh/runtime\";`,\n  );\n  expect(files[\"/vendor/foo/bar.ts\"]).toEqual(\n    `import { IS_BROWSER } from \"fresh/runtime\";`,\n  );\n  expect(files[\"/routes/index.tsx\"]).toEqual(\n    `import { PageProps } from \"fresh\";\nexport default function Foo(props: PageProps) {\n  return null;\n}`,\n  );\n\n  const fullLog = consoleLogSpy.calls.map((call: SpyCall) =>\n    call.args.join(\" \")\n  ).join(\n    \"\\n\",\n  );\n\n  expect(fullLog).toMatch(/Total files processed: 1/);\n  expect(fullLog).toMatch(/Successfully modified: 1/);\n  expect(fullLog).toMatch(/Unmodified \\(no changes needed\\): 0/);\n  expect(fullLog).not.toMatch(/node_modules/);\n  expect(fullLog).not.toMatch(/vendor/);\n  expect(fullLog).toMatch(/✓ routes\\/index.tsx/);\n});\n"
  },
  {
    "path": "packages/update/src/utils.ts",
    "content": "import * as semver from \"@std/semver\";\n\n/**\n * Check that the minimum supported Deno version is being used.\n */\nexport function ensureMinDenoVersion(minVersion: string) {\n  if (\n    !semver.greaterOrEqual(\n      semver.parse(Deno.version.deno),\n      semver.parse(minVersion),\n    )\n  ) {\n    let message =\n      `Deno version ${minVersion} or higher is required. Please update Deno.\\n\\n`;\n\n    if (Deno.execPath().includes(\"homebrew\")) {\n      message +=\n        \"You seem to have installed Deno via homebrew. To update, run: `brew upgrade deno`\\n\";\n    } else {\n      message += \"To update, run: `deno upgrade`\\n\";\n    }\n\n    error(message);\n  }\n}\n\nexport function error(message: string): never {\n  // deno-lint-ignore no-console\n  console.error(`%cerror%c: ${message}`, \"color: red; font-weight: bold\", \"\");\n  Deno.exit(1);\n}\n"
  },
  {
    "path": "tools/check_docs.ts",
    "content": "import { checkDocs } from \"https://github.com/denoland/std/raw/refs/heads/main/_tools/check_docs.ts\";\n\nawait checkDocs([\n  import.meta.resolve(\"../packages/fresh/src/error.ts\"),\n]);\n"
  },
  {
    "path": "tools/check_links.ts",
    "content": "import { DOMParser } from \"linkedom\";\nimport * as path from \"@std/path\";\n\nimport { launchProd } from \"../packages/plugin-vite/tests/test_utils.ts\";\nimport { createBuilder } from \"vite\";\n\nconst www = path.join(import.meta.dirname!, \"..\", \"www\");\nconst builder = await createBuilder({\n  root: www,\n});\n\nawait builder.buildApp();\n\ninterface CheckLink {\n  url: URL;\n  referrer: URL | null;\n}\n\nawait launchProd({ cwd: www }, async (address) => {\n  const first = new URL(address);\n\n  const stack: CheckLink[] = [{ referrer: null, url: first }];\n\n  const seen = new Set<string>();\n\n  let current: CheckLink | undefined;\n  while ((current = stack.pop()) !== undefined) {\n    seen.add(current.url.pathname);\n\n    // deno-lint-ignore no-console\n    console.log(\"Checking...\", current.url.href);\n\n    const headers = new Headers();\n    headers.set(\n      \"accept\",\n      \"text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8\",\n    );\n    const res = await fetch(current.url, { headers });\n    const text = await res.text();\n\n    if (res.status === 404) {\n      throw new Error(\n        `Failed url ${current.url.href}, referrer: ${current.referrer?.href}`,\n      );\n    }\n\n    if (!res.headers.get(\"Content-type\")?.includes(\"text/html\")) {\n      continue;\n    }\n\n    const doc = new DOMParser().parseFromString(text, \"text/html\");\n\n    for (const link of doc.querySelectorAll(\"a\")) {\n      const next = new URL(link.href, first.origin);\n      if (next.origin !== first.origin) continue;\n      if (seen.has(next.pathname)) continue;\n\n      stack.push({ url: next, referrer: current.url });\n    }\n  }\n});\n"
  },
  {
    "path": "tools/release.ts",
    "content": "import * as cl from \"@std/fmt/colors\";\nimport * as path from \"@std/path\";\nimport type { DenoJson } from \"../packages/update/src/update.ts\";\nimport * as semver from \"@std/semver\";\n\nfunction showHelp() {\n  // deno-lint-ignore no-console\n  console.log(`\n  Usage: deno run -A release.ts <major|minor|patch|...>\n`);\n}\n\nfunction exitError(msg: string): never {\n  // deno-lint-ignore no-console\n  console.error(cl.red(msg));\n  showHelp();\n  Deno.exit(1);\n}\n\nif (Deno.args.length === 0) {\n  exitError(`Missing version argument.`);\n} else if (Deno.args.length > 1) {\n  exitError(`Too many arguments. Expected only one release argument`);\n}\n\nconst ROOT_DIR = path.join(import.meta.dirname!, \"..\");\nconst denoJsonPath = path.join(ROOT_DIR, \"packages\", \"fresh\", \"deno.json\");\nconst wwwDenoJsonPath = path.join(ROOT_DIR, \"www\", \"deno.json\");\nconst denoJson = JSON.parse(await Deno.readTextFile(denoJsonPath)) as DenoJson;\n\nconst version = Deno.args[0];\nconst current = semver.parse(denoJson.version!);\nconst next = semver.parse(denoJson.version!);\nif (version === \"major\") {\n  next.major++;\n  next.minor = 0;\n  next.patch = 0;\n  next.prerelease = undefined;\n} else if (version === \"minor\") {\n  next.minor++;\n  next.patch = 0;\n} else if (version === \"patch\") {\n  next.patch++;\n  next.prerelease = undefined;\n} else {\n  if (!next.prerelease) {\n    exitError(`Unknown prerelease version`);\n  }\n\n  if (next.prerelease[0] === version) {\n    ((next.prerelease[1]) as number)++;\n  } else {\n    next.prerelease[0] = version;\n    next.prerelease[1] = 1;\n  }\n}\n\nconst updateJsonPath = path.join(ROOT_DIR, \"packages\", \"update\", \"deno.json\");\nconst updateJson = JSON.parse(await Deno.readTextFile(updateJsonPath));\nconst currentUpdate = semver.parse(updateJson.version);\n\nfunction formatUpgradeMsg(\n  name: string,\n  from: semver.SemVer,\n  to: semver.SemVer,\n): string {\n  const nameMsg = cl.yellow(name);\n  const fromMsg = cl.green(semver.format(from));\n  const toMsg = cl.yellow(semver.format(to));\n\n  return `  ${nameMsg}: ${fromMsg} -> ${toMsg}`;\n}\n\n// deno-lint-ignore no-console\nconsole.log(formatUpgradeMsg(denoJson.name!, current, next));\n// deno-lint-ignore no-console\nconsole.log(formatUpgradeMsg(updateJson.name!, currentUpdate, next));\n\nif (!confirm(\"Proceed with update?\")) {\n  Deno.exit(0);\n}\n\nconst denoTailwindJson = JSON.parse(\n  await Deno.readTextFile(\n    path.join(ROOT_DIR, \"packages\", \"plugin-tailwindcss\", \"deno.json\"),\n  ),\n) as DenoJson;\n\nasync function replaceInFile(\n  file: string,\n  replacer: (content: string) => string,\n) {\n  const raw = await Deno.readTextFile(file);\n  const replaced = replacer(raw);\n  await Deno.writeTextFile(file, replaced);\n}\n\nconst nextVersion = semver.format(next);\n\nfunction replaceJsonVersion(version: string) {\n  return (content: string) =>\n    content.replace(/\"version\":\\s\"[^\"]+\"/, `\"version\": \"${version}\"`);\n}\nawait replaceInFile(denoJsonPath, replaceJsonVersion(nextVersion));\nawait replaceInFile(updateJsonPath, replaceJsonVersion(nextVersion));\n\nasync function getNpmVersion(name: string) {\n  const res = await fetch(`https://registry.npmjs.org/${name}`, {\n    headers: {\n      \"Accept\":\n        \"application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*\",\n    },\n  });\n  const json = await res.json();\n  return json[\"dist-tags\"].latest;\n}\n\nconst [preactVersion, preactSignalsVersion] = await Promise.all([\n  getNpmVersion(\"preact\"),\n  getNpmVersion(\"@preact/signals\"),\n]);\n\nfunction updateVersions(content: string): string {\n  const replaced = content\n    .replace(\n      /FRESH_VERSION\\s=\\s[\"']([^'\"]+)['\"]/g,\n      `FRESH_VERSION = \"${nextVersion}\"`,\n    )\n    .replace(\n      /FRESH_TAILWIND_VERSION\\s=\\s[\"']([^'\"]+)['\"]/g,\n      `FRESH_TAILWIND_VERSION = \"${denoTailwindJson.version!}\"`,\n    )\n    .replace(\n      /PREACT_VERSION\\s=\\s[\"']([^'\"]+)['\"]/g,\n      `PREACT_VERSION = \"${preactVersion!}\"`,\n    )\n    .replace(\n      /PREACT_SIGNALS_VERSION\\s=\\s[\"']([^'\"]+)['\"]/g,\n      `PREACT_SIGNALS_VERSION = \"${preactSignalsVersion!}\"`,\n    );\n\n  if (content === replaced) {\n    exitError(`Did not find FRESH_VERSION string`);\n  }\n\n  return replaced;\n}\n\nfunction replaceDepVersion(\n  registry: \"jsr\" | \"npm\",\n  name: string,\n  version: string,\n) {\n  return (content: string) => {\n    return content.replace(\n      new RegExp(`\"${name}\":\\\\s\"[^\"]+\"`),\n      `\"${name}\": \"${registry}:${name}@^${version}\"`,\n    );\n  };\n}\n\n// Update preact + @preact/signals version\nawait replaceInFile(\n  denoJsonPath,\n  replaceDepVersion(\"npm\", \"preact\", preactVersion),\n);\nawait replaceInFile(\n  denoJsonPath,\n  replaceDepVersion(\"npm\", \"@preact/signals\", preactSignalsVersion),\n);\nawait replaceInFile(\n  wwwDenoJsonPath,\n  replaceDepVersion(\"npm\", \"preact\", preactVersion),\n);\nawait replaceInFile(\n  wwwDenoJsonPath,\n  replaceDepVersion(\"npm\", \"@preact/signals\", preactSignalsVersion),\n);\n\nconst updateScriptPath = path.join(\n  ROOT_DIR,\n  \"packages\",\n  \"update\",\n  \"src\",\n  \"update.ts\",\n);\nawait replaceInFile(updateScriptPath, updateVersions);\n\nconst initScriptPath = path.join(\n  ROOT_DIR,\n  \"packages\",\n  \"init\",\n  \"src\",\n  \"init.ts\",\n);\nawait replaceInFile(initScriptPath, updateVersions);\n"
  },
  {
    "path": "versions.json",
    "content": "[\n  \"1.7.3\",\n  \"1.7.2\",\n  \"1.7.1\",\n  \"1.7.0\",\n  \"1.6.8\",\n  \"1.6.7\",\n  \"1.6.6\",\n  \"1.6.5\",\n  \"1.6.4\",\n  \"1.6.3\",\n  \"1.6.2\",\n  \"1.6.1\",\n  \"1.6.0\",\n  \"1.5.4\",\n  \"1.5.3\",\n  \"1.5.2\",\n  \"1.5.1\",\n  \"1.5.0\",\n  \"1.4.3\",\n  \"1.4.2\",\n  \"1.4.1\",\n  \"1.4.0\",\n  \"1.3.1\",\n  \"1.3.0\",\n  \"1.2.0\",\n  \"1.1.6\",\n  \"1.1.5\",\n  \"1.1.4\",\n  \"1.1.3\",\n  \"1.1.2\",\n  \"1.1.1\",\n  \"1.1.0\",\n  \"1.0.2\",\n  \"1.0.1\",\n  \"1.0.0\",\n  \"1.0.0-rc.6\",\n  \"1.0.0-rc.5\",\n  \"1.0.0-rc.4\",\n  \"1.0.0-rc.3\",\n  \"1.0.0-rc.2\",\n  \"1.0.0-rc.1\"\n]\n"
  },
  {
    "path": "www/README.md",
    "content": "# Fresh website\n\nThis is the Fresh website source. The Fresh website contains:\n\n- a homepage\n- a documentation page\n\n### Usage\n\nStart the project:\n\n```\ndeno task start\n```\n\nThis will watch the project directory and restart as necessary.\n"
  },
  {
    "path": "www/assets/styles.css",
    "content": "@import \"tailwindcss\";\n\n@custom-variant dark (&:where([data-theme=\"dark\"], [data-theme=\"dark\"] *));\n\n@theme {\n  --color-fresh: hsl(50 100% 56%);\n  --color-fresh-green: hsl(142 71% 29%);\n\n  --color-background-primary: hsl(215deg 100% 100%);\n  --color-background-secondary: hsl(210 29% 97%);\n  --color-background-tertiary: hsl(207 33% 95%);\n  --color-foreground-primary: hsl(0 0% 9%);\n  --color-foreground-secondary: hsl(0 0% 23%);\n  --color-foreground-tertiary: hsl(0 0% 32%);\n  --color-foreground-quaternary: hsl(0 0% 42%);\n}\n\n@variant dark {\n  --color-fresh: hsl(50 100% 56%);\n  --color-fresh-green: hsl(142 71% 29%);\n\n  --color-background-primary: hsl(220deg 11% 11%);\n  --color-background-secondary: hsl(216deg 19% 18%);\n  --color-background-tertiary: hsl(216deg 27.7% 22%);\n  --color-foreground-primary: hsl(215deg 17% 99%);\n  --color-foreground-secondary: hsl(215deg 17% 83%);\n  --color-foreground-tertiary: hsl(215deg 17% 20%);\n  --color-foreground-quaternary: hsl(215deg 17% 10%);\n\n  --color-info: hsl(194 76% 41%);\n}\n\nhtml[data-theme=\"dark\"]:root {\n  color: var(--color-foreground-primary);\n  background-color: var(--color-background-primary);\n}\n\n/* Scrollbar colors that look good on light and dark theme */\n* {\n  scrollbar-color: hsl(0deg 0% 50% / 0.5) hsl(0 0% 50% / 0.1) !important;\n}\n\n@font-face {\n  font-family: Fixel;\n  font-style: normal;\n  src: url(\"/fonts/FixelVariable.woff2\") format(\"woff2\");\n  font-weight: 100 900;\n  font-display: swap;\n}\n\n@font-face {\n  font-family: Fixel;\n  font-style: italic;\n  src: url(\"/fonts/FixelVariableItalic.woff2\") format(\"woff2\");\n  font-weight: 100 900;\n  font-display: swap;\n}\n\nbody {\n  font-family:\n    Fixel, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto,\n    Oxygen, Ubuntu, Cantarell, \"Open Sans\", \"Helvetica Neue\", sans-serif;\n  font-weight: 450;\n  font-size: 1.125rem;\n  line-height: 1.5;\n  color: #333;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  scroll-margin-top: 6rem;\n}\n\nhr {\n  border-color: hsl(from var(--color-foreground-secondary) h s l / 0.1);\n}\n\n::selection {\n  background-color: #b1d5ff;\n}\n\nhtml[data-theme=\"dark\"] ::selection {\n  background-color: #064c9c;\n}\n\n.form-select-bg {\n  background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3e%3cpath d='M7 7l3-3 3 3m0 6l-3 3-3-3' stroke='%239fa6b2' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3e%3c/svg%3e\");\n  background-position: right 0.5rem center;\n  background-size: 1.5em 1.5em;\n  background-repeat: no-repeat;\n}\n\n.group[data-copied] .group-not-copied,\n.group:not([data-copied]) .group-copied {\n  display: none;\n}\n"
  },
  {
    "path": "www/client.ts",
    "content": "import \"./assets/styles.css\";\n\ndocument.addEventListener(\"click\", async (ev) => {\n  let el = ev.target as HTMLElement | null;\n  if (el === null) return;\n  if (!(el instanceof HTMLButtonElement)) {\n    el = el.closest(\"button\");\n  }\n  if (el === null) return;\n\n  const code = el.dataset.code;\n  if (!code) return;\n\n  try {\n    await navigator.clipboard.writeText(code);\n\n    el.dataset.copied = \"true\";\n\n    setTimeout(() => {\n      delete el.dataset.copied;\n    }, 1000);\n  } catch (error) {\n    const message = error instanceof Error ? error.message : String(error);\n    // deno-lint-ignore no-console\n    console.error(message || \"Copy failed\");\n  }\n});\n"
  },
  {
    "path": "www/components/CodeBlock.tsx",
    "content": "import { Prism } from \"../utils/prism.ts\";\n\nexport function CodeBlock(\n  { code, lang }: { code: string; lang: \"js\" | \"ts\" | \"jsx\" | \"md\" | \"bash\" },\n) {\n  return (\n    <pre\n      class=\"rounded-lg text-base leading-relaxed bg-slate-800 text-white p-4 sm:p-6 md:p-4 lg:p-6 2xl:p-8 overflow-x-auto\"\n      data-language={lang}\n      // deno-lint-ignore react-no-danger\n    ><code dangerouslySetInnerHTML={{ __html: Prism.highlight(code, Prism.languages[lang], lang)}} /></pre>\n  );\n}\n"
  },
  {
    "path": "www/components/CodeWindow.tsx",
    "content": "import type { JSX } from \"preact\";\n\ninterface CodeWindowProps extends JSX.HTMLAttributes<HTMLDivElement> {\n  name?: string;\n}\n\nexport function CodeWindow(props: CodeWindowProps) {\n  return (\n    <div\n      {...props}\n      class={`-mx-4 sm:mx-0 w-[calc(100%+2rem)] sm:w-full sm:rounded-lg bg-slate-700 overflow-hidden sm:max-w-full [&>*]:rounded-t-none ${\n        props.class ?? \"\"\n      }`}\n      style={props.style}\n    >\n      <div class=\"flex items-stretch justify-start\">\n        <div class=\"p-4 flex items-center\">\n          <svg\n            aria-hidden=\"true\"\n            width=\"41\"\n            height=\"10\"\n            viewBox=\"0 0 41 10\"\n            fill=\"none\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <ellipse cx=\"20.9388\" cy=\"5\" rx=\"4.99155\" ry=\"5\" fill=\"#FFBD2D\" />\n            <ellipse cx=\"5.96421\" cy=\"5\" rx=\"4.99155\" ry=\"5\" fill=\"#FF5F56\" />\n            <ellipse cx=\"35.9134\" cy=\"5\" rx=\"4.99155\" ry=\"5\" fill=\"#26C940\" />\n          </svg>\n        </div>\n        <div class=\"p-4 w-full leading-none text-slate-400 text-base font-mono\">\n          {props.name ?? \"\"}\n        </div>\n      </div>\n      {props.children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/components/CopyButton.tsx",
    "content": "import * as Icons from \"./Icons.tsx\";\n\nexport function CopyButton(props: { code: string }) {\n  return (\n    <div class=\"relative my-2 mr-4 sm:mr-6\">\n      <button\n        type=\"button\"\n        data-code={props.code}\n        aria-label=\"Copy to Clipboard\"\n        class=\"rounded-sm p-1.5 border border-foreground-secondary/30 hover:bg-foreground-secondary/70 data-copied:text-green-500 relative group cursor-pointer\"\n      >\n        <span class=\"group-copied\">\n          <Icons.Check />\n        </span>\n        <span class=\"group-not-copied\">\n          <Icons.Copy />\n        </span>\n      </button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/components/DocsSidebar.tsx",
    "content": "import type {\n  TableOfContentsCategory,\n  TableOfContentsCategoryEntry,\n} from \"../data/docs.ts\";\n\nexport function SidebarCategory(props: {\n  category: TableOfContentsCategory;\n}) {\n  const { title, href, entries } = props.category;\n\n  return (\n    <li class=\"my-2 block\">\n      <a\n        href={href}\n        class=\"text-foreground-secondary hover:text-gray-600 dark:hover:text-green-300 aria-[current]:text-fresh-green dark:aria-[current]:text-green-300 aria-[current]:hover:underline font-bold\"\n      >\n        {title}\n      </a>\n      {entries.length > 0 && (\n        <ul class=\"py-2 nested list-outside\">\n          {entries.map((entry) => (\n            <SidebarEntry key={entry.href} entry={entry} />\n          ))}\n        </ul>\n      )}\n    </li>\n  );\n}\n\nexport function SidebarEntry(props: {\n  entry: TableOfContentsCategoryEntry;\n}) {\n  const { title, href } = props.entry;\n\n  return (\n    <li class=\"py-[1px]\">\n      <a\n        href={href}\n        class=\"aria-[current]:text-fresh-green aria-[current]:border-green-600 dark:aria-[current]:text-green-300 aria-[current]:bg-fresh-green/5 dark:hover:text-green-300 border-l-4 border-transparent px-4 py-0.5 transition-colors hover:text-fresh-green/80 font-normal block\"\n      >\n        {title}\n      </a>\n    </li>\n  );\n}\n"
  },
  {
    "path": "www/components/FancyLink.tsx",
    "content": "import type { ComponentChildren } from \"preact\";\n\nexport function FancyLink(\n  props: { href: string; children: ComponentChildren; class?: string },\n) {\n  return (\n    <a\n      href={props.href}\n      class={`group p-4 px-5 pb-3.5 bg-gradient-to-br from-fresh/40 to-fresh font-semibold rounded-sm leading-none flex items-center justify-center hover:bg-fresh transition-colors duration-200 ease-in-out max-w-max text-balance ${\n        props.class ?? \"\"\n      }`}\n    >\n      {props.children}\n      <svg\n        aria-hidden=\"true\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        class=\"icon icon-tabler icon-tabler-arrow-right relative ml-2 bottom-[0.05em] transition-transform ease-out duration-150 group-hover:translate-x-1\"\n        width=\"1em\"\n        height=\"1em\"\n        viewBox=\"0 0 24 24\"\n        stroke-width=\"3\"\n        stroke=\"currentColor\"\n        fill=\"none\"\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n      >\n        <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n        <path d=\"M5 12l14 0\" />\n        <path d=\"M13 18l6 -6\" />\n        <path d=\"M13 6l6 6\" />\n      </svg>\n    </a>\n  );\n}\n"
  },
  {
    "path": "www/components/FeatureIcons.tsx",
    "content": "export function NoBuild() {\n  return (\n    <svg\n      class=\"bg-green-200 md:bg-yellow-200 rounded-full w-20 h-20\"\n      viewBox=\"0 0 75 75\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M29.6442 31.5258C32.7992 30.1446 35 27.0249 35 23.3976C35 19.5107 31.9774 16.2066 28.4569 15L28.4771 23.0715L26.1651 24.4575L23.6055 23.0715L22.5523 15C19.0318 16.2066 17 19.5107 17 23.3976C17 27.0249 19.2008 30.1446 22.3558 31.5258L20.9876 59.8211C20.8443 62.6385 23.1298 65 26 65C28.8702 65 31.1557 62.6385 31.0124 59.8211L29.6442 31.5258Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M47.284 42.6163L47.2873 41L53.9958 41.0114L53.9921 42.795L54.6175 43.4406C55.4816 44.3326 56.0026 45.5025 56 46.789L55.9644 60.98C55.9588 63.7576 53.4998 66.0051 50.472 66C47.4443 65.9948 44.9944 63.7389 45 60.9613L45.0356 46.7703C45.0383 45.4095 45.6267 44.1799 46.5868 43.2741L47.284 42.6163Z\"\n        fill=\"white\"\n      />\n      <path\n        fill-rule=\"evenodd\"\n        clip-rule=\"evenodd\"\n        d=\"M49.8521 12.4306L51.318 12.4333L53.1045 15.1525L52.2037 17.8668L52.1223 17.8666L52.0805 40.4179L56.64 40.4264L56.6332 44.1144C57.5516 45.3706 58.0938 46.9224 58.0907 48.5973L58.0649 59.8452C58.0571 64.0303 54.6581 67.4168 50.473 67.409C46.2878 67.4012 42.9014 64.0022 42.9092 59.8171L42.9351 48.5692C42.9384 46.7973 43.5513 45.1662 44.5726 43.878L44.579 40.404L48.9858 40.4122L49.0276 17.8609L48.0554 15.1432L49.8521 12.4306ZM48.1615 45.2833L48.1639 44.0022L53.0418 44.0113L53.0392 45.425L53.494 45.9367C54.1223 46.6437 54.5011 47.5709 54.4992 48.5907L54.4733 59.8386C54.4692 62.0401 52.6812 63.8215 50.4796 63.8174C48.2781 63.8134 46.4966 62.0253 46.5007 59.8238L46.5266 48.5759C46.5286 47.4973 46.9564 46.5227 47.6545 45.8047L48.1615 45.2833Z\"\n        fill=\"#6C6E78\"\n      />\n    </svg>\n  );\n}\n\nexport function TypeScript() {\n  return (\n    <svg\n      class=\"bg-yellow-200 md:bg-blue-200 rounded-full w-20 h-20\"\n      viewBox=\"0 0 75 75\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <rect\n        x=\"19.0002\"\n        y=\"39.2439\"\n        width=\"35\"\n        height=\"13\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M24.0538 40.3665C24.4356 39.4174 24.0397 38.3332 23.1361 37.8535L15.509 33.8043C14.5877 33.3152 13.4452 33.6163 12.8846 34.4959L8.31343 41.6682C7.99514 42.1676 7.91454 42.7824 8.09335 43.347C8.27217 43.9115 8.692 44.3678 9.23976 44.593L18.6291 48.4517C19.1221 48.6543 19.6756 48.6518 20.1667 48.4446C20.6578 48.2374 21.0459 47.8428 21.2448 47.3483L24.0538 40.3665Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M49.9732 40.9998C49.7598 40.5079 49.7529 39.9509 49.954 39.4539C50.1551 38.9569 50.5473 38.5614 51.0427 38.3562L59.0669 35.0327C59.964 34.6611 60.9984 34.9876 61.5197 35.8069L65.6876 42.3581C66.0054 42.8575 66.0856 43.472 65.9066 44.0363C65.7277 44.6005 65.308 45.0565 64.7605 45.2815L55.6007 49.0459C54.5929 49.4601 53.4391 48.9914 53.0056 47.9918L49.9732 40.9998Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <mask id=\"path-4-inside-1_230_106\" fill=\"white\">\n        <rect x=\"22.7859\" y=\"6\" width=\"27.9707\" height=\"26.5363\" rx=\"3.45287\" />\n      </mask>\n      <rect\n        x=\"22.7859\"\n        y=\"6\"\n        width=\"27.9707\"\n        height=\"26.5363\"\n        rx=\"3.45287\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"8\"\n        stroke-linejoin=\"round\"\n        mask=\"url(#path-4-inside-1_230_106)\"\n      />\n      <path\n        d=\"M21.0002 48.2439C20.4343 48.2439 19.8949 48.4837 19.5157 48.9037C19.1364 49.3238 18.953 49.8849 19.0107 50.4479L20.8562 68.4479C20.9608 69.4681 21.8202 70.2439 22.8458 70.2439H50.696C51.7445 70.2439 52.615 69.4342 52.6908 68.3884L53.995 50.3884C54.0352 49.8343 53.843 49.2885 53.4647 48.8817C53.0863 48.475 52.5558 48.2439 52.0002 48.2439H21.0002Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M20.5889 48.2439C19.6815 48.2439 18.8878 48.8548 18.6555 49.732L17.0669 55.732C16.908 56.332 17.0367 56.9719 17.4153 57.4638C17.794 57.9557 18.3795 58.2439 19.0002 58.2439H53.0002C53.6034 58.2439 54.1744 57.9717 54.5541 57.5031C54.9339 57.0344 55.0818 56.4195 54.9568 55.8294L53.6857 49.8294C53.4899 48.9051 52.6739 48.2439 51.7291 48.2439H20.5889Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M29.7296 17.3967V15.6467H36.6827V17.3967H34.2765V23.6467H32.1359V17.3967H29.7296ZM41.8126 18.1467C41.7918 17.8863 41.6941 17.6832 41.5197 17.5374C41.3478 17.3915 41.0861 17.3186 40.7345 17.3186C40.5105 17.3186 40.3269 17.3459 40.1837 17.4006C40.0431 17.4527 39.9389 17.5243 39.8712 17.6155C39.8035 17.7066 39.7684 17.8108 39.7657 17.928C39.7605 18.0243 39.7775 18.1116 39.8165 18.1897C39.8582 18.2652 39.9233 18.3342 40.0118 18.3967C40.1004 18.4566 40.2137 18.5113 40.3517 18.5608C40.4897 18.6103 40.6538 18.6545 40.8439 18.6936L41.5001 18.8342C41.9428 18.928 42.3217 19.0517 42.6368 19.2053C42.9519 19.359 43.2098 19.54 43.4103 19.7483C43.6108 19.954 43.7579 20.1858 43.8517 20.4436C43.948 20.7014 43.9975 20.9827 44.0001 21.2874C43.9975 21.8134 43.866 22.2587 43.6056 22.6233C43.3452 22.9879 42.9728 23.2652 42.4884 23.4553C42.0066 23.6454 41.4272 23.7405 40.7501 23.7405C40.0548 23.7405 39.448 23.6376 38.9298 23.4319C38.4142 23.2262 38.0131 22.9097 37.7267 22.4827C37.4428 22.053 37.2996 21.5035 37.297 20.8342H39.3595C39.3725 21.079 39.4337 21.2847 39.5431 21.4514C39.6525 21.6181 39.8061 21.7444 40.004 21.8303C40.2045 21.9163 40.4428 21.9592 40.7189 21.9592C40.9506 21.9592 41.1447 21.9306 41.3009 21.8733C41.4572 21.816 41.5756 21.7366 41.6564 21.635C41.7371 21.5334 41.7788 21.4176 41.7814 21.2874C41.7788 21.165 41.7384 21.0582 41.6603 20.967C41.5848 20.8733 41.4598 20.79 41.2853 20.717C41.1108 20.6415 40.8751 20.5712 40.5782 20.5061L39.7814 20.3342C39.073 20.1806 38.5144 19.9241 38.1056 19.5647C37.6993 19.2027 37.4975 18.7092 37.5001 18.0842C37.4975 17.5764 37.6329 17.1324 37.9064 16.7522C38.1824 16.3694 38.5639 16.0712 39.0509 15.8577C39.5405 15.6441 40.1017 15.5374 40.7345 15.5374C41.3803 15.5374 41.9389 15.6454 42.4103 15.8616C42.8816 16.0777 43.2449 16.3824 43.5001 16.7756C43.7579 17.1663 43.8881 17.6233 43.8907 18.1467H41.8126Z\"\n        fill=\"#6C6E78\"\n      />\n    </svg>\n  );\n}\n\nexport function Island() {\n  return (\n    <svg\n      class=\"bg-blue-200 rounded-full w-20 h-20\"\n      viewBox=\"0 0 75 75\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M24.8765 39.3727C16.4753 36.5509 8.60772 50.9392 13.5157 54.999C18.4236 59.0588 27.0005 56.7329 35.569 56.7329C38.2618 56.7329 41.2797 56.7952 44.2466 56.8159C49.2995 57.4544 59.6147 57.6582 60.4526 53.3657C62.4427 47.3005 51.5966 33.7385 47.8048 32.5681C36.246 29.0003 37.3759 43.5709 24.8765 39.3727Z\"\n        fill=\"white\"\n      />\n      <path\n        d=\"M43.8994 56.801C40.9326 56.7803 37.9147 56.718 35.2219 56.718C26.6533 56.718 18.0765 59.0439 13.1685 54.9841C8.26055 50.9243 16.1281 36.536 24.5293 39.3578C37.0288 43.556 35.8989 28.9854 47.4577 32.5532C51.2495 33.7236 62.0956 47.2856 60.1054 53.3508\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linecap=\"round\"\n      />\n      <path\n        d=\"M53.4297 52.6355C50.5204 52.2718 45.7928 60.2724 46.5201 62.0907C47.2474 63.9091 48.3384 65 53.4297 65C58.521 65 60.3393 63.5454 59.9756 61.3634C59.6119 59.1814 56.339 52.9991 53.4297 52.6355Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n      />\n      <path\n        d=\"M15.8976 48.26C18.0796 47.169 22.8072 46.0776 26.0801 47.5322C29.3531 48.9869 34.0807 50.8051 40.6266 49.3505C47.1725 47.8959 47.1725 43.1682 55.5368 45.7139\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linecap=\"round\"\n      />\n      <path\n        d=\"M41 21.1538C41 23.2294 39.2584 25 37 25C34.7416 25 33 23.2294 33 21.1538C33 20.87 33.163 20.269 33.708 19.2319C34.1695 18.3536 34.7738 17.3956 35.4483 16.3263C35.5287 16.1988 35.6102 16.0697 35.6925 15.9389C36.1237 15.2541 36.5758 14.5288 37 13.7903C37.4242 14.5288 37.8763 15.2541 38.3075 15.9389C38.3898 16.0697 38.4713 16.1988 38.5517 16.3263C39.2262 17.3956 39.8305 18.3536 40.292 19.2319C40.837 20.269 41 20.87 41 21.1538Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\n// from https://heroicons.com/\nexport function Globe() {\n  return (\n    <svg\n      class=\"bg-green-200 rounded-full w-20 h-20\"\n      viewBox=\"0 0 75 75\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <circle cx=\"37.5\" cy=\"37.5\" r=\"20.5\" fill=\"white\" />\n      <path\n        d=\"M17.1253 35.2222H21.5556C22.7638 35.2222 23.9225 35.7022 24.7768 36.5565C25.6312 37.4108 26.1111 38.5696 26.1111 39.7778V42.0556C26.1111 43.2638 26.5911 44.4225 27.4454 45.2768C28.2997 46.1311 29.4585 46.6111 30.6667 46.6111C31.8749 46.6111 33.0336 47.0911 33.8879 47.9454C34.7423 48.7997 35.2222 49.9585 35.2222 51.1667V57.8747M28.3889 19.1297V22.6944C28.3889 24.2047 28.9888 25.6531 30.0568 26.721C31.1247 27.7889 32.5731 28.3889 34.0833 28.3889H35.2222C36.4304 28.3889 37.5892 28.8688 38.4435 29.7232C39.2978 30.5775 39.7778 31.7362 39.7778 32.9444C39.7778 34.1527 40.2577 35.3114 41.1121 36.1657C41.9664 37.02 43.1251 37.5 44.3333 37.5C45.5415 37.5 46.7003 37.02 47.5546 36.1657C48.4089 35.3114 48.8889 34.1527 48.8889 32.9444C48.8889 31.7362 49.3688 30.5775 50.2232 29.7232C51.0775 28.8688 52.2362 28.3889 53.4444 28.3889H55.868M44.3333 56.8338V51.1667C44.3333 49.9585 44.8133 48.7997 45.6676 47.9454C46.522 47.0911 47.6807 46.6111 48.8889 46.6111H55.868M58 37.5C58 40.1921 57.4698 42.8578 56.4395 45.345C55.4093 47.8322 53.8993 50.0921 51.9957 51.9957C50.0921 53.8993 47.8322 55.4093 45.345 56.4395C42.8578 57.4698 40.1921 58 37.5 58C34.8079 58 32.1422 57.4698 29.655 56.4395C27.1678 55.4093 24.9079 53.8993 23.0043 51.9957C21.1007 50.0921 19.5907 47.8322 18.5605 45.345C17.5302 42.8578 17 40.1921 17 37.5C17 32.0631 19.1598 26.8488 23.0043 23.0043C26.8488 19.1598 32.0631 17 37.5 17C42.9369 17 48.1512 19.1598 51.9957 23.0043C55.8402 26.8488 58 32.0631 58 37.5Z\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function LightWeight() {\n  return (\n    <svg\n      class=\"bg-yellow-200 rounded-full w-20 h-20\"\n      viewBox=\"0 0 75 75\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M30.323 50.1677C29.8721 49.8671 28.7299 48.6949 27.7681 46.4105C36.7993 41.3595 29.128 29.5594 21.1555 35.8905C15.5533 40.3393 17.6603 50.2484 19.7385 56.4481C17.4611 57.4948 14.4355 63.0071 16.9474 65.5191C19.4594 68.031 23.3436 65.8806 24.9717 64.4913C27.4228 65.3605 30.8953 65.8286 34.3916 66.2169C33.3449 73.1945 43.4625 72.8456 45.207 70.4035C46.9514 67.9613 45.9047 68.3102 45.9047 66.2169C47.5891 65.8119 48.1797 65.4739 49.5 64.4913C52.0585 64.9679 59.5112 67.6124 63 59.5881C57.069 60.9836 53.7647 58.8955 51 57.5C48 44.5 34.3916 44 30.323 50.1677Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M26.7447 65.1702C24.3025 58.1925 29.6623 51.64 33.5 48.5C34.4304 48.5 38.4297 47.6627 41.4999 48.5C35.0804 53.803 31.8617 62.9606 32.3268 65.868C31.7454 65.868 29.8149 65.7284 26.7447 65.1702Z\"\n        fill=\"#6C6E78\"\n      />\n      <path d=\"M36 46L36.4849 29.1428\" stroke=\"#6C6E78\" stroke-width=\"4\" />\n      <path\n        d=\"M36.8337 27.7464C42.2272 27.7464 46.1603 22.8429 46.1603 17.3732C46.1603 11.9035 42.2272 7 36.8337 7C31.4403 7 27.5072 11.9035 27.5072 17.3732C27.5072 22.8429 31.4403 27.7464 36.8337 27.7464Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n      />\n      <path\n        d=\"M34.3916 30.8922L36.4849 28.1011L38.9271 30.8922H34.3916Z\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M38.2293 59.5881C34.7405 60.9836 34.3916 64.8213 34.3916 65.868\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linecap=\"round\"\n      />\n      <circle cx=\"25.5\" cy=\"38.5\" r=\"1.5\" fill=\"#6C6E78\" />\n    </svg>\n  );\n}\n\nexport function Garbage() {\n  return (\n    <svg\n      class=\"bg-blue-200 md:bg-green-200 rounded-full w-20 h-20\"\n      viewBox=\"0 0 75 75\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M16.005 26.4841C15.4819 23.4303 56.4538 22.9234 57 26.4841L56.0103 35.8989L48.0082 42.1286C48.0082 47.559 26.9502 47.2714 26.9502 41.298L16.8423 35.0682L16.005 26.4841Z\"\n        fill=\"#6C6E78\"\n      />\n      <rect\n        x=\"31.8011\"\n        y=\"11.3972\"\n        width=\"21.6202\"\n        height=\"24.1198\"\n        rx=\"2\"\n        transform=\"rotate(17.4292 31.8011 11.3972)\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M44.4035 36.4333L26.8075 37.3565C25.7044 37.4144 24.7633 36.5671 24.7054 35.4641L23.6512 15.3719C23.5933 14.2689 24.4406 13.3278 25.5436 13.2699L36.0817 12.717C36.629 12.6882 37.1642 12.8854 37.562 13.2624L45.0097 20.3184C45.3819 20.6712 45.6045 21.1534 45.6314 21.6655L46.296 34.3312C46.3538 35.4343 45.5066 36.3754 44.4035 36.4333Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M37.0664 12.6393V20.7628H41.1282H45.1899\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n      <path\n        d=\"M21.371 59.8173L18.3324 30.1443C19.439 30.3495 20.766 30.5156 22.2219 30.6503C26.1528 31.0142 31.3471 31.1776 36.5067 31.1603C41.6674 31.1431 46.8576 30.9448 50.7825 30.5728C52.264 30.4323 53.6071 30.2634 54.7178 30.0605L52.0492 59.8426C52.0457 59.8822 52.0433 59.922 52.0421 59.9617C52.0222 60.0083 51.9242 60.2102 51.5236 60.5509C50.9569 61.0328 50.0124 61.585 48.6549 62.1081C45.9574 63.1474 41.9747 63.9394 37.1105 63.9965C32.0512 64.0559 27.8606 63.3506 25.0081 62.325C23.5731 61.809 22.5738 61.2466 21.9724 60.7394C21.4388 60.2893 21.3856 60.0355 21.3814 60.0124C21.3811 59.9473 21.3777 59.8822 21.371 59.8173Z\"\n        fill=\"white\"\n        stroke=\"#6C6E78\"\n        stroke-width=\"4\"\n        stroke-linejoin=\"round\"\n      />\n    </svg>\n  );\n}\n"
  },
  {
    "path": "www/components/Footer.tsx",
    "content": "import type { JSX } from \"preact\";\n\nconst LINKS = [\n  {\n    title: \"Source\",\n    href: \"https://github.com/denoland/fresh\",\n  },\n  {\n    title: \"License\",\n    href: \"https://github.com/denoland/fresh/blob/main/LICENSE\",\n  },\n  {\n    title: \"Code of Conduct\",\n    href: \"https://github.com/denoland/fresh/blob/main/CODE_OF_CONDUCT.md\",\n  },\n];\n\nexport default function Footer(props: JSX.HTMLAttributes<HTMLElement>) {\n  return (\n    <footer\n      class={`border-t-2 border-foreground-secondary/20 md:h-16 flex mt-16 justify-center md:mx-16 ${props.class}`}\n    >\n      <div class=\"flex flex-col sm:flex-row gap-4 justify-between items-center max-w-screen-xl mx-auto w-full sm:px-6 md:px-8 p-4 text-base\">\n        <div class=\"text-foreground-secondary text-center\">\n          <span>© {new Date().getFullYear()} the Fresh authors</span>\n        </div>\n\n        <div class=\"flex items-center gap-8\">\n          {LINKS.map((link) => (\n            <a\n              href={link.href}\n              class=\"text-foreground-secondary hover:underline\"\n            >\n              {link.title}\n            </a>\n          ))}\n        </div>\n      </div>\n    </footer>\n  );\n}\n"
  },
  {
    "path": "www/components/Header.tsx",
    "content": "import NavigationBar from \"./NavigationBar.tsx\";\n\nexport default function Header(props: { title: string; active: string }) {\n  const isHome = props.active == \"/\";\n  const isDocs = props.active == \"/docs\";\n  const isShowcase = props.active == \"/showcase\";\n\n  return (\n    <header\n      class={[\n        \"mx-auto flex gap-3 items-center\",\n        isHome ? \"justify-end h-20 max-w-screen-xl\" : \"justify-between\",\n        isDocs\n          ? \"h-20 max-w-screen-2xl w-full sticky top-0 bg-background-primary  z-50 backdrop-blur-xs\"\n          : \"\",\n        isShowcase ? \"max-w-screen-xl w-full\" : \"\",\n      ].join(\" \")}\n      f-client-nav={false}\n    >\n      {!isHome && (\n        <div class=\"p-4 flex items-center\">\n          <Logo />\n        </div>\n      )}\n      <NavigationBar class=\"\" active={props.active} />\n    </header>\n  );\n}\n\nexport function Logo() {\n  return (\n    <a href=\"/\" class=\"flex mr-3 items-center shrink-0\" aria-label=\"Top Page\">\n      <img\n        src=\"/logo.svg\"\n        alt=\"Fresh logo\"\n        width={40}\n        height={40}\n      />\n      <svg\n        preserveAspectRatio=\"xMinYMin\"\n        viewBox=\"0 0 250 75\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        class=\"h-5 ml-2.5 shrink-0 hidden sm:inline-block fill-[#0A140C] dark:fill-[#f5ebf3]\"\n        aria-label=\"Fresh logo\"\n      >\n        <path\n          d=\"M14.0805 0.761269V70.0893H0V0.761269H14.0805ZM35.6322 30.2257V41.7803H10.3448V30.2257H35.6322ZM38.5057 0.761269V12.3159H10.3448V0.761269H38.5057ZM46.5517 0.761269H68.9655C73.5632 0.761269 77.5862 1.62785 80.7471 3.36105C83.908 4.80538 86.4943 7.11632 88.2184 10.2939C90.1641 13.8269 91.1548 17.8107 91.092 21.8485C91.092 25.6038 90.5172 28.7813 89.3678 31.3811C88.5057 34.2698 87.069 36.2919 85.0575 38.0251C83.046 39.7583 81.0345 40.9137 78.4483 42.3581L74.1379 44.669H56.0345V33.1143H68.3908C70.0971 33.2247 71.7975 32.8225 73.2759 31.9589C74.4253 31.0923 75.2874 29.9368 76.1494 28.4925C76.8253 26.6458 77.1186 24.6798 77.0115 22.7151C77.0115 20.6931 76.7241 18.9599 76.1494 17.2267C75.5747 15.7823 74.7126 14.338 73.2759 13.7603C72.4138 12.8937 70.6897 12.3159 68.9655 12.3159H60.3448V70.0893H46.5517V0.761269V0.761269ZM78.1609 70.0893L65.5172 39.1805H79.8851L93.1035 69.5115V70.0893H78.1609ZM140.517 58.5346V70.0893H110.345V58.5346H140.517ZM114.655 0.761269V70.0893H100.575V0.761269H114.943H114.655ZM136.494 29.0702V40.0471H110.345V29.0702H136.207H136.494ZM140.517 0.761269V12.3159H110.345V0.761269H140.517ZM178.161 51.8907C178.161 50.7352 178.161 49.5797 177.586 48.7131C177.586 47.5577 177.012 46.6911 176.149 45.8245L173.276 42.9358L167.816 40.6249L160.345 37.1585L154.023 32.8255C152.021 31.2537 150.359 29.2878 149.138 27.0481C148.066 24.5949 147.574 21.9252 147.701 19.2487C147.701 16.3601 147.989 13.4714 149.138 11.4493C150.166 9.00506 151.739 6.83084 153.736 5.09427C155.747 3.64994 158.046 2.20559 160.632 1.33899C167.362 -0.855543 174.677 -0.337717 181.034 2.78333C184.483 4.51653 186.782 7.11632 188.793 10.2939C190.517 13.1825 191.667 17.2267 191.667 21.2708H177.299C177.424 19.5169 177.229 17.7548 176.724 16.0712C176.149 14.6269 175.287 13.1825 173.851 12.6048C172.701 11.7382 170.977 11.1605 169.253 11.1605C167.529 11.1605 166.092 11.7382 164.943 12.3159C163.793 12.8937 162.931 14.0491 162.069 15.2046L161.494 19.2487C161.494 20.4042 161.782 21.5597 162.356 22.4263L164.655 24.7372C167.235 26.2818 169.924 27.6335 172.701 28.7813L181.034 32.8255C183.199 34.2594 185.136 36.0121 186.782 38.0251C188.793 39.7583 189.655 41.7803 190.805 43.8024C192.605 48.9619 192.503 54.5998 190.517 59.6901C189.368 62.001 187.931 64.0231 186.207 65.7563C184.115 67.5878 181.669 68.9647 179.023 69.8004C173.276 71.2447 176.149 75 170.402 75C164.655 75 166.667 70.667 161.207 69.8004C158.333 68.9338 155.46 67.4895 153.448 65.7563C151.19 63.7856 149.419 61.313 148.276 58.5346C147.126 55.6459 146.552 52.1795 146.552 48.4243H160.632C160.632 50.4463 160.632 52.1795 161.207 53.6239C161.494 55.0682 162.069 56.5125 162.931 57.3791C163.793 57.9569 164.943 58.8235 166.092 59.1123C167.529 59.6901 168.966 59.6901 170.402 59.6901C172.414 59.6901 173.851 59.6901 175 58.8235C176.149 57.9569 176.724 57.0903 177.299 55.9348C177.874 54.7793 178.161 53.335 178.161 51.8907V51.8907ZM239.943 28.7813V40.336H211.207V28.7813H239.943ZM215.23 0.761269V70.0893H201.437V0.761269H215.23ZM250 0.761269V70.0893H236.207V0.761269H250Z\"\n          fill=\"currentColor\"\n        />\n      </svg>\n    </a>\n  );\n}\n"
  },
  {
    "path": "www/components/Icons.tsx",
    "content": "export function IconMinus() {\n  return (\n    <svg\n      class=\"h-6 w-6\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      fill=\"none\"\n      viewBox=\"0 0 24 24\"\n      stroke=\"currentColor\"\n      aria-hidden=\"true\"\n    >\n      <path\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n        stroke-width=\"2\"\n        d=\"M20 12H4\"\n      />\n    </svg>\n  );\n}\n\nexport function IconPlus() {\n  return (\n    <svg\n      class=\"h-6 w-6\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      fill=\"none\"\n      viewBox=\"0 0 24 24\"\n      stroke=\"currentColor\"\n      aria-hidden=\"true\"\n    >\n      <path\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n        stroke-width=\"2\"\n        d=\"M12 6v6m0 0v6m0-6h6m-6 0H6\"\n      />\n    </svg>\n  );\n}\n\nexport function Leaf() {\n  return (\n    <svg width=\"24\" height=\"24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n      <path fill=\"#fff\" d=\"M0 0h24v24H0z\" />\n      <path\n        d=\"M2 10.276c.862-2.673 2.155-5.259 5.603-5.69\"\n        stroke=\"#6BA377\"\n        stroke-width=\"1.724\"\n      />\n      <path\n        d=\"M19.091 5.146c-4.44-5.697-10.945-2.374-13.643 0 2.081-1.662 3.006 4.747 4.163 9.733.925 3.987 8.71 6.409 12.486 7.121.848-3.244 1.434-11.157-3.006-16.854Z\"\n        fill=\"#25D24B\"\n      />\n    </svg>\n  );\n}\n\nexport function Copy() {\n  return (\n    <svg\n      class=\"h-4 w-4\"\n      viewBox=\"0 0 15 15\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M1.55566 2.7C1.55566 2.03726 2.09292 1.5 2.75566 1.5H8.75566C9.41841 1.5 9.95566 2.03726 9.95566 2.7V5.1H12.3557C13.0184 5.1 13.5557 5.63726 13.5557 6.3V12.3C13.5557 12.9627 13.0184 13.5 12.3557 13.5H6.35566C5.69292 13.5 5.15566 12.9627 5.15566 12.3V9.9H2.75566C2.09292 9.9 1.55566 9.36274 1.55566 8.7V2.7ZM6.35566 9.9V12.3H12.3557V6.3H9.95566V8.7C9.95566 9.36274 9.41841 9.9 8.75566 9.9H6.35566ZM8.75566 8.7V2.7L2.75566 2.7V8.7H8.75566Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\n// from https://heroicons.com/\nexport function Check() {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      class=\"h-4 w-4\"\n      fill=\"none\"\n      viewBox=\"0 0 24 24\"\n      stroke=\"currentColor\"\n      aria-hidden=\"true\"\n    >\n      <path\n        stroke-width={3}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        d=\"M5 13l4 4L19 7\"\n      />\n    </svg>\n  );\n}\n\n// from https://heroicons.com/\nexport function Info() {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      class=\"h-6 w-6\"\n      fill=\"none\"\n      viewBox=\"0 0 24 24\"\n      stroke=\"currentColor\"\n      stroke-width=\"2\"\n    >\n      <path\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n        d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n      />\n    </svg>\n  );\n}\n\nexport function GitHub(props: { class?: string }) {\n  return (\n    <svg\n      class={`h-6 w-6 ${props.class ?? \"\"}`}\n      fill=\"currentColor\"\n      viewBox=\"0 0 24 24\"\n      aria-hidden=\"true\"\n    >\n      <path\n        fillRule=\"evenodd\"\n        d=\"M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z\"\n        clipRule=\"evenodd\"\n      />\n    </svg>\n  );\n}\n\nexport function Discord(props: { class?: string }) {\n  return (\n    <svg\n      class={`h-6 w-6 ${props.class ?? \"\"}`}\n      fill=\"currentColor\"\n      viewBox=\"0 0 24 24\"\n      aria-hidden=\"true\"\n    >\n      <path\n        fill=\"currentColor\"\n        fillRule=\"evenodd\"\n        clipRule=\"evenodd\"\n        d=\"M20.16 4.50747C18.5996 3.79309 16.9523 3.28628 15.2603 3C15.0287 3.41393 14.8192 3.8398 14.6326 4.27584C12.8302 4.00424 10.9973 4.00424 9.19488 4.27584C9.00819 3.83984 8.79868 3.41398 8.56723 3C6.87405 3.2887 5.22569 3.79671 3.66374 4.51121C0.56287 9.099 -0.277728 13.5729 0.142571 17.9832C1.95852 19.3249 3.99108 20.3453 6.15191 21C6.63846 20.3456 7.069 19.6514 7.43896 18.9247C6.73628 18.6622 6.05807 18.3384 5.41219 17.957C5.58217 17.8337 5.74842 17.7067 5.90907 17.5834C7.78846 18.4673 9.83971 18.9255 11.9165 18.9255C13.9934 18.9255 16.0446 18.4673 17.924 17.5834C18.0865 17.7161 18.2528 17.8431 18.4209 17.957C17.7738 18.339 17.0943 18.6635 16.3904 18.9265C16.7599 19.6529 17.1905 20.3466 17.6774 21C19.8401 20.3479 21.8742 19.328 23.6905 17.9851C24.1837 12.8705 22.848 8.43773 20.16 4.50747ZM7.97134 15.2709C6.80011 15.2709 5.83248 14.208 5.83248 12.9004C5.83248 11.5928 6.76648 10.5205 7.9676 10.5205C9.16872 10.5205 10.1289 11.5928 10.1083 12.9004C10.0878 14.208 9.16499 15.2709 7.97134 15.2709ZM15.8617 15.2709C14.6886 15.2709 13.7248 14.208 13.7248 12.9004C13.7248 11.5928 14.6588 10.5205 15.8617 10.5205C17.0647 10.5205 18.0174 11.5928 17.9969 12.9004C17.9763 14.208 17.0554 15.2709 15.8617 15.2709Z\"\n      />\n    </svg>\n  );\n}\n\nexport function ArrowRight() {\n  return (\n    <svg\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M13.0833 14.4167L16.9166 10.5834C16.9999 10.5 17.0588 10.4098 17.0933 10.3125C17.1283 10.2153 17.1458 10.1112 17.1458 10C17.1458 9.88893 17.1283 9.78476 17.0933 9.68754C17.0588 9.59032 16.9999 9.50004 16.9166 9.41671L13.0624 5.56254C12.9096 5.40976 12.7221 5.33337 12.4999 5.33337C12.2777 5.33337 12.0833 5.41671 11.9166 5.58337C11.7638 5.73615 11.6874 5.9306 11.6874 6.16671C11.6874 6.40282 11.7638 6.59726 11.9166 6.75004L14.3333 9.16671H3.33325C3.09714 9.16671 2.89936 9.24643 2.73992 9.40587C2.57992 9.56587 2.49992 9.76393 2.49992 10C2.49992 10.2362 2.57992 10.4339 2.73992 10.5934C2.89936 10.7534 3.09714 10.8334 3.33325 10.8334H14.3333L11.8958 13.2709C11.743 13.4237 11.6666 13.6112 11.6666 13.8334C11.6666 14.0556 11.7499 14.25 11.9166 14.4167C12.0694 14.5695 12.2638 14.6459 12.4999 14.6459C12.736 14.6459 12.9305 14.5695 13.0833 14.4167Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n"
  },
  {
    "path": "www/components/NavigationBar.tsx",
    "content": "import ThemeToggle from \"../islands/ThemeToggle.tsx\";\nimport * as Icons from \"./Icons.tsx\";\n\nexport default function NavigationBar(\n  props: { active: string; class?: string },\n) {\n  const items = [\n    {\n      name: \"Docs\",\n      href: \"/docs\",\n    },\n    {\n      name: \"Showcase\",\n      href: \"/showcase\",\n    },\n    {\n      name: \"Blog\",\n      href: \"https://deno.com/blog?tag=fresh\",\n    },\n  ];\n  const isHome = props.active == \"/\";\n  const isDocs = props.active == \"/docs\";\n  return (\n    <nav class={\"flex \" + (props.class ?? \"\")} f-client-nav={false}>\n      <ul class=\"flex items-center gap-x-2 sm:gap-4 mx-4 my-2 sm:my-6 flex-wrap lg:mx-8 2xl:mr-1\">\n        {items.map((item) => (\n          <li key={item.name}>\n            <a\n              href={item.href}\n              class={`p-1 sm:p-2 ${\n                isHome\n                  ? \"text-green-900\"\n                  : isDocs\n                  ? \"text-foreground-secondary\"\n                  : \"text-gray-600\"\n              } hover:underline aria-[current]:font-bold`}\n            >\n              {item.name}\n            </a>\n          </li>\n        ))}\n\n        <li class=\"flex items-center\">\n          <a\n            href=\"https://github.com/denoland/fresh\"\n            class=\"hover:text-green-600 inline-block transition\"\n            aria-label=\"GitHub\"\n          >\n            <Icons.GitHub />\n          </a>\n        </li>\n        <li class=\"flex items-center\">\n          <a\n            href=\"https://discord.com/invite/deno\"\n            class=\"hover:text-green-600 inline-block transition\"\n            aria-label=\"Discord\"\n          >\n            <Icons.Discord />\n          </a>\n        </li>\n        {isDocs && (\n          <li class=\"flex items-center\">\n            <ThemeToggle />\n          </li>\n        )}\n      </ul>\n    </nav>\n  );\n}\n"
  },
  {
    "path": "www/components/PageSection.tsx",
    "content": "import type { JSX } from \"preact\";\n\nexport function PageSection(props: JSX.HTMLAttributes<HTMLDivElement>) {\n  return (\n    <section\n      id={props.id ?? \"\"}\n      class={`w-full max-w-screen-xl mx-auto my-16 md:my-24 lg:my-32 px-4 sm:px-8 lg:px-16 2xl:px-0 flex flex-col gap-8 md:gap-16 ${\n        props.class ?? \"\"\n      }`}\n    >\n      {props.children}\n    </section>\n  );\n}\n"
  },
  {
    "path": "www/components/Projects.tsx",
    "content": "import * as Icons from \"../components/Icons.tsx\";\nexport interface Project {\n  image: string;\n  title: string;\n  link: string;\n  github?: string;\n}\n\ninterface ProjectProps {\n  items: Project[];\n  class?: string;\n}\n\nexport default function Projects(props: ProjectProps) {\n  return (\n    <div\n      class={`pt-8 grid grid-cols-1 sm:grid-cols-3 items-center ${\n        props.class ?? \"\"\n      }`}\n    >\n      {props.items.filter((item) => item.link.length > 0).map((project) => (\n        <div class=\"w-full max-w-sm mx-auto group\">\n          <a href={project.link} tabIndex={-1}>\n            <img\n              loading=\"lazy\"\n              src={`/showcase/${project.image}1x.jpg`}\n              srcset={`/showcase/${project.image}2x.jpg 2x, /showcase/${project.image}1x.jpg 1x`}\n              alt={project.title}\n              width={600}\n              height={337}\n              style={{ aspectRatio: \"16/9\" }}\n              class=\"object-cover shadow-lg group-hover:shadow-xl group-hover:opacity-70 rounded-lg\"\n            />\n          </a>\n          <div class=\"mt-4 flex items-center\">\n            <div class=\"text-lg text-gray-600 flex-1 group-hover:text-underline\">\n              <a href={project.link}>{project.title}</a>\n            </div>\n            {project.github && (\n              <a\n                href={`https://github.com/${project.github}`}\n                class=\"ml-2 text-gray-500 hover:text-gray-700\"\n              >\n                <span class=\"sr-only\">GitHub</span>\n                <Icons.GitHub class=\"inline float-right h-6 w-6\" />\n              </a>\n            )}\n          </div>\n        </div>\n      ))}\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/components/SideBySide.tsx",
    "content": "import type { JSX } from \"preact/jsx-runtime\";\n\ntype ColumnConfiguration = \"1/1\" | \"2/3\" | \"3/2\";\n\ninterface SideBySideProps extends JSX.HTMLAttributes<HTMLDivElement> {\n  mdColSplit?: ColumnConfiguration;\n  lgColSplit?: ColumnConfiguration;\n  reverseOnDesktop?: boolean;\n}\n\nexport function SideBySide(props: SideBySideProps) {\n  let mdSplitClass = \"md:grid-cols-2\";\n  let lgSplitClass = \"lg:grid-cols-2\";\n\n  if (props.mdColSplit === \"2/3\") {\n    mdSplitClass = \"md:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]\";\n  } else if (props.mdColSplit === \"3/2\") {\n    mdSplitClass = \"md:grid-cols-[minmax(0,3fr)_minmax(0,2fr)]\";\n  }\n\n  if (props.lgColSplit === \"2/3\") {\n    lgSplitClass = \" lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]\";\n  } else if (props.lgColSplit === \"3/2\") {\n    lgSplitClass = \"lg:grid-cols-[minmax(0,3fr)_minmax(0,2fr)]\";\n  }\n\n  return (\n    <div\n      class={`grid grid-cols-1 items-center gap-12 md:gap-16 xl:gap-32 ${\n        props.reverseOnDesktop ? \"[&>*]:md:first:order-1\" : \"\"\n      } ${mdSplitClass} ${lgSplitClass} ${props.class ?? \"\"}`}\n    >\n      {props.children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/components/WaveTank.ts",
    "content": "export interface Spring {\n  p: number;\n  v: number;\n}\n\nexport class WaveTank {\n  springs = [] as Spring[];\n  waveLength = 100;\n  k = 0.02;\n  damping = 0.02;\n  spread = 0.02;\n\n  constructor() {\n    for (let i = 0; i < this.waveLength; i++) {\n      this.springs[i] = {\n        p: 0,\n        v: 0,\n      };\n    }\n  }\n\n  update(springs: Spring[]) {\n    for (const i of springs) {\n      const a = -this.k * i.p - this.damping * i.v;\n      i.p += i.v;\n      i.v += a;\n    }\n\n    const leftDeltas = [];\n    const rightDeltas = [];\n\n    for (let t = 0; t < 8; t++) {\n      for (let i = 0; i < springs.length; i++) {\n        const prev = springs[(i - 1 + springs.length) % springs.length];\n        const next = springs[(i + 1) % springs.length];\n\n        leftDeltas[i] = this.spread * (springs[i].p - prev.p);\n        rightDeltas[i] = this.spread * (springs[i].p - next.p);\n      }\n\n      for (let i = 0; i < springs.length; i++) {\n        const prev = springs[(i - 1 + springs.length) % springs.length];\n        const next = springs[(i + 1) % springs.length];\n        prev.v += leftDeltas[i];\n        next.v += rightDeltas[i];\n        prev.p += leftDeltas[i];\n        next.p += rightDeltas[i];\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "www/components/homepage/CTA.tsx",
    "content": "import { FancyLink } from \"../FancyLink.tsx\";\n\nexport function CTA() {\n  return (\n    <div class=\"relative mt-16\">\n      <div class=\"text-center bg-gradient-to-br from-blue-100 via-green-200 to-yellow-100 pb-32 md:pb-48 xl:pb-56 !my-0 before:w-full before:bg-white before:h-16 md:before:h-32 xl:before:h-48 before:[clip-path:polygon(0%_0%,_100%_0%,_0%_100%)] z-0 before:absolute before:top-0 before:left-0\">\n        <div class=\"flex flex-col gap-4\">\n          <img\n            src=\"/lemon.svg\"\n            alt=\"Illustration of a lemon sliced cleanly in half, suspended in midair as though frozen in time the instant after the cut, the juice flung from the edges\"\n            class=\"w-full max-w-48 sm:max-w-64 -mt-24 mx-auto mb-12 relative z-0\"\n          />\n          <h2 class=\"text-3xl sm:text-4xl md:text-5xl lg:text-6xl text-gray-600 font-extrabold\">\n            Time for a Fresh start\n          </h2>\n          <div class=\"flex flex-col justify-start items-center gap-4\">\n            <p class=\"text-xl text-balance max-w-prose\">\n              Jump right in and build your website with Fresh. Learn everything\n              you need to know in seconds.\n            </p>\n            <FancyLink\n              href=\"/docs/getting-started\"\n              class=\"mx-auto mt-4\"\n            >\n              Get started\n            </FancyLink>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/CodeExampleBox.tsx",
    "content": ""
  },
  {
    "path": "www/components/homepage/DemoBox.tsx",
    "content": "import type { JSX } from \"preact\";\n\ninterface DemoBoxProps extends JSX.HTMLAttributes<HTMLDivElement> {\n  flip?: boolean;\n}\n\nexport function DemoBox(props: DemoBoxProps) {\n  const outerFlip = props.flip ? \"-skew-y-2 -skew-x-3\" : \"skew-y-2 skew-x-3\";\n  const innerFlip = props.flip ? \"skew-y-2 skew-x-3\" : \"-skew-y-2 -skew-x-3\";\n  return (\n    <div\n      class={`bg-gradient-to-br font-medium from-blue-200 via-green-300 to-yellow-200 p-8 py-12 text-center items-center flex justify-center ${outerFlip}`}\n    >\n      <div class={`w-full ${innerFlip}`}>\n        {props.children}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/DenoSection.tsx",
    "content": "import { PageSection } from \"../PageSection.tsx\";\nimport { FancyLink } from \"../FancyLink.tsx\";\nimport { SideBySide } from \"../SideBySide.tsx\";\nimport { SectionHeading } from \"../homepage/SectionHeading.tsx\";\n\nexport function DenoSection() {\n  return (\n    <PageSection>\n      <SideBySide reverseOnDesktop>\n        <img\n          src=\"/illustration/lemon-squash.svg\"\n          class=\"w-full h-auto mx-auto max-w-[24rem] mt-8 mb-4\"\n          alt=\"Deno is drinking Fresh lemon squash\"\n        />\n        <div class=\"flex flex-col gap-4\">\n          <SectionHeading>\n            Built on Deno\n          </SectionHeading>\n          <p>\n            Deno is the next evolution of server-side JavaScript, with stronger\n            security, a robust built-in toolchain, and zero-config TypeScript\n            support. (It's faster than Node, too.)\n          </p>\n          <FancyLink\n            href=\"https://deno.com\"\n            class=\"mt-4\"\n          >\n            Learn more about Deno\n          </FancyLink>\n        </div>\n      </SideBySide>\n    </PageSection>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/ExampleArrow.tsx",
    "content": "import type { JSX } from \"preact\";\n\nexport function ExampleArrow(\n  props: JSX.HTMLAttributes<HTMLDivElement>,\n) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      width=\"100%\"\n      height=\"100%\"\n      viewBox=\"0 0 137 291\"\n      version=\"1.1\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      xml:space=\"preserve\"\n      style=\"fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;\"\n      class={`w-12 -my-8 relative z-10 mx-auto ${props?.class ?? \"\"}`}\n    >\n      <path\n        d=\"M50.704,18c46.89,80.967 38.288,189.344 5.941,254.255\"\n        style=\"fill:none;stroke:#fff;stroke-width:36px;\"\n      />\n      <path\n        d=\"M18,207.944c13.297,11.903 33.957,43.376 38.645,64.311c19.034,-17.758 43.173,-31.125 62.237,-36.621\"\n        style=\"fill:none;stroke:#fff;stroke-width:36px;\"\n      />\n      <path\n        d=\"M50.704,18c47.238,81.059 38.288,189.344 5.941,254.255\"\n        style=\"fill:none;stroke-width:18px;\"\n        class=\"stroke-gray-600\"\n      />\n      <path\n        d=\"M18,207.944c13.044,11.646 33.957,43.376 38.645,64.311c19.034,-17.758 43.173,-31.125 62.237,-36.621\"\n        style=\"fill:none;stroke-width:18px;\"\n        class=\"stroke-gray-600\"\n      />\n    </svg>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/FormsSection.tsx",
    "content": "import { PageSection } from \"../PageSection.tsx\";\nimport { SideBySide } from \"../SideBySide.tsx\";\nimport { CodeWindow } from \"../CodeWindow.tsx\";\nimport { CodeBlock } from \"../CodeBlock.tsx\";\nimport { SectionHeading } from \"../homepage/SectionHeading.tsx\";\nimport { DemoBox } from \"../homepage/DemoBox.tsx\";\nimport { ExampleArrow } from \"../homepage/ExampleArrow.tsx\";\nimport { FancyLink } from \"../FancyLink.tsx\";\nimport { FormSubmitDemo } from \"../../islands/FormSubmitDemo.tsx\";\n\nconst routingCode = `import { Handlers } from \"$fresh/server.ts\";\n\nexport const handler: Handlers = {\n  async POST(req) {\n    const form = await req.formData();\n\n    // Do something with the form data here,\n    // then redirect user to thank you page\n\n    const headers = new Headers();\n    headers.set(\"location\", \"/thanks\");\n    return new Response(null, {\n      status: 303,\n      headers,\n    });\n  },\n};`;\n\nexport function FormsSection() {\n  return (\n    <PageSection id=\"forms-section\">\n      <SideBySide mdColSplit=\"2/3\" lgColSplit=\"2/3\" class=\"!items-start\">\n        <div class=\"flex flex-col gap-4 md:sticky md:top-4\">\n          <div class=\"leading-none text-[4rem]\">\n            <svg\n              aria-hidden=\"true\"\n              xmlns=\"http://www.w3.org/2000/svg\"\n              class=\"icon icon-tabler icon-tabler-route-square-2 text-fresh\"\n              width=\"4rem\"\n              height=\"4rem\"\n              viewBox=\"0 0 24 24\"\n              stroke-width=\"1.5\"\n              stroke=\"currentColor\"\n              fill=\"none\"\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n            >\n              <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n              <path d=\"M14 5a2 2 0 0 0 -2 2v10a2 2 0 0 1 -2 2\" />\n              <path d=\"M3 17h4v4h-4z\" />\n              <path d=\"M17 3h4v4h-4z\" />\n            </svg>\n          </div>\n          <SectionHeading>\n            Forms, the right way\n          </SectionHeading>\n          <p>\n            Don't fight the browser. Fresh helps you handle form submissions and\n            other dynamic requests server-side, from any route.\n          </p>\n          <p>\n            Since Fresh is built on{\" \"}\n            <a href=\"https://deno.com\" class=\"underline\">Deno</a>, it's built on\n            web standards.\n          </p>\n          <FancyLink href=\"/docs/advanced/forms\" class=\"mt-2\">\n            Forms in Fresh\n          </FancyLink>\n        </div>\n        <div class=\"flex flex-col gap-4\">\n          <CodeWindow name=\"routes/index.tsx\">\n            <CodeBlock\n              code={routingCode}\n              lang=\"jsx\"\n            />\n          </CodeWindow>\n          <ExampleArrow />\n          <DemoBox>\n            <FormSubmitDemo />\n          </DemoBox>\n        </div>\n      </SideBySide>\n    </PageSection>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/Hero.tsx",
    "content": "import { FancyLink } from \"../../components/FancyLink.tsx\";\nimport LemonTop from \"../../islands/LemonTop.tsx\";\nimport LemonBottom from \"../../islands/LemonBottom.tsx\";\nimport { CopyButton } from \"../CopyButton.tsx\";\n\nexport function Hero() {\n  return (\n    <>\n      <div class=\"bg-green-300 mt-0 pt-32 md:pt-48 !mb-0 bg-gradient-to-br from-blue-100 via-green-200 to-yellow-100\">\n        <div class=\"md:grid grid-cols-5 gap-8 md:gap-16 items-center w-full max-w-screen-xl mx-auto px-4 md:px-8 lg:px-16 2xl:px-0\">\n          <div class=\"flex-1 text-center md:text-left md:col-span-3 pb-8 md:pb-32\">\n            <h2 class=\"text-[calc(1rem+4vw)] leading-tight sm:text-5xl lg:text-6xl sm:tracking-tight sm:leading-none font-extrabold\">\n              The simple, approachable, productive web framework\n            </h2>\n            <div class=\"mt-12 flex flex-wrap justify-center items-stretch md:justify-start gap-4\">\n              <FancyLink href=\"/docs/getting-started\">Get started</FancyLink>\n              <CopyArea code={`deno run -Ar jsr:@fresh/init`} />\n            </div>\n          </div>\n          <div class=\"md:col-span-2 flex justify-center items-end\">\n            <LemonTop />\n          </div>\n        </div>\n      </div>\n      <LemonBottom />\n    </>\n  );\n}\n\nfunction CopyArea(props: { code: string }) {\n  return (\n    <div class=\"bg-slate-800 rounded-sm text-green-100 flex items-center min-w-0 overflow-x-auto\">\n      <pre class=\"overflow-x-auto w-full flex-1 px-6 py-4\">\n        {props.code}\n      </pre>\n\n      <CopyButton code={props.code} />\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/IslandsSection.tsx",
    "content": "import Counter from \"../../islands/Counter.tsx\";\nimport { CodeBlock } from \"../../components/CodeBlock.tsx\";\nimport { CodeWindow } from \"../../components/CodeWindow.tsx\";\nimport { PageSection } from \"../../components/PageSection.tsx\";\nimport { SideBySide } from \"../../components/SideBySide.tsx\";\nimport { SectionHeading } from \"../../components/homepage/SectionHeading.tsx\";\nimport { DemoBox } from \"../../components/homepage/DemoBox.tsx\";\nimport { ExampleArrow } from \"../../components/homepage/ExampleArrow.tsx\";\nimport { FancyLink } from \"../../components/FancyLink.tsx\";\n\nconst islandCode = `import { useSignal } from \"@preact/signals\";\n\nexport default function Counter(props) {\n  const count = useSignal(props.start);\n\n  return (\n    <div>\n      <h3>Interactive island</h3>\n      <p>The server supplied the initial value of {props.start}.</p>\n      <div>\n        <button onClick={() => count.value -= 1}>-</button>\n        <div>{count}</div>\n        <button onClick={() => count.value += 1}>+</button>\n      </div>\n    </div>\n  );\n}`;\n\nexport function IslandsSection() {\n  return (\n    <PageSection>\n      <SideBySide\n        mdColSplit=\"3/2\"\n        lgColSplit=\"3/2\"\n        reverseOnDesktop\n        class=\"!items-start\"\n      >\n        <div class=\"flex flex-col gap-4 md:sticky md:top-4\">\n          <svg\n            aria-hidden=\"true\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            class=\"icon icon-tabler icon-tabler-beach text-fresh\"\n            width=\"4rem\"\n            height=\"4rem\"\n            viewBox=\"0 0 24 24\"\n            stroke-width=\"1.5\"\n            stroke=\"currentColor\"\n            fill=\"none\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n            <path d=\"M17.553 16.75a7.5 7.5 0 0 0 -10.606 0\" />\n            <path d=\"M18 3.804a6 6 0 0 0 -8.196 2.196l10.392 6a6 6 0 0 0 -2.196 -8.196z\" />\n            <path d=\"M16.732 10c1.658 -2.87 2.225 -5.644 1.268 -6.196c-.957 -.552 -3.075 1.326 -4.732 4.196\" />\n            <path d=\"M15 9l-3 5.196\" />\n            <path d=\"M3 19.25a2.4 2.4 0 0 1 1 -.25a2.4 2.4 0 0 1 2 1a2.4 2.4 0 0 0 2 1a2.4 2.4 0 0 0 2 -1a2.4 2.4 0 0 1 2 -1a2.4 2.4 0 0 1 2 1a2.4 2.4 0 0 0 2 1a2.4 2.4 0 0 0 2 -1a2.4 2.4 0 0 1 2 -1a2.4 2.4 0 0 1 1 .25\" />\n          </svg>\n          <SectionHeading>Island-based architecture</SectionHeading>\n          <p>\n            Fresh ships plain HTML to the client, then hydrates with JavaScript\n            only where needed.\n          </p>\n          <p>\n            Because it's Preact, you get best-in-class performance, plus the\n            convenience of{\" \"}\n            <a href=\"https://preactjs.com/guide/v10/signals/\" class=\"underline\">\n              Signals\n            </a>.\n          </p>\n          <FancyLink href=\"/docs/concepts/islands\" class=\"mt-2\">\n            Learn more about islands\n          </FancyLink>\n        </div>\n        <div class=\"flex flex-col gap-4 relative\">\n          <CodeWindow name=\"islands/Counter.tsx\">\n            <CodeBlock\n              code={islandCode}\n              lang=\"jsx\"\n            />\n          </CodeWindow>\n          <ExampleArrow class=\"[transform:rotateY(-180deg)]\" />\n          <DemoBox flip>\n            <Counter start={3} />\n          </DemoBox>\n        </div>\n      </SideBySide>\n    </PageSection>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/PartialsSection.tsx",
    "content": "import { CodeBlock } from \"../../components/CodeBlock.tsx\";\nimport { CodeWindow } from \"../../components/CodeWindow.tsx\";\nimport { PageSection } from \"../../components/PageSection.tsx\";\nimport { SideBySide } from \"../../components/SideBySide.tsx\";\nimport { SectionHeading } from \"../../components/homepage/SectionHeading.tsx\";\nimport { DemoBox } from \"../../components/homepage/DemoBox.tsx\";\nimport { ExampleArrow } from \"../../components/homepage/ExampleArrow.tsx\";\nimport { RecipeDemo } from \"../../components/homepage/RecipeDemo.tsx\";\nimport { FancyLink } from \"../../components/FancyLink.tsx\";\n\nconst islandCode = `import { Partial } from \"$fresh/runtime.ts\";\n\nexport const Recipes = () => (\n  <div f-client-nav>\n    <aside>\n      <button f-partial=\"/recipes/lemonade\">\n        Lemonade\n      </button>\n      <button f-partial=\"/recipes/lemon-honey-tea\">\n        Lemon-honey tea\n      </button>\n      <button f-partial=\"/recipes/lemondrop\">\n        Lemondrop Martini\n      </button>\n    </aside>\n    <main>\n      <Partial name=\"recipe\">\n        Click a recipe to stream HTML into this spot\n      </Partial>\n    </main>\n  </div>\n);`;\n\nexport function PartialsSection() {\n  return (\n    <PageSection>\n      <SideBySide\n        mdColSplit=\"3/2\"\n        lgColSplit=\"3/2\"\n        reverseOnDesktop\n        class=\"!items-start\"\n      >\n        <div class=\"flex flex-col gap-4 md:sticky md:top-4\">\n          <svg\n            aria-hidden=\"true\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            class=\"icon icon-tabler icon-tabler-arrows-transfer-down text-fresh\"\n            width=\"4rem\"\n            height=\"4rem\"\n            viewBox=\"0 0 24 24\"\n            stroke-width=\"1.5\"\n            stroke=\"currentColor\"\n            fill=\"none\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <title>Arrows transferring data down</title>\n            <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n            <path d=\"M17 3v6\" />\n            <path d=\"M10 18l-3 3l-3 -3\" />\n            <path d=\"M7 21v-18\" />\n            <path d=\"M20 6l-3 -3l-3 3\" />\n            <path d=\"M17 21v-2\" />\n            <path d=\"M17 15v-2\" />\n          </svg>\n\n          <SectionHeading>Stream HTML straight from the server</SectionHeading>\n          <p>\n            Fresh Partials let you fetch HTML and slot it directly into the\n            page, without a full page reload—perfect for interactive elements\n            and dynamic apps.\n          </p>\n          <FancyLink href=\"/docs/advanced/partials\" class=\"mt-4\">\n            Learn more about Partials\n          </FancyLink>\n        </div>\n        <div class=\"flex flex-col gap-4\">\n          <CodeWindow name=\"components/Recipes.tsx\">\n            <CodeBlock\n              code={islandCode}\n              lang=\"jsx\"\n            />\n          </CodeWindow>\n          <ExampleArrow class=\"[transform:rotateY(-180deg)]\" />\n          <DemoBox flip>\n            <RecipeDemo />\n          </DemoBox>\n        </div>\n      </SideBySide>\n    </PageSection>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/RecipeDemo.tsx",
    "content": "import { Partial } from \"fresh/runtime\";\n\nexport const RecipeDemo = () => (\n  <div\n    f-client-nav\n    class=\"w-full grid grid-cols-1 md:grid-cols-[auto_1fr] text-left gap-6 items-stretch min-h-64\"\n  >\n    <aside class=\"flex flex-col gap-2 underline p-6 pl-0 border-b md:border-r md:border-b-0 border-current text-left items-start justify-center\">\n      <button type=\"button\" f-partial=\"/recipes/lemonade\">\n        Lemonade\n      </button>\n      <button type=\"button\" f-partial=\"/recipes/lemon-honey-tea\">\n        Lemon-honey tea\n      </button>\n      <button type=\"button\" f-partial=\"/recipes/lemondrop\">\n        Lemondrop Martini\n      </button>\n    </aside>\n    <main class=\"w-full flex flex-col justify-center\">\n      <Partial name=\"recipe\">\n        Click a recipe to stream HTML into this spot\n      </Partial>\n    </main>\n  </div>\n);\n"
  },
  {
    "path": "www/components/homepage/RenderingSection.tsx",
    "content": "import { PageSection } from \"../../components/PageSection.tsx\";\nimport { SideBySide } from \"../../components/SideBySide.tsx\";\nimport { CodeWindow } from \"../../components/CodeWindow.tsx\";\nimport { CodeBlock } from \"../../components/CodeBlock.tsx\";\nimport { SectionHeading } from \"../../components/homepage/SectionHeading.tsx\";\nimport { DemoBox } from \"../../components/homepage/DemoBox.tsx\";\nimport { ExampleArrow } from \"../../components/homepage/ExampleArrow.tsx\";\n\nconst serverCode = `export default function HomePage() {\n  const time = new Date().toLocaleString();\n  return (\n    <p>Freshly server-rendered {time}</p>\n  );\n}`;\n\nexport function RenderingSection() {\n  return (\n    <PageSection>\n      <SideBySide mdColSplit=\"2/3\" lgColSplit=\"2/3\" class=\"!items-start\">\n        <div class=\"flex flex-col gap-4 md:sticky md:top-4\">\n          <svg\n            aria-hidden=\"true\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n            class=\"icon icon-tabler icon-tabler-atom text-fresh\"\n            width=\"4rem\"\n            height=\"4rem\"\n            viewBox=\"0 0 24 24\"\n            stroke-width=\"1.5\"\n            stroke=\"currentColor\"\n            fill=\"none\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n            role=\"img\"\n          >\n            <title>Preact icon</title>\n            <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n            <path d=\"M12 12v.01\" />\n            <path d=\"M19.071 4.929c-1.562 -1.562 -6 .337 -9.9 4.243c-3.905 3.905 -5.804 8.337 -4.242 9.9c1.562 1.561 6 -.338 9.9 -4.244c3.905 -3.905 5.804 -8.337 4.242 -9.9\" />\n            <path d=\"M4.929 4.929c-1.562 1.562 .337 6 4.243 9.9c3.905 3.905 8.337 5.804 9.9 4.242c1.561 -1.562 -.338 -6 -4.244 -9.9c-3.905 -3.905 -8.337 -5.804 -9.9 -4.242\" />\n          </svg>\n          <SectionHeading>\n            Build fast apps fast\n          </SectionHeading>\n          <p>\n            Fresh routes are dynamically server-rendered{\" \"}\n            <a href=\"https://preactjs.com/\" class=\"underline\">Preact</a>{\" \"}\n            components, so there's zero JavaScript shipped to the browser by\n            default.\n          </p>\n          <p>Simple to write; fast to run.</p>\n        </div>\n        <div class=\"flex flex-col gap-4\">\n          <CodeWindow name=\"routes/index.tsx\">\n            <CodeBlock code={serverCode} lang=\"jsx\" />\n          </CodeWindow>\n          <ExampleArrow class=\"ml-[55%]\" />{\" \"}\n          <DemoBox>\n            <p>\n              Freshly server-rendered {new Date().toLocaleString(\"default\", {\n                dateStyle: \"medium\",\n                timeStyle: \"medium\",\n              })} UTC\n            </p>\n          </DemoBox>\n        </div>\n      </SideBySide>\n    </PageSection>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/SectionHeading.tsx",
    "content": "import type { ComponentChildren } from \"preact\";\n\nexport function SectionHeading(\n  { children }: { children: ComponentChildren },\n) {\n  return (\n    <h2 class=\"text-3xl lg:text-4xl xl:text-5xl text-gray-600 font-[750] text-balance\">\n      {children}\n    </h2>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/Simple.tsx",
    "content": "import { PageSection } from \"../../components/PageSection.tsx\";\n\nexport function Simple() {\n  return (\n    <PageSection class=\"!mb-12 !mt-20\">\n      <div class=\"text-center max-w-max mx-auto flex flex-col gap-4\">\n        <p class=\"italic text-gray-500 text-lg\">Introducing Fresh:</p>\n        <h2 class=\"text-4xl sm:text-5xl md:text-6xl lg:text-7xl sm:tracking-tight font-extrabold text-balance text-gray-600\">\n          The framework so simple, you already know it.\n        </h2>\n        <p class=\"text-xl text-balance max-w-prose mx-auto\">\n          Fresh is designed to be easy to use by building on the best well-known\n          tools, conventions, and web standards.\n        </p>\n      </div>\n    </PageSection>\n  );\n}\n"
  },
  {
    "path": "www/components/homepage/SocialProof.tsx",
    "content": "import { PageSection } from \"../../components/PageSection.tsx\";\nimport { FancyLink } from \"../../components/FancyLink.tsx\";\nimport { DemoBox } from \"../../components/homepage/DemoBox.tsx\";\n\nexport function SocialProof() {\n  return (\n    <PageSection>\n      <div class=\"text-center max-w-max mx-auto flex flex-col gap-4\">\n        <h2 class=\"text-gray-600 text-4xl sm:text-5xl md:text-6xl lg:text-7xl sm:tracking-tight sm:leading-[1.1] font-extrabold text-balance\">\n          Built for the edge\n        </h2>\n        <p class=\"text-xl text-balance max-w-prose mx-auto\">\n          Fresh is the secret sauce behind production-grade, enterprise-ready\n          software like{\" \"}\n          <a href=\"https://deco.cx\" class=\"underline\">Deco.cx</a>, Brazil's top\n          eCommerce platform\n        </p>\n      </div>\n      <a href=\"https://deno.com/blog/deco-cx-subhosting-serve-their-clients-storefronts-fast\">\n        <img\n          src=\"/showcase/deco.webp\"\n          alt=\"Deco CX\"\n          class=\"mx-auto\"\n          loading=\"lazy\"\n        />\n      </a>\n      <div class=\"flex flex-col gap-8 items-center justify-center -mt-8 md:-mt-16 lg:-mt-32\">\n        <DemoBox flip>\n          <blockquote class=\"text-center italic text-lg sm:text-xl md:text-2xl lg:text-2xl xl:text-3xl max-w-screen-md text-gray-700 text-balance font-normal border-l-yellow-300 mx-auto my-4\">\n            <span class=\"font-semibold inline-block transform scale-150 relative -left-3 top-1 leading-none\">\n              “\n            </span>The team also used{\" \"}\n            <b>Fresh</b>, a next-gen Deno-native full stack web framework that\n            sends zero JavaScript to the client, for its modern developer\n            experience and snappy performance…<br />\n            <br />This stack unlocked{\" \"}\n            <b>\n              5x faster page load speeds and a 30% jump in conversion rates\n            </b>{\" \"}\n            for their\n            clients.<span class=\"font-semibold inline-block transform scale-150 relative -right-2 top-1 leading-none\">\n              ”\n            </span>\n          </blockquote>\n        </DemoBox>\n        <FancyLink\n          href=\"https://deno.com/blog/deco-cx-subhosting-serve-their-clients-storefronts-fast\"\n          class=\"mt-8\"\n        >\n          Read the case study\n        </FancyLink>\n      </div>\n    </PageSection>\n  );\n}\n"
  },
  {
    "path": "www/data/docs.ts",
    "content": "import toc from \"../../docs/toc.ts\";\n\nexport interface TableOfContentsEntry {\n  slug: string;\n  title: string;\n  category?: string;\n  href: string;\n  file: string;\n}\n\nexport interface TableOfContentsCategory {\n  title: string;\n  href: string;\n  entries: TableOfContentsCategoryEntry[];\n}\n\nexport interface TableOfContentsCategoryEntry {\n  title: string;\n  href: string;\n}\n\nexport const TABLE_OF_CONTENTS: Record<\n  string,\n  Record<string, TableOfContentsEntry>\n> = {};\nexport const CATEGORIES: Record<string, TableOfContentsCategory[]> = {};\n\nexport const VERSIONS = Object.keys(toc);\nexport const CANARY_VERSION = toc.canary ? \"canary\" : \"\";\nexport const LATEST_VERSION =\n  VERSIONS.find((version) => version !== \"canary\") ?? \"\";\n\nfor (const version in toc) {\n  const RAW_VERSION = toc[version];\n  const versionSlug = version === LATEST_VERSION ? \"\" : `/${version}`;\n  TABLE_OF_CONTENTS[version] = {};\n  CATEGORIES[version] = [];\n\n  for (const parent in RAW_VERSION.content) {\n    const rawEntry = RAW_VERSION.content[parent];\n\n    // Allow versioned documentation to stack on each other. This should\n    // only be used for canary versions. This avoids having us to copy\n    // all documentation content and backport changes.\n    const fileVersion = rawEntry.link ?? version;\n    const versionFilePath = fileVersion === LATEST_VERSION\n      ? \"/latest\"\n      : `/${fileVersion}`;\n\n    const href = `/docs${versionSlug}/${parent}`;\n    const file = `docs${versionFilePath}/${parent}/index.md`;\n\n    const entry = {\n      slug: parent,\n      title: rawEntry.title,\n      href,\n      file,\n    };\n    TABLE_OF_CONTENTS[version][parent] = entry;\n    const category: TableOfContentsCategory = {\n      title: rawEntry.title,\n      href,\n      entries: [],\n    };\n    CATEGORIES[version].push(category);\n    if (rawEntry.pages) {\n      for (const [id, title, linkedVersion] of rawEntry.pages) {\n        const slug = `${parent}/${id}`;\n\n        // Allow stacked documentation\n        const pageVersion = linkedVersion\n          ? linkedVersion.slice(\"link:\".length)\n          : version;\n        const versionFilePath = !pageVersion || pageVersion === LATEST_VERSION\n          ? \"/latest\"\n          : `/${pageVersion}`;\n\n        const href = `/docs${versionSlug}/${slug}`;\n\n        const file = `docs${versionFilePath}/${slug}.md`;\n        const entry = { slug, title, category: parent, href, file };\n        TABLE_OF_CONTENTS[version][slug] = entry;\n        category.entries.push({\n          title,\n          href,\n        });\n      }\n    }\n  }\n}\n\nexport function getFirstPageUrl(version: string) {\n  const group = TABLE_OF_CONTENTS[version];\n  if (group) {\n    for (const slug in group) {\n      return group[slug].href;\n    }\n  }\n\n  throw new Error(`Could not find version \"${version}\"`);\n}\n"
  },
  {
    "path": "www/data/showcase.json",
    "content": "[\n  {\n    \"title\": \"JSR\",\n    \"link\": \"https://jsr.io/\",\n    \"github\": \"jsr-io/jsr\",\n    \"image\": \"jsr\"\n  },\n  {\n    \"title\": \"Deno.com\",\n    \"link\": \"https://deno.com/\",\n    \"image\": \"deno\"\n  },\n  {\n    \"title\": \"OpenAI Semantic Search\",\n    \"link\": \"https://supabase-openai-doc-search.deno.dev/\",\n    \"github\": \"supabase-community/deno-fresh-openai-doc-search\",\n    \"image\": \"openai-semantic-search\"\n  },\n  {\n    \"title\": \"Deno SaaSKit\",\n    \"link\": \"https://saaskit.deno.dev/\",\n    \"github\": \"denoland/saaskit\",\n    \"image\": \"deno-saaskit\"\n  },\n  {\n    \"title\": \"Shoaib Hossain Portfolio\",\n    \"link\": \"https://sihab.deno.dev\",\n    \"github\": \"mrsihab/fresh-portfolio\",\n    \"image\": \"mrsihab\"\n  },\n  {\n    \"title\": \"Deno Merch\",\n    \"link\": \"https://merch.deno.com/\",\n    \"github\": \"denoland/merch\",\n    \"image\": \"merch\"\n  },\n  {\n    \"title\": \"Fresh Website\",\n    \"link\": \"https://fresh.deno.dev/\",\n    \"github\": \"denoland/fresh/tree/main/www\",\n    \"image\": \"fresh\"\n  },\n  {\n    \"title\": \"Video Poker Academy\",\n    \"link\": \"https://videopoker-academy.deno.dev/\",\n    \"github\": \"hyprtxt/videopoker.academy\",\n    \"image\": \"videopoker-academy\"\n  },\n  {\n    \"title\": \"D3no Data\",\n    \"link\": \"https://d3nodata.deno.dev/\",\n    \"github\": \"D3nosaurs/d3nodata-website\",\n    \"image\": \"d3nodata\"\n  },\n  {\n    \"title\": \"Fresh Hacker News\",\n    \"link\": \"https://fresh-hacker-news.deno.dev/\",\n    \"github\": \"morinokami/fresh-hacker\",\n    \"image\": \"hn\"\n  },\n  {\n    \"title\": \"LDkit\",\n    \"link\": \"https://ldkit.io\",\n    \"github\": \"karelklima/ldkit\",\n    \"image\": \"ldkit\"\n  },\n  {\n    \"title\": \"CGPA(AU R-2021) Calculator\",\n    \"link\": \"https://cgpa-au-2021.deno.dev\",\n    \"github\": \"codesculpture/cgpa-au-2021\",\n    \"image\": \"cgpa-au-2021\"\n  },\n  {\n    \"title\": \"diKnow\",\n    \"link\": \"https://xyntechx-diknow.deno.dev/\",\n    \"github\": \"xyntechx/diKnow\",\n    \"image\": \"diKnow\"\n  },\n  {\n    \"title\": \"Fresh Strapi\",\n    \"link\": \"https://fresh-strapi.deno.dev/\",\n    \"github\": \"hyprtxt/fresh-strapi.deno.dev\",\n    \"image\": \"fresh-strapi\"\n  },\n  {\n    \"title\": \"Portfolio Page\",\n    \"link\": \"http://adamsobotka.deno.dev/\",\n    \"github\": \"vorcigernix/adamsobotka\",\n    \"image\": \"adam-portfolio\"\n  },\n  {\n    \"title\": \"Developer Portfolio\",\n    \"link\": \"https://mooxl.dev\",\n    \"github\": \"mooxl/fresh.mooxl.dev\",\n    \"image\": \"mooxl\"\n  },\n  {\n    \"title\": \"Developer Portfolio\",\n    \"link\": \"https://guillaumecomte.deno.dev\",\n    \"github\": \"guigui64/www\",\n    \"image\": \"guigui64\"\n  },\n  {\n    \"title\": \"Fresh Project Manager\",\n    \"link\": \"https://xyntechx-project-manager.deno.dev/\",\n    \"github\": \"xyntechx/NexLiber-Projects/tree/main/Fresh%20Project%20Manager\",\n    \"image\": \"fresh-project-manager\"\n  },\n  {\n    \"title\": \"Fresh ToDo List App\",\n    \"link\": \"https://fresh-todo-list.deno.dev/\",\n    \"github\": \"MrSaeedNasiri/fresh-todo-list\",\n    \"image\": \"fresh-todo\"\n  },\n  {\n    \"title\": \"Optimem app\",\n    \"link\": \"https://optimem.org/\",\n    \"image\": \"optimem\"\n  },\n  {\n    \"title\": \"Balello\",\n    \"link\": \"https://balello.com\",\n    \"image\": \"balello\"\n  },\n  {\n    \"title\": \"Wricord\",\n    \"link\": \"https://wricord.com\",\n    \"image\": \"wricord\"\n  },\n  {\n    \"title\": \"ANDBOUNDS Inc.\",\n    \"link\": \"https://andbounds.com/\",\n    \"image\": \"andbounds\"\n  },\n  {\n    \"title\": \"YCRM\",\n    \"link\": \"https://ycrm.xyz/\",\n    \"image\": \"ycrm\"\n  },\n  {\n    \"title\": \"Local SEO Studio\",\n    \"link\": \"https://www.localseo.studio/\",\n    \"image\": \"localseostudio\"\n  },\n  {\n    \"title\": \"Fondest Landing Page\",\n    \"link\": \"https://fondest.com\",\n    \"image\": \"fondest\"\n  },\n  {\n    \"title\": \"im@sparql Data Dashboard\",\n    \"link\": \"https://imasparql-data-dashboard.deno.dev/\",\n    \"github\": \"arrow2nd/imasparql-data-dashboard\",\n    \"image\": \"imasparql-dd\"\n  },\n  {\n    \"title\": \"Craig’s Deno Diary\",\n    \"link\": \"https://deno-blog.com\",\n    \"github\": \"cdoremus/deno-blog\",\n    \"image\": \"deno-diary\"\n  },\n  {\n    \"title\": \"Melon\",\n    \"link\": \"https://m.not-ivy.dev/\",\n    \"github\": \"not-ivy/melon\",\n    \"image\": \"melon\"\n  },\n  {\n    \"title\": \"Webhook Manager\",\n    \"link\": \"https://webhook-demo.deno.dev/\",\n    \"github\": \"avnigenc/deno-fresh-webhook-demo\",\n    \"image\": \"webhook-manager\"\n  },\n  {\n    \"title\": \"Deno Jobs\",\n    \"link\": \"https://vacancy.deno.dev/\",\n    \"github\": \"VofSwords/deno-jobs\",\n    \"image\": \"deno-jobs\"\n  },\n  {\n    \"title\": \"java.minidoodle\",\n    \"link\": \"https://javaminidoodle.de/\",\n    \"image\": \"javaminidoodle\"\n  },\n  {\n    \"title\": \"Ppaste\",\n    \"link\": \"https://ppaste.deno.dev/\",\n    \"github\": \"n4ze3m/p-paste\",\n    \"image\": \"ppaste\"\n  },\n  {\n    \"title\": \"Linksapp\",\n    \"link\": \"https://linksapp.deno.dev/\",\n    \"github\": \"commune-org/linksapp-fresh\",\n    \"image\": \"linksapp\"\n  },\n  {\n    \"title\": \"Grape Chat\",\n    \"link\": \"https://grape-chat.miqsel.net/\",\n    \"github\": \"yahiro07/grape-chat\",\n    \"image\": \"grape-chat\"\n  },\n  {\n    \"title\": \"Fresh Notion Blog\",\n    \"link\": \"https://fresh-notion-blog.deno.dev/\",\n    \"github\": \"xijaja/fresh-notion-blog\",\n    \"image\": \"fresh-notion-blog\"\n  },\n  {\n    \"title\": \"Denopaste\",\n    \"link\": \"https://denopaste.com/\",\n    \"github\": \"stephenmelnicki/denopaste\",\n    \"image\": \"denopaste\"\n  },\n  {\n    \"title\": \"m33t\",\n    \"link\": \"https://m33t.deno.dev/\",\n    \"github\": \"dunkbing/m33t\",\n    \"image\": \"m33t\"\n  },\n  {\n    \"title\": \"YAML to TypeScript\",\n    \"link\": \"https://yaml-to-ts.deno.dev/\",\n    \"github\": \"jiawei397/yaml_to_ts_web\",\n    \"image\": \"yaml-to-ts\"\n  },\n  {\n    \"title\": \"Denosoar\",\n    \"link\": \"https://denosoar.deno.dev/\",\n    \"github\": \"Denosoar-GUI/Denosoar-GUI\",\n    \"image\": \"Denosoar\"\n  },\n  {\n    \"title\": \"link-maker\",\n    \"link\": \"https://link-maker.deno.dev/\",\n    \"github\": \"kuizuo/link-maker\",\n    \"image\": \"link-maker\"\n  },\n  {\n    \"title\": \"Delicious\",\n    \"link\": \"https://sking.deno.dev/\",\n    \"github\": \"shijianzhong/fresh-blog-system\",\n    \"image\": \"fresh-blog-system\"\n  },\n  {\n    \"title\": \"Alvaro Vanegas Sites\",\n    \"link\": \"https://ajvanegasv.dev/\",\n    \"github\": \"ajvanegasv/my-portfolio\",\n    \"image\": \"ajvanegasv\"\n  },\n  {\n    \"title\": \"Teams Template\",\n    \"link\": \"https://teams-template.deno.dev/\",\n    \"github\": \"davit-b/teams-template\",\n    \"image\": \"teams-template\"\n  },\n  {\n    \"title\": \"FestWithMe\",\n    \"link\": \"https://fest-with-me.deno.dev/\",\n    \"github\": \"rnaidenov/fest-with-me\",\n    \"image\": \"festwithme\"\n  },\n  {\n    \"title\": \"Pollify\",\n    \"link\": \"https://pollify.deno.dev/\",\n    \"github\": \"n4ze3m/pollify\",\n    \"image\": \"pollify\"\n  },\n  {\n    \"title\": \"JPT ChatGPT Rooms\",\n    \"link\": \"https://jpt.ma/\",\n    \"github\": \"zizwar/jpt\",\n    \"image\": \"jptchat\"\n  },\n  {\n    \"title\": \"Mieszko\",\n    \"link\": \"https://mieszko.xyz/\",\n    \"image\": \"mieszko\"\n  },\n  {\n    \"title\": \"Awesome Fresh\",\n    \"link\": \"https://uki00a.github.io/awesome-fresh/\",\n    \"image\": \"awesome-fresh\"\n  },\n  {\n    \"title\": \"Learn Mandarin\",\n    \"link\": \"https://mandarin.deno.dev/\",\n    \"github\": \"silvercrow/mandarin\",\n    \"image\": \"learn-mandarin\"\n  },\n  {\n    \"title\": \"Url shorter and Pastebin\",\n    \"link\": \"https://uspb.deno.dev/\",\n    \"github\": \"jneeee/uspb\",\n    \"image\": \"uspb\"\n  },\n  {\n    \"title\": \"Battleship\",\n    \"link\": \"https://battleship.deno.dev\",\n    \"github\": \"karelklima/battleship\",\n    \"image\": \"battleship\"\n  },\n  {\n    \"title\": \"Kanji.Academy\",\n    \"link\": \"https://kanji.academy\",\n    \"image\": \"kanji-academy\"\n  },\n  {\n    \"title\": \"LiberChat\",\n    \"link\": \"https://liberchat.deno.dev\",\n    \"image\": \"liberchat\"\n  },\n  {\n    \"title\": \"Miguel Rangel Site\",\n    \"link\": \"https://crawford.ml\",\n    \"github\": \"denyncrawford/new-portfolio\",\n    \"image\": \"miguel-rangel-site\"\n  },\n  {\n    \"title\": \"Deno Artwork\",\n    \"link\": \"https://artwork.deno.dev/\",\n    \"github\": \"jasonjgardner/deno-artwork\",\n    \"image\": \"deno-artwork\"\n  },\n  {\n    \"title\": \"Text2Audio\",\n    \"link\": \"https://text2audio.cc/\",\n    \"github\": \"dunkbing/text2audio\",\n    \"image\": \"text2audio\"\n  },\n  {\n    \"title\": \"FilterHN\",\n    \"link\": \"https://filterhn.com/\",\n    \"image\": \"filter-hn\"\n  },\n  {\n    \"title\": \"AquaVibes\",\n    \"link\": \"https://aquavibes.earth/\",\n    \"github\": \"arturolume/aquavibes\",\n    \"image\": \"aquavibes\"\n  },\n  {\n    \"title\": \"CS2 Items\",\n    \"link\": \"https://cs2items.pro/\",\n    \"image\": \"cs2items\"\n  },\n  {\n    \"title\": \"Enric Perpinyà Site\",\n    \"link\": \"https://perpinya.eu/\",\n    \"github\": \"evilmonkey19/cv\",\n    \"image\": \"evilmonkey19-cv\"\n  },\n  {\n    \"title\": \"Paul Jacks Blog and Experiments\",\n    \"link\": \"https://jacks.se/\",\n    \"github\": \"rebstorm/jacks.se\",\n    \"image\": \"rebstorm\"\n  },\n  {\n    \"title\": \"MergePanic\",\n    \"link\": \"https://mergepanic.com\",\n    \"image\": \"MergePanic\"\n  },\n  {\n    \"title\": \"Living Pixel Solutions\",\n    \"link\": \"https://livingpixel.io\",\n    \"image\": \"living-pixel\"\n  },\n  {\n    \"title\": \"RFUI\",\n    \"link\": \"https://rfui.deno.dev/\",\n    \"image\": \"rfui\"\n  },\n  {\n    \"title\": \"Echo-Echo\",\n    \"link\": \"https://echo-echo.deno.dev/\",\n    \"github\": \"Octo8080X/Echo-Echo\",\n    \"image\": \"echo-echo\"\n  },\n  {\n    \"title\": \"VirtualFreight Landing Page\",\n    \"link\": \"https://virtualfreight.software/\",\n    \"image\": \"virtualfreight\"\n  },\n  {\n    \"title\": \"Daniel G. Aubert's Site\",\n    \"link\": \"https://dgaubert.dev/\",\n    \"image\": \"dgaubert\"\n  },\n  {\n    \"title\": \"Do not remove this, it’s for preventing conflicts by trailing comma\",\n    \"link\": \"\",\n    \"github\": \"\",\n    \"image\": \"\"\n  },\n  {\n    \"title\": \"Thoth-Doc\",\n    \"link\": \"https://thoth-doc.deno.dev/\",\n    \"github\": \"Octo8080X/thoth\",\n    \"image\": \"thoth-doc\"\n  },\n  {\n    \"title\": \"Pile-Up\",\n    \"link\": \"https://pile-up.deno.dev/\",\n    \"github\": \"Octo8080X/Pile-Up\",\n    \"image\": \"pile-up\"\n  },\n  {\n    \"title\": \"2coffee's Blog\",\n    \"link\": \"https://2coffee.dev/\",\n    \"image\": \"hicoffee\"\n  },\n  {\n    \"title\": \"TicOXs\",\n    \"link\": \"https://ticoxs.deno.dev/\",\n    \"github\": \"Trid3r/ticoxs\",\n    \"image\": \"ticoxs\"\n  },\n  {\n    \"title\": \"WTF Is Congress Doing Now?\",\n    \"link\": \"https://wtfiscongressdoingnow.us\",\n    \"github\": \"gibbygano/wtfiscongressdoingnow\",\n    \"image\": \"wtfcongress\"\n  },\n  {\n    \"title\": \"PUBG Items\",\n    \"link\": \"https://pubgitems.info\",\n    \"image\": \"pubgitems\"\n  },\n  {\n    \"title\": \"Zhi's Blog\",\n    \"link\": \"https://zhi.deno.dev\",\n    \"github\": \"SisyphusZheng/me\",\n    \"image\": \"zhiblog\"\n  },\n  {\n    \"title\": \"Moatez's desktop\",\n    \"link\": \"https://moatez-bejaoui.deno.dev\",\n    \"github\": \"https://github.com/MoatezNG\",\n    \"image\": \"moatez-desktop\"\n  },\n  {\n    \"title\": \"Retro Ranker\",\n    \"link\": \"https://retroranker.site\",\n    \"github\": \"https://github.com/Nergy101/retro-ranker\",\n    \"image\": \"retro-ranker\"\n  }\n]\n"
  },
  {
    "path": "www/deno.json",
    "content": "{\n  \"tasks\": {\n    \"start\": \"deno serve -A _fresh/server.js\",\n    \"dev\": \"vite\",\n    \"build\": \"vite build\"\n  },\n  \"imports\": {\n    \"@tailwindcss/vite\": \"npm:@tailwindcss/vite@^4.1.12\",\n    \"vite\": \"npm:vite@^7.1.4\",\n    \"vite-plugin-inspect\": \"npm:vite-plugin-inspect@^11.3.2\"\n  }\n}\n"
  },
  {
    "path": "www/dev.ts",
    "content": "#!/usr/bin/env -S deno run -A --watch=static/,routes/\n\nimport { Builder } from \"fresh/dev\";\nimport { tailwind } from \"@fresh/plugin-tailwind\";\n\nconst builder = new Builder({ target: \"safari12\" });\ntailwind(builder);\n\nif (Deno.args.includes(\"build\")) {\n  await builder.build();\n} else {\n  await builder.listen(() => import(\"./main.ts\"));\n}\n"
  },
  {
    "path": "www/islands/Counter.tsx",
    "content": "import { useSignal } from \"@preact/signals\";\nimport * as Icons from \"../components/Icons.tsx\";\nimport type { JSX } from \"preact\";\n\ninterface CounterProps {\n  start: number;\n}\n\nexport default function Counter(props: CounterProps) {\n  const count = useSignal(props.start);\n\n  return (\n    <div>\n      <h3 class=\"text-2xl font-bold\">\n        Interactive island\n      </h3>\n      <p>\n        The server supplied the initial value of 3.\n      </p>\n      <div\n        class=\"flex justify-between items-center mt-4 mx-auto\"\n        style=\"max-width: 20rem\"\n      >\n        <RoundedButton\n          title=\"Subtract 1\"\n          onClick={() => count.value -= 1}\n        >\n          <Icons.IconMinus />\n        </RoundedButton>\n        <div class=\"text-6xl tabular-nums font-bold\">\n          {count}\n        </div>\n\n        <RoundedButton\n          title=\"Add 1\"\n          onClick={() => count.value += 1}\n        >\n          <Icons.IconPlus />\n        </RoundedButton>\n      </div>\n    </div>\n  );\n}\n\nfunction RoundedButton(props: JSX.HTMLAttributes<HTMLButtonElement>) {\n  return (\n    <button\n      style={{\n        touchAction: \"manipulation\",\n      }}\n      {...props}\n      class=\"p-3 rounded-full border-2 border-current bg-white/20 hover:bg-white/50 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:bg-green-200 disabled:cursor-default\"\n    />\n  );\n}\n"
  },
  {
    "path": "www/islands/FormSubmitDemo.tsx",
    "content": "export function FormSubmitDemo() {\n  return (\n    <form action=\"/\" method=\"POST\" class=\"text-left md:px-8\">\n      <fieldset class=\"mb-4\">\n        <legend class=\"mb-2 font-bold\">What's your favorite treat?</legend>\n\n        <label for=\"lemon-meringue-pie\" class=\"flex gap-2 items-center\">\n          <input\n            type=\"radio\"\n            value=\"lemon-meringue-pie\"\n            id=\"lemon-meringue-pie\"\n            name=\"treat\"\n            checked\n          />\n          Lemon meringue pie\n        </label>\n        <label for=\"lemon-shortbread-cookies\" class=\"flex gap-2 items-center\">\n          <input\n            type=\"radio\"\n            value=\"lemon-shortbread-cookies\"\n            id=\"lemon-shortbread-cookies\"\n            name=\"treat\"\n          />\n          Lemon shortbread cookies\n        </label>\n        <label for=\"lemon-sherbet\" class=\"flex gap-2 items-center\">\n          <input\n            type=\"radio\"\n            value=\"lemon-sherbet\"\n            id=\"lemon-sherbet\"\n            name=\"treat\"\n          />\n          Lemon sherbet\n        </label>\n        <label for=\"lemon-bars\" class=\"flex gap-2 items-center\">\n          <input\n            type=\"radio\"\n            value=\"lemon-bars\"\n            id=\"lemon-bars\"\n            name=\"treat\"\n          />\n          Lemon bars\n        </label>\n      </fieldset>\n\n      <button\n        type=\"submit\"\n        value=\"Submit\"\n        class=\"pt-2 pb-1 px-3 rounded-sm border-current border-[1.5px] font-semibold\"\n      >\n        Submit\n      </button>\n    </form>\n  );\n}\n"
  },
  {
    "path": "www/islands/LemonBottom.tsx",
    "content": "import { useEffect, useRef } from \"preact/hooks\";\nimport { useSignal } from \"@preact/signals\";\nimport { WaveTank } from \"../components/WaveTank.ts\";\n\nfunction easeInCirc(x: number) {\n  return 1 - Math.sqrt(1 - Math.pow(x, 2));\n}\n\nconst waveTank = new WaveTank();\n\nfunction LemonBottom() {\n  const SVG_WIDTH = 100;\n  const counter = useSignal(0);\n  const dropY = useSignal(60);\n  const width = useSignal(SVG_WIDTH);\n  const widthRef = useRef(width.value);\n  const springs = useSignal(waveTank.springs);\n  const requestIdRef = useRef<number>();\n  const grid = SVG_WIDTH / waveTank.waveLength;\n  let lemonTop: SVGElement | null;\n  let lemonTopLeft: number;\n  const points = [\n    [0, 100],\n    [0, 0],\n    ...springs.value.map((x, i) => [i * grid, x.p]),\n    [width.value, 0],\n    [width.value, 100],\n  ];\n  const springsPath = `${points.map((x) => x.join(\",\")).join(\" \")}`;\n\n  function updateJuice(timestamp: number) {\n    const amp = 40;\n    const x = timestamp / 2000;\n    const saw = x - Math.floor(x);\n    if (saw < 0.6) {\n      counter.value = easeInCirc(saw) * amp;\n      dropY.value = -100;\n    } else {\n      counter.value = easeInCirc(1 - saw) * amp * 0.1;\n      dropY.value = 70 + Math.pow(saw - 0.6, 2) * 10000;\n    }\n  }\n\n  function update(timestamp: number) {\n    updateJuice(timestamp);\n    waveTank.update(waveTank.springs);\n    springs.value = [...waveTank.springs];\n\n    const offset = 500;\n    const saw = (timestamp + offset) / 2000 -\n      Math.floor((timestamp + offset) / 2000);\n    if (saw < 0.01) {\n      drop();\n    }\n    requestIdRef.current = globalThis.requestAnimationFrame(update);\n  }\n\n  function resize() {\n    width.value = document.body.clientWidth;\n    lemonTop = document.querySelector(\"#lemon-top\");\n    lemonTopLeft = lemonTop ? lemonTop.getBoundingClientRect().left + 25 : 50;\n  }\n\n  function drop() {\n    let dropPosition = 50;\n    if (widthRef.current >= 768) {\n      dropPosition = Math.round(100 / widthRef.current * lemonTopLeft);\n    } else {\n      dropPosition = Math.round(\n        ((widthRef.current / 2 - 30) / widthRef.current) * 100,\n      );\n    }\n    waveTank.springs[dropPosition].p = -40;\n  }\n\n  useEffect(() => {\n    widthRef.current = width.value;\n  }, [width.value]);\n\n  useEffect(() => {\n    const mediaQuery = globalThis.matchMedia(\n      \"(prefers-reduced-motion: reduce)\",\n    );\n    if (mediaQuery.matches) {\n      return;\n    }\n\n    requestIdRef.current = requestAnimationFrame(update);\n    globalThis.addEventListener(\"resize\", resize);\n    resize();\n\n    return () => {\n      globalThis.removeEventListener(\"resize\", resize);\n      if (requestIdRef.current !== undefined) {\n        cancelAnimationFrame(requestIdRef.current);\n      }\n    };\n  }, []);\n\n  return (\n    <svg\n      aria-hidden=\"true\"\n      width=\"100%\"\n      height=\"100px\"\n      viewBox=\"0 0 100 100\"\n      preserveAspectRatio=\"none\"\n      style=\"margin-top: -60px\"\n    >\n      <polygon\n        points={springsPath}\n        fill=\"white\"\n        transform=\"translate(0, 50)\"\n      >\n      </polygon>\n    </svg>\n  );\n}\n\nexport default LemonBottom;\n"
  },
  {
    "path": "www/islands/LemonDrop.tsx",
    "content": "import { useEffect, useRef } from \"preact/hooks\";\nimport { useSignal } from \"@preact/signals\";\nimport { WaveTank } from \"../components/WaveTank.ts\";\n\nfunction easeInCirc(x: number) {\n  return 1 - Math.sqrt(1 - Math.pow(x, 2));\n}\n\nconst waveTank = new WaveTank();\n\nfunction LemonDrop() {\n  const SVG_WIDTH = 100;\n  const counter = useSignal(0);\n  const dropY = useSignal(60);\n  const width = useSignal(SVG_WIDTH);\n  const widthRef = useRef(width.value);\n  const springs = useSignal(waveTank.springs);\n  const requestIdRef = useRef<number>();\n  const grid = SVG_WIDTH / waveTank.waveLength;\n  const points = [\n    [0, 100],\n    [0, 0],\n    ...springs.value.map((x, i) => [i * grid, x.p]),\n    [width.value, 0],\n    [width.value, 100],\n  ];\n  const springsPath = `${points.map((x) => x.join(\",\")).join(\" \")}`;\n  const juice = `M18 ${63 + counter.value} C15 ${63 + counter.value} 16 ${\n    63 + counter.value\n  } 12 61L9 56C2 33 62 -3 80 12C103 27 44 56 29 58C27 58 25 59 24 61C20 ${\n    63 + counter.value\n  } 21 ${63 + counter.value} 18 ${63 + counter.value}Z`;\n\n  function updateJuice(timestamp: number) {\n    const amp = 40;\n    const x = timestamp / 2000;\n    const saw = x - Math.floor(x);\n    if (saw < 0.6) {\n      counter.value = easeInCirc(saw) * amp;\n      dropY.value = -100;\n    } else {\n      counter.value = easeInCirc(1 - saw) * amp * 0.1;\n      dropY.value = 70 + Math.pow(saw - 0.6, 2) * 10000;\n    }\n  }\n\n  function update(timestamp: number) {\n    updateJuice(timestamp);\n    waveTank.update(waveTank.springs);\n    springs.value = [...waveTank.springs];\n\n    const offset = 500;\n    const saw = (timestamp + offset) / 2000 -\n      Math.floor((timestamp + offset) / 2000);\n    if (saw < 0.01) {\n      drop();\n    }\n    requestIdRef.current = globalThis.requestAnimationFrame(update);\n  }\n\n  function resize() {\n    width.value = document.body.clientWidth;\n  }\n\n  function drop() {\n    const dropPosition = Math.round(\n      ((widthRef.current / 2 - 30) / widthRef.current) * 100,\n    );\n    waveTank.springs[dropPosition].p = -40;\n  }\n\n  useEffect(() => {\n    widthRef.current = width.value;\n  }, [width.value]);\n\n  useEffect(() => {\n    const mediaQuery = globalThis.matchMedia(\n      \"(prefers-reduced-motion: reduce)\",\n    );\n    if (mediaQuery.matches) {\n      return;\n    }\n\n    requestIdRef.current = requestAnimationFrame(update);\n    globalThis.addEventListener(\"resize\", resize);\n    resize();\n\n    return () => {\n      globalThis.removeEventListener(\"resize\", resize);\n      if (requestIdRef.current !== undefined) {\n        cancelAnimationFrame(requestIdRef.current);\n      }\n    };\n  }, []);\n\n  return (\n    <>\n      <svg\n        width=\"120\"\n        height=\"240\"\n        viewBox=\"0 0 100 180\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        role=\"img\"\n        class=\"mt-8\"\n      >\n        <title>Fresh logo</title>\n        <circle cx=\"18\" cy={dropY.value} r=\"4\" fill=\"white\"></circle>\n        <path\n          d=\"M11.9 96.3v24H7v-24h4.9Zm7.5 10.2v4h-8.8v-4h8.8Zm1-10.2v4h-9.8v-4h9.8ZM23.2 96.3H31c1.6 0 3 .3 4.1.9 1.1.5 2 1.3 2.6 2.4a8 8 0 0 1 1 4c0 1.3-.2 2.4-.6 3.3-.3 1-.8 1.7-1.5 2.3-.7.6-1.4 1-2.3 1.5l-1.5.8h-6.3v-4h4.3a3 3 0 0 0 1.7-.4c.4-.3.7-.7 1-1.2a5 5 0 0 0 .3-2c0-.7-.1-1.3-.3-1.9-.2-.5-.5-1-1-1.2-.3-.3-.9-.5-1.5-.5h-3v20h-4.8v-24Zm11 24-4.4-10.7h5l4.6 10.5v.2h-5.2ZM55.9 116.3v4H45.4v-4h10.5Zm-9-20v24H42v-24H47Zm7.6 9.8v3.8h-9.1v-3.8h9Zm1.4-9.8v4H45.4v-4h10.5ZM69 114c0-.4 0-.8-.2-1.1 0-.4-.2-.7-.5-1l-1-1-1.9-.8-2.6-1.2-2.2-1.5a6.5 6.5 0 0 1-1.7-2 6 6 0 0 1-.5-2.7c0-1 .1-2 .5-2.7a6 6 0 0 1 1.6-2.2c.7-.5 1.5-1 2.4-1.3a9.5 9.5 0 0 1 7.1.5c1.2.6 2 1.5 2.7 2.6.6 1 1 2.4 1 3.8h-5a5 5 0 0 0-.2-1.8c-.2-.5-.5-1-1-1.2-.4-.3-1-.5-1.6-.5-.6 0-1.1.2-1.5.4-.4.2-.7.6-1 1l-.2 1.4c0 .4.1.8.3 1.1l.8.8a21.3 21.3 0 0 0 2.8 1.4l2.9 1.4a9 9 0 0 1 2 1.8c.7.6 1 1.3 1.4 2a7.9 7.9 0 0 1-.1 5.5c-.4.8-.9 1.5-1.5 2.1a7 7 0 0 1-2.5 1.4c-2 .5-1 1.8-3 1.8s-1.3-1.5-3.2-1.8c-1-.3-2-.8-2.7-1.4a6.7 6.7 0 0 1-1.8-2.5c-.4-1-.6-2.2-.6-3.5h4.9c0 .7 0 1.3.2 1.8.1.5.3 1 .6 1.3.3.2.7.5 1.1.6.5.2 1 .2 1.5.2.7 0 1.2 0 1.6-.3.4-.3.6-.6.8-1 .2-.4.3-.9.3-1.4ZM90.5 106v4h-10v-4h10Zm-8.6-9.7v24h-4.8v-24h4.8Zm12.1 0v24h-4.8v-24H94Z\"\n          fill=\"#0A140C\"\n        />\n        <path\n          d=\"M84 16c13 27 1 52-7 59 0 4-9 9-12 7-12 5-38-2-53-21-6-7 1-21 21-36 13-10 33-17 51-9Z\"\n          fill=\"#FFD80B\"\n        />\n        <path d={juice} fill=\"white\" />\n        <path\n          d=\"M69 15c15-1 9 10-6 19L44 44c-2 1-4-2-6 0l-3 6c-3 1-13 3-16 2-5-2-5-9 5-18l7-4c-1-2-1-2 2-5 3-2 19-10 29-11l1 2 6-1Z\"\n          fill=\"#FFED4E\"\n        />\n        <path\n          d=\"M38 35c1-1 3-2 3-4l8-3c0 1-1 3 1 4-2 1-7 1-8 5-1-2-1-2-4-2Z\"\n          fill=\"#fff\"\n        />\n      </svg>\n      <svg\n        width=\"100%\"\n        height=\"100px\"\n        viewBox=\"0 0 100 100\"\n        preserveAspectRatio=\"none\"\n        class=\"-mt-5\"\n      >\n        <polygon\n          points={springsPath}\n          fill=\"white\"\n          transform=\"translate(0, 50)\"\n        >\n        </polygon>\n      </svg>\n    </>\n  );\n}\n\nexport default LemonDrop;\n"
  },
  {
    "path": "www/islands/LemonTop.tsx",
    "content": "import { useEffect, useRef } from \"preact/hooks\";\nimport { useSignal } from \"@preact/signals\";\nimport { WaveTank } from \"../components/WaveTank.ts\";\n\nfunction easeInCirc(x: number) {\n  return 1 - Math.sqrt(1 - Math.pow(x, 2));\n}\n\nconst waveTank = new WaveTank();\n\nfunction LemonTop() {\n  const SVG_WIDTH = 100;\n  const counter = useSignal(0);\n  const dropY = useSignal(60);\n  const width = useSignal(SVG_WIDTH);\n  const widthRef = useRef(width.value);\n  const springs = useSignal(waveTank.springs);\n  const requestIdRef = useRef<number>();\n\n  const juice = `M18 ${63 + counter.value} C15 ${63 + counter.value} 16 ${\n    63 + counter.value\n  } 12 61L9 56C2 33 62 -3 80 12C103 27 44 56 29 58C27 58 25 59 24 61C20 ${\n    63 + counter.value\n  } 21 ${63 + counter.value} 18 ${63 + counter.value}Z`;\n\n  function updateJuice(timestamp: number) {\n    const amp = 40;\n    const x = timestamp / 2000;\n    const saw = x - Math.floor(x);\n    if (saw < 0.6) {\n      counter.value = easeInCirc(saw) * amp;\n      dropY.value = -100;\n    } else {\n      counter.value = easeInCirc(1 - saw) * amp * 0.1;\n      dropY.value = 70 + Math.pow(saw - 0.6, 2) * 10000;\n    }\n  }\n\n  function update(timestamp: number) {\n    updateJuice(timestamp);\n    waveTank.update(waveTank.springs);\n    springs.value = [...waveTank.springs];\n\n    const offset = 500;\n    const saw = (timestamp + offset) / 2000 -\n      Math.floor((timestamp + offset) / 2000);\n    if (saw < 0.01) {\n      drop();\n    }\n    requestIdRef.current = globalThis.requestAnimationFrame(update);\n  }\n\n  function resize() {\n    width.value = document.body.clientWidth;\n  }\n\n  function drop() {\n    const dropPosition = Math.round(\n      ((widthRef.current / 2 - 30) / widthRef.current) * 100,\n    );\n    waveTank.springs[dropPosition].p = -40;\n  }\n\n  useEffect(() => {\n    widthRef.current = width.value;\n  }, [width.value]);\n\n  useEffect(() => {\n    const mediaQuery = globalThis.matchMedia(\n      \"(prefers-reduced-motion: reduce)\",\n    );\n    if (mediaQuery.matches) {\n      return;\n    }\n\n    requestIdRef.current = requestAnimationFrame(update);\n    globalThis.addEventListener(\"resize\", resize);\n    resize();\n\n    return () => {\n      globalThis.removeEventListener(\"resize\", resize);\n      if (requestIdRef.current !== undefined) {\n        cancelAnimationFrame(requestIdRef.current);\n      }\n    };\n  }, []);\n\n  return (\n    <>\n      <svg\n        aria-hidden=\"true\"\n        width=\"150\"\n        height=\"500\"\n        viewBox=\"0 0 100 300\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        role=\"img\"\n        class=\"mt-8 w-full h-auto max-w-32 lg:max-w-36\"\n        id=\"lemon-top\"\n      >\n        <title>Fresh logo</title>\n        <circle cx=\"18\" cy={dropY.value} r=\"4\" fill=\"#fff\"></circle>\n        <path\n          d=\"M11.9 96.3v24H7v-24h4.9Zm7.5 10.2v4h-8.8v-4h8.8Zm1-10.2v4h-9.8v-4h9.8ZM23.2 96.3H31c1.6 0 3 .3 4.1.9 1.1.5 2 1.3 2.6 2.4a8 8 0 0 1 1 4c0 1.3-.2 2.4-.6 3.3-.3 1-.8 1.7-1.5 2.3-.7.6-1.4 1-2.3 1.5l-1.5.8h-6.3v-4h4.3a3 3 0 0 0 1.7-.4c.4-.3.7-.7 1-1.2a5 5 0 0 0 .3-2c0-.7-.1-1.3-.3-1.9-.2-.5-.5-1-1-1.2-.3-.3-.9-.5-1.5-.5h-3v20h-4.8v-24Zm11 24-4.4-10.7h5l4.6 10.5v.2h-5.2ZM55.9 116.3v4H45.4v-4h10.5Zm-9-20v24H42v-24H47Zm7.6 9.8v3.8h-9.1v-3.8h9Zm1.4-9.8v4H45.4v-4h10.5ZM69 114c0-.4 0-.8-.2-1.1 0-.4-.2-.7-.5-1l-1-1-1.9-.8-2.6-1.2-2.2-1.5a6.5 6.5 0 0 1-1.7-2 6 6 0 0 1-.5-2.7c0-1 .1-2 .5-2.7a6 6 0 0 1 1.6-2.2c.7-.5 1.5-1 2.4-1.3a9.5 9.5 0 0 1 7.1.5c1.2.6 2 1.5 2.7 2.6.6 1 1 2.4 1 3.8h-5a5 5 0 0 0-.2-1.8c-.2-.5-.5-1-1-1.2-.4-.3-1-.5-1.6-.5-.6 0-1.1.2-1.5.4-.4.2-.7.6-1 1l-.2 1.4c0 .4.1.8.3 1.1l.8.8a21.3 21.3 0 0 0 2.8 1.4l2.9 1.4a9 9 0 0 1 2 1.8c.7.6 1 1.3 1.4 2a7.9 7.9 0 0 1-.1 5.5c-.4.8-.9 1.5-1.5 2.1a7 7 0 0 1-2.5 1.4c-2 .5-1 1.8-3 1.8s-1.3-1.5-3.2-1.8c-1-.3-2-.8-2.7-1.4a6.7 6.7 0 0 1-1.8-2.5c-.4-1-.6-2.2-.6-3.5h4.9c0 .7 0 1.3.2 1.8.1.5.3 1 .6 1.3.3.2.7.5 1.1.6.5.2 1 .2 1.5.2.7 0 1.2 0 1.6-.3.4-.3.6-.6.8-1 .2-.4.3-.9.3-1.4ZM90.5 106v4h-10v-4h10Zm-8.6-9.7v24h-4.8v-24h4.8Zm12.1 0v24h-4.8v-24H94Z\"\n          fill=\"#0A140C\"\n        />\n        <path\n          d=\"M84 16c13 27 1 52-7 59 0 4-9 9-12 7-12 5-38-2-53-21-6-7 1-21 21-36 13-10 33-17 51-9Z\"\n          fill=\"#FFD80B\"\n        />\n        <path d={juice} fill=\"#fff\" />\n        <path\n          d=\"M69 15c15-1 9 10-6 19L44 44c-2 1-4-2-6 0l-3 6c-3 1-13 3-16 2-5-2-5-9 5-18l7-4c-1-2-1-2 2-5 3-2 19-10 29-11l1 2 6-1Z\"\n          fill=\"#FFED4E\"\n        />\n        <path\n          d=\"M38 35c1-1 3-2 3-4l8-3c0 1-1 3 1 4-2 1-7 1-8 5-1-2-1-2-4-2Z\"\n          fill=\"#fff\"\n        />\n      </svg>\n    </>\n  );\n}\n\nexport default LemonTop;\n"
  },
  {
    "path": "www/islands/SearchButton.tsx",
    "content": "import { useEffect, useRef } from \"preact/hooks\";\nimport docsearch from \"docsearch\";\n\n// Copied from algolia source code\ntype DocSearchProps = {\n  appId: string;\n  apiKey: string;\n  indexName: string;\n  container: HTMLElement | string;\n};\n\nexport default function SearchButton(\n  props: { docsearch?: (args: DocSearchProps) => void; class?: string },\n) {\n  const ref = useRef<HTMLDivElement>(null);\n  useEffect(() => {\n    if (ref.current) {\n      props.docsearch || docsearch({\n        appId: \"CWUS37S0PK\",\n        apiKey: \"caa591b6dcb2c9308551361d954a728b\",\n        indexName: \"fresh\",\n        container: ref.current,\n      });\n    }\n  }, [ref.current]);\n  return (\n    <div\n      title=\"Search Button\"\n      class={\"h-9 mb-6 \" + (props.class ?? \"\")}\n      ref={ref}\n    >\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/islands/TableOfContents.tsx",
    "content": "import { useEffect, useRef } from \"preact/hooks\";\nimport type { MarkdownHeading } from \"../utils/markdown.ts\";\nimport { useSignal } from \"@preact/signals\";\n\nexport interface TableOfContentsProps {\n  headings: MarkdownHeading[];\n}\n\nfunction setActiveLink(\n  container: HTMLElement,\n  marker: HTMLElement,\n  id: string,\n) {\n  container.querySelectorAll(`a`).forEach((link) =>\n    link.classList.remove(\"active\")\n  );\n  const tocLink = container.querySelector(\n    `a[href=\"#${id}\"]`,\n  ) as HTMLElement;\n\n  if (tocLink === null) return;\n\n  tocLink.classList.add(\"active\");\n\n  const rect = tocLink\n    .getBoundingClientRect();\n  const markerRect = marker.getBoundingClientRect();\n\n  const top = tocLink.offsetTop + (rect.height / 2) -\n    (markerRect.height / 2);\n  marker.style.cssText = `transform: translate3d(0, ${top}px, 0); opacity: 1`;\n}\n\nfunction hLevelToClass(level: number): string {\n  if (level === 3) return \"ml-4\";\n  if (level === 4) return \"ml-8\";\n  return \"\";\n}\n\nexport function TableOfContents({ headings }: TableOfContentsProps) {\n  const ref = useRef<HTMLDivElement>(null);\n  const refMarker = useRef<HTMLDivElement>(null);\n  const isOpen = useSignal(false);\n\n  useEffect(() => {\n    if (!ref.current) return;\n    const container = ref.current;\n\n    const activeList = new Array(headings.length).fill(false);\n    const visibleList = new Array(headings.length).fill(false);\n\n    const marker = refMarker.current!;\n    const observer = new IntersectionObserver((entries) => {\n      for (let i = 0; i < entries.length; i++) {\n        const entry = entries[i];\n        const target = entry.target;\n\n        for (let j = 0; j < headings.length; j++) {\n          const heading = headings[j];\n          if (heading.id === target.id) {\n            const active = entry.isIntersecting ||\n              entry.boundingClientRect.top < 0;\n            activeList[j] = active;\n            visibleList[j] = entry.isIntersecting;\n          }\n        }\n      }\n\n      // Reset links\n      for (let i = 0; i < headings.length; i++) {\n        const id = headings[i].id;\n        const tocLink = container.querySelector(\n          `a[href=\"#${id}\"]`,\n        );\n        if (tocLink !== null) {\n          tocLink.classList.remove(\"active\");\n        }\n      }\n\n      let activeIdx = visibleList.indexOf(true);\n      if (activeIdx < 0) {\n        activeIdx = activeList.lastIndexOf(true);\n      }\n\n      if (activeIdx > -1) {\n        const id = headings[activeIdx].id;\n        setActiveLink(container, marker, id);\n      } else {\n        marker.style.cssText = `transform: translate3d(0, 0, 0); opacity: 0`;\n      }\n    });\n\n    document.querySelectorAll(\n      \".markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6\",\n    ).forEach((elem) => {\n      observer.observe(elem);\n    });\n\n    return () => {\n      observer.disconnect();\n    };\n  }, [headings]);\n\n  return (\n    <div\n      ref={ref}\n      class=\"relative xl:order-2 w-56 xl:max-w-xs xl:top-14 shrink-0\"\n    >\n      {headings.length > 0 && (\n        <>\n          <div class=\"xl:hidden mx-4 md:mx-0 mt-4 md:mt-0\">\n            <button\n              type=\"button\"\n              id=\"toc-outline-btn\"\n              onClick={() => isOpen.value = !isOpen.value}\n              class=\"bg-background-primary py-2 px-4 rounded-sm border border-foreground-secondary/30 flex items-center hover:border-fresh-green/80 transition-colors text-sm\"\n            >\n              On this page\n              <svg\n                class={`w-4 h-4 inline-block ml-2 -rotate-90 [&.active]:rotate-0 ${\n                  isOpen ? \"active\" : \"\"\n                }`}\n              >\n                <use href=\"/icons.svg#arrow-down\" />\n              </svg>\n            </button>\n            {isOpen.value && (\n              <div class=\"mt-2 pl-4 border-l border-foreground-primary/20 text-[13px] leading-7\">\n                <nav aria-labelledby=\"toc-outline-btn\">\n                  <ul>\n                    {headings.map((heading) => {\n                      return (\n                        <li key={heading.id}>\n                          <a\n                            href={`#${heading.id}`}\n                            class={`block ${\n                              hLevelToClass(heading.level)\n                            } truncate text-gray-600 dark:text-gray-400`}\n                            // deno-lint-ignore react-no-danger\n                            dangerouslySetInnerHTML={{ __html: heading.html }}\n                          />\n                        </li>\n                      );\n                    })}\n                  </ul>\n                </nav>\n              </div>\n            )}\n          </div>\n          <div class=\"hidden xl:block xl:sticky xl:top-40\">\n            <div class=\"relative\">\n              <div\n                ref={refMarker}\n                class=\"marker w-[2px] bg-green-400 h-5 absolute top-0 opacity-0 transition-all\"\n              />\n              <div class=\"pl-4 border-l border-foreground-secondary/20 text-[13px] leading-7\">\n                <div role=\"heading\" aria-level={2} class=\"font-semibold\">\n                  On this page\n                </div>\n                <nav aria-labelledby=\"doc-outline-aria-label\">\n                  <span id=\"doc-outline-aria-label\" class=\"sr-only\">\n                    Table of Contents for current page\n                  </span>\n                  <ul>\n                    {headings.map((heading) => {\n                      return (\n                        <li key={heading.id}>\n                          <a\n                            href={`#${heading.id}`}\n                            class={`block truncate transition-colors ${\n                              hLevelToClass(heading.level)\n                            } text-gray-600 dark:text-gray-400 [&.active]:text-green-600 dark:[&.active]:text-green-300`}\n                            onClick={() => {\n                              setActiveLink(\n                                ref.current!,\n                                refMarker.current!,\n                                heading.id,\n                              );\n                            }}\n                            // deno-lint-ignore react-no-danger\n                            dangerouslySetInnerHTML={{ __html: heading.html }}\n                          />\n                        </li>\n                      );\n                    })}\n                  </ul>\n                </nav>\n              </div>\n            </div>\n          </div>\n        </>\n      )}\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/islands/ThemeToggle.tsx",
    "content": "import { IS_BROWSER } from \"fresh/runtime\";\nimport { useSignal } from \"@preact/signals\";\n\nexport default function ThemeToggle() {\n  const theme = useSignal(\n    !IS_BROWSER ? \"light\" : document.documentElement.dataset.theme ?? \"light\",\n  );\n\n  const toggleTheme = () => {\n    theme.value = theme.value === \"light\" ? \"dark\" : \"light\";\n    document.documentElement.setAttribute(\"data-theme\", theme.value);\n    localStorage.setItem(\"theme\", theme.value);\n  };\n\n  return (\n    <button\n      type=\"button\"\n      onClick={toggleTheme}\n      class=\"cursor-pointer p-1 -m-1\"\n      aria-label=\"Toggle Theme\"\n    >\n      {theme.value === \"light\"\n        ? (\n          <svg\n            class=\"fill-foreground-primary hover:fill-fresh w-6 h-6\"\n            viewBox=\"0 0 20 20\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <path d=\"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z\">\n            </path>\n          </svg>\n        )\n        : (\n          <svg\n            class=\"fill-foreground-primary hover:fill-fresh w-6 h-6\"\n            viewBox=\"0 0 20 20\"\n            xmlns=\"http://www.w3.org/2000/svg\"\n          >\n            <path\n              d=\"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1\n                  0 100-2H3a1 1 0 000 2h1z\"\n              fill-rule=\"evenodd\"\n              clip-rule=\"evenodd\"\n            >\n            </path>\n          </svg>\n        )}\n    </button>\n  );\n}\n"
  },
  {
    "path": "www/islands/VersionSelect.tsx",
    "content": "// Copyright 2022-2023 the Deno authors. All rights reserved. MIT license.\n\nimport { IS_BROWSER } from \"fresh/runtime\";\nimport type { VersionLink } from \"../routes/docs/[...slug].tsx\";\n\nexport default function VersionSelect(\n  { versions, selectedVersion }: {\n    versions: VersionLink[];\n    selectedVersion: string;\n  },\n) {\n  const selectedIsLatest = selectedVersion === \"latest\";\n  const selectedIsCanary = selectedVersion === \"canary\";\n\n  return (\n    <>\n      <div class=\"relative mr-4 sm:mr-0\">\n        <label htmlFor=\"version\" class=\"sr-only\">\n          Version\n        </label>\n        {selectedIsLatest && (\n          <div class=\"flex absolute pointer-events-none select-none w-full h-full items-center justify-end pr-8\">\n            <div class=\"rounded-full px-2 py-1 text-xs tag-label bg-[#056CF025] dark:bg-[#2182ff45] text-blue-700 dark:text-blue-400\">\n              Latest\n            </div>\n          </div>\n        )}\n        {selectedIsCanary && (\n          <div class=\"flex absolute pointer-events-none select-none w-full h-full items-center justify-end pr-8\">\n            <div class=\"rounded-full px-2 py-1 text-xs tag-label bg-[#F0900525] text-yellow-600\">\n              🚧 Preview\n            </div>\n          </div>\n        )}\n        <select\n          id=\"version\"\n          class={`rounded-md block border border-foreground-primary/20 appearance-none bg-background-primary form-select-bg font-semibold ${\n            selectedIsLatest ? \"pr-22\" : \"pr-10\"\n          } py-2 pl-3 w-full h-full leading-none sm:text-sm sm:leading-5 focus:outline-hidden focus:border-blue-300 hover:bg-background-secondary`}\n          value={selectedVersion}\n          onChange={(e) => {\n            if (e.currentTarget.value !== selectedVersion) {\n              const entry = versions.find((entry) =>\n                entry.value === e.currentTarget.value\n              );\n              if (entry) {\n                location.href = entry.href;\n              }\n            }\n          }}\n          disabled={!IS_BROWSER}\n        >\n          {versions.map((entry) => (\n            <option key={entry.value} value={entry.value}>\n              {entry.label}\n            </option>\n          ))}\n        </select>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "www/main.ts",
    "content": "import { App, staticFiles, trailingSlashes } from \"fresh\";\n\nexport const app = new App()\n  .use(staticFiles())\n  .use(trailingSlashes(\"never\"))\n  .fsRoutes();\n"
  },
  {
    "path": "www/main_test.ts",
    "content": "import {\n  withBrowser,\n  withChildProcessServer,\n} from \"../packages/fresh/tests/test_utils.tsx\";\nimport { expect } from \"@std/expect\";\nimport { retry } from \"@std/async/retry\";\nimport {\n  buildVite,\n  launchProd,\n} from \"../packages/plugin-vite/tests/test_utils.ts\";\n\nlet result: Awaited<ReturnType<typeof buildVite>>;\n\nDeno.test.beforeAll(async () => {\n  result = await buildVite(import.meta.dirname!);\n});\n\nDeno.test.afterAll(async () => {\n  await result?.[Symbol.asyncDispose]();\n});\n\nDeno.test({\n  name: \"CORS should not set on GET /fresh-badge.svg\",\n  sanitizeOps: false,\n  sanitizeResources: false,\n  fn: async () => {\n    await launchProd({ cwd: result.tmp }, async (address) => {\n      const resp = await fetch(`${address}/fresh-badge.svg`);\n      await resp?.body?.cancel();\n      expect(resp.headers.get(\"cross-origin-resource-policy\")).toEqual(null);\n    });\n  },\n});\n\nDeno.test({\n  name: \"shows version selector\",\n  sanitizeOps: false,\n  sanitizeResources: false,\n  fn: async () => {\n    await retry(async () => {\n      await withChildProcessServer(\n        {\n          cwd: result.tmp,\n          args: [\"serve\", \"-A\", \"--port\", \"0\", \"_fresh/server.js\"],\n        },\n        async (address) => {\n          await withBrowser(async (page) => {\n            await page.goto(`${address}/docs`);\n            await page.waitForSelector(\"#version\");\n\n            // Check that we redirected to the first page\n            await page.waitForFunction(() => {\n              const url = new URL(window.location.href);\n              return url.pathname === \"/docs/introduction\";\n            });\n\n            // Wait for version selector to be enabled\n            await page.waitForSelector(\"#version:not([disabled])\");\n\n            const options = await page\n              .locator<HTMLSelectElement>(\"#version\")\n              .evaluate((el) => {\n                return Array.from(el.options).map((option) => ({\n                  value: option.value,\n                  label: option.textContent,\n                }));\n              });\n\n            expect(options.map((item) => item.value)).toEqual([\n              \"latest\",\n              \"1.x\",\n            ]);\n\n            const selectValue = await page\n              .locator<HTMLSelectElement>(\"#version\")\n              .evaluate((el) => el.value);\n            expect(selectValue).toEqual(\"latest\");\n\n            // Go to canary page\n            await page.evaluate(() => {\n              const el = document.querySelector(\n                \"#version\",\n              ) as HTMLSelectElement;\n              el.value = \"1.x\";\n              el.dispatchEvent(new Event(\"change\"));\n            });\n\n            await new Promise((r) => setTimeout(r, 1000));\n\n            await page.waitForSelector(\"#version:not([disabled])\");\n            const selectValue2 = await page\n              .locator<HTMLSelectElement>(\"#version\")\n              .evaluate((el) => el.value);\n            expect(selectValue2).toEqual(\"1.x\");\n\n            await page.waitForFunction(() => {\n              const url = new URL(window.location.href);\n              return url.pathname === \"/docs/1.x/introduction\";\n            });\n          });\n        },\n      );\n    });\n  },\n});\n"
  },
  {
    "path": "www/routes/_app.tsx",
    "content": "import { asset } from \"fresh/runtime\";\nimport { define } from \"../utils/state.ts\";\n\nexport default define.page(function App({ Component, state, url }) {\n  return (\n    <html lang=\"en\">\n      <head>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        {state.title ? <title>{state.title}</title> : null}\n        {state.description\n          ? <meta name=\"description\" content={state.description} />\n          : null}\n        {state.title\n          ? <meta property=\"og:title\" content={state.title} />\n          : null}\n        {state.description\n          ? <meta property=\"og:description\" content={state.description} />\n          : null}\n        <meta property=\"og:type\" content=\"website\" />\n        <meta property=\"og:url\" content={url.href} />\n        {state.ogImage\n          ? <meta property=\"og:image\" content={state.ogImage} />\n          : null}\n        {state.noIndex ? <meta name=\"robots\" content=\"noindex\" /> : null}\n        <meta name=\"color-scheme\" content=\"light dark\" />\n        <link\n          rel=\"preload\"\n          href={asset(\"/fonts/FixelVariable.woff2\")}\n          as=\"font\"\n          type=\"font/woff2\"\n          crossorigin=\"anonymous\"\n        />\n        {url.pathname === \"/\"\n          ? <link rel=\"stylesheet\" href={asset(\"/prism.css\")} />\n          : null}\n        {url.pathname.startsWith(\"/docs/\")\n          ? (\n            <>\n              <link rel=\"stylesheet\" href={asset(\"/docsearch.css\")} />\n              <link rel=\"stylesheet\" href={asset(\"/markdown.css\")} />\n            </>\n          )\n          : null}\n        <script\n          type=\"module\"\n          // deno-lint-ignore react-no-danger\n          dangerouslySetInnerHTML={{\n            __html: `\nconst isDarkMode = localStorage.theme === \"dark\"\n  || (!(\"theme\" in localStorage)\n    && window.matchMedia(\"(prefers-color-scheme: dark)\").matches);\ndocument.documentElement.dataset.theme = isDarkMode ? \"dark\" : \"light\";`,\n          }}\n        >\n        </script>\n      </head>\n      <body>\n        <Component />\n      </body>\n    </html>\n  );\n});\n"
  },
  {
    "path": "www/routes/_error.tsx",
    "content": "import { HttpError, type PageProps } from \"fresh\";\nimport LemonDrop from \"../islands/LemonDrop.tsx\";\n\nexport function ServerCodePage(\n  props: { serverCode: number; codeDescription: string },\n) {\n  return (\n    <>\n      <section>\n        <div class=\"w-full flex justify-center items-center flex-col bg-green-300 mt-0 pt-8 !mb-0 bg-gradient-to-br from-blue-100 via-green-200 to-yellow-100\">\n          <LemonDrop />\n        </div>\n        <div class=\"text-center\">\n          <h1 class=\"text-6xl md:text-9xl font-extrabold\">\n            {props.serverCode}\n          </h1>\n\n          <p class=\"p-4 text-2xl md:text-3xl\">\n            {props.codeDescription}\n          </p>\n\n          <p class=\"p-4\">\n            <a href=\"/\" class=\"hover:underline\">Back to the Homepage</a>\n          </p>\n        </div>\n      </section>\n    </>\n  );\n}\n\nexport default function ErrorPage(props: PageProps) {\n  const error = props.error;\n  if (error instanceof HttpError) {\n    if (error.status === 404) {\n      return ServerCodePage({\n        serverCode: 404,\n        codeDescription: \"Couldn’t find what you’re looking for.\",\n      });\n    }\n  }\n\n  // deno-lint-ignore no-console\n  console.error(error);\n\n  return ServerCodePage({\n    serverCode: 500,\n    codeDescription: \"Oops! Something went wrong.\",\n  });\n}\n"
  },
  {
    "path": "www/routes/_middleware.ts",
    "content": "import type { Context } from \"fresh\";\nimport type { Event } from \"$ga4\";\nimport { GA4Report, isDocument, isServerError } from \"$ga4\";\n\nconst GA4_MEASUREMENT_ID = Deno.env.get(\"GA4_MEASUREMENT_ID\");\n\nlet showedMissingEnvWarning = false;\n\nfunction ga4<T>(\n  request: Request,\n  conn: Context<T>,\n  response: Response,\n  _start: number,\n  error?: unknown,\n) {\n  if (GA4_MEASUREMENT_ID === undefined) {\n    if (!showedMissingEnvWarning) {\n      showedMissingEnvWarning = true;\n      // deno-lint-ignore no-console\n      console.warn(\n        \"GA4_MEASUREMENT_ID environment variable not set. Google Analytics reporting disabled.\",\n      );\n    }\n    return;\n  }\n  Promise.resolve().then(async () => {\n    // We're tracking page views and file downloads. These are the only two\n    // HTTP methods that _might_ be used.\n    if (!/^(GET|POST)$/.test(request.method)) {\n      return;\n    }\n\n    // If the visitor is using a web browser, only create events when we serve\n    // a top level documents or download; skip assets like css, images, fonts.\n    if (!isDocument(request, response) && error == null) {\n      return;\n    }\n\n    let event: Event | null = null;\n    const contentType = response.headers.get(\"content-type\");\n    if (/text\\/html/.test(contentType!)) {\n      event = { name: \"page_view\", params: {} }; // Probably an old browser.\n    }\n\n    if (event == null && error == null) {\n      return;\n    }\n\n    // If an exception was thrown, build a separate event to report it.\n    let exceptionEvent;\n    if (error != null) {\n      exceptionEvent = {\n        name: \"exception\",\n        params: {\n          description: String(error),\n          fatal: isServerError(response),\n        },\n      };\n    } else {\n      exceptionEvent = undefined;\n    }\n\n    // Create basic report.\n    const measurementId = GA4_MEASUREMENT_ID;\n    // @ts-ignore GA4Report doesn't even use the localAddress parameter\n    const report = new GA4Report({\n      measurementId,\n      request,\n      response,\n      // Doesn't use localAddr\n      // deno-lint-ignore no-explicit-any\n      conn: conn.info as any,\n    });\n\n    // Override the default (page_view) event.\n    report.event = event;\n\n    // Add the exception event, if any.\n    if (exceptionEvent != null) {\n      report.events.push(exceptionEvent);\n    }\n\n    await report.send();\n  }).catch((err) => {\n    // deno-lint-ignore no-console\n    console.error(err);\n  });\n}\n\nexport async function handler<T>(\n  ctx: Context<T>,\n): Promise<Response> {\n  let err;\n  let res: Response;\n  const start = performance.now();\n  try {\n    const resp = await ctx.next();\n    const headers = new Headers(resp.headers);\n    res = new Response(resp.body, { status: resp.status, headers });\n    return res;\n  } catch (e) {\n    res = new Response(\"Internal Server Error\", {\n      status: 500,\n    });\n    err = e;\n    throw e;\n  } finally {\n    ga4(\n      ctx.req,\n      ctx,\n      res!,\n      start,\n      err,\n    );\n  }\n}\n"
  },
  {
    "path": "www/routes/docs/[...slug].tsx",
    "content": "import { HttpError, page } from \"fresh\";\nimport { asset, Partial } from \"fresh/runtime\";\nimport { SidebarCategory } from \"../../components/DocsSidebar.tsx\";\nimport Footer from \"../../components/Footer.tsx\";\nimport Header from \"../../components/Header.tsx\";\nimport * as Icons from \"../../components/Icons.tsx\";\nimport {\n  CATEGORIES,\n  getFirstPageUrl,\n  LATEST_VERSION,\n  TABLE_OF_CONTENTS,\n  type TableOfContentsEntry,\n} from \"../../data/docs.ts\";\nimport { frontMatter, renderMarkdown } from \"../../utils/markdown.ts\";\nimport toc from \"../../../docs/toc.ts\";\nimport { TableOfContents } from \"../../islands/TableOfContents.tsx\";\nimport SearchButton from \"../../islands/SearchButton.tsx\";\nimport VersionSelect from \"../../islands/VersionSelect.tsx\";\nimport { define } from \"../../utils/state.ts\";\n\ninterface Data {\n  page: Page;\n}\n\ninterface NavEntry {\n  title: string;\n  category?: string;\n  href: string;\n}\n\nexport interface VersionLink {\n  label: string;\n  href: string;\n  value: string;\n}\n\ninterface Page extends TableOfContentsEntry {\n  markdown: string;\n  data: Record<string, unknown>;\n  versionLinks: VersionLink[];\n  version: string;\n  prevNav?: NavEntry;\n  nextNav?: NavEntry;\n}\n\nconst pattern = new URLPattern({ pathname: \"/:version/:page*\" });\n\nexport const handler = define.handlers<Data>({\n  async GET(ctx) {\n    const slug = ctx.params.slug;\n\n    // Check if the slug is the index page of a version tag\n    if (TABLE_OF_CONTENTS[slug]) {\n      const href = getFirstPageUrl(slug);\n      return new Response(\"\", {\n        status: 307,\n        headers: { location: href },\n      });\n    }\n\n    const match = pattern.exec(\"https://localhost/\" + slug);\n    if (!match) {\n      throw new HttpError(404);\n    }\n\n    let { version, page: path = \"\" } = match.pathname.groups;\n    if (!version) {\n      throw new HttpError(404);\n    }\n\n    // Latest version doesn't show up in the url\n    if (!TABLE_OF_CONTENTS[version]) {\n      path = version + (path ? \"/\" + path : \"\");\n      version = LATEST_VERSION;\n    }\n\n    // Check if the page exists\n    const currentToc = TABLE_OF_CONTENTS[version];\n    const entry = currentToc[path];\n    if (!entry) {\n      throw new HttpError(404);\n    }\n\n    // Build up the link map for the version selector.\n    const versionLinks: VersionLink[] = [];\n    for (const version in TABLE_OF_CONTENTS) {\n      const label = toc[version].label;\n      const maybeEntry = TABLE_OF_CONTENTS[version][path];\n\n      // Check if the same page is available for this version and\n      // link to that. Pick the index page for that version if an\n      // exact match doesn't exist.\n      versionLinks.push({\n        label,\n        value: version,\n        href: maybeEntry ? maybeEntry.href : getFirstPageUrl(version),\n      });\n    }\n\n    // Add previous and next page entry if available\n\n    const entryKeys = Object.keys(currentToc);\n    const idx = entryKeys.findIndex((name) => name === entry.slug);\n\n    let nextNav: NavEntry | undefined;\n    let prevNav: NavEntry | undefined;\n    const prevEntry = currentToc[entryKeys[idx - 1]];\n    const nextEntry = currentToc[entryKeys[idx + 1]];\n\n    if (prevEntry) {\n      let category = prevEntry.category;\n      category = category ? currentToc[category].title : \"\";\n      prevNav = { title: prevEntry.title, category, href: prevEntry.href };\n    }\n\n    if (nextEntry) {\n      let category = nextEntry.category;\n      category = category ? currentToc[category].title : \"\";\n      nextNav = { title: nextEntry.title, category, href: nextEntry.href };\n    }\n\n    // Parse markdown front matter\n    // deno-lint-ignore no-explicit-any\n    const url = (import.meta as any).env.PROD\n      ? new URL(`../${entry.file}`, import.meta.url)\n      : new URL(`../../../${entry.file}`, import.meta.url);\n    const fileContent = await Deno.readTextFile(url);\n    const { body, attrs } = frontMatter<Record<string, unknown>>(fileContent);\n\n    ctx.state.title = `${entry.title ?? \"Not Found\"} | Fresh docs`;\n    ctx.state.description = attrs?.description\n      ? String(attrs.description)\n      : \"Fresh Document\";\n    ctx.state.ogImage = new URL(asset(\"/og-image.webp\"), ctx.url).href;\n\n    return page({\n      page: {\n        ...entry,\n        markdown: body,\n        data: attrs ?? {},\n        versionLinks,\n        version,\n        prevNav,\n        nextNav,\n      },\n    });\n  },\n});\n\nexport default define.page<typeof handler>(function DocsPage(props) {\n  const { page } = props.data;\n  const { html, headings } = renderMarkdown(page.markdown);\n\n  const isCanary = page.href.includes(\"/canary\");\n\n  return (\n    <div class=\"flex flex-col min-h-screen mx-auto max-w-screen-2xl\">\n      <Header title=\"docs\" active=\"/docs\" />\n      <div f-client-nav>\n        <MobileSidebar page={page} />\n        <div class=\"flex mx-auto max-w-screen-2xl px-0 md:px-4 md:py-0 justify-start bg-background-secondary\">\n          <label\n            for=\"docs_sidebar\"\n            class=\"px-4 py-3 lg:hidden flex items-center  rounded-sm gap-2 cursor-pointer\"\n          >\n            <svg\n              class=\"h-6 w-6\"\n              stroke=\"currentColor\"\n              fill=\"none\"\n              viewBox=\"0 0 24 24\"\n            >\n              <path\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                strokeWidth=\"2\"\n                d=\"M4 6h16M4 12h16M4 18h7\"\n              >\n              </path>\n            </svg>\n            <div>Table of Contents</div>\n          </label>\n        </div>\n        <nav class=\"flex-shrink-0 hidden lg:block lg:px-4 bg-white\">\n          <div class=\"fixed top-24 w-[17rem] flex overflow-hidden text-base\">\n            <div class=\"flex-1 h-[calc(100vh_-_6rem)] overflow-y-auto pb-8\">\n              <SearchButton class=\"mr-4 sm:mr-0\" />\n              <div class=\"mb-4 px-1\">\n                <VersionSelect\n                  selectedVersion={page.version}\n                  versions={page.versionLinks}\n                />\n              </div>\n              <ul class=\"list-inside font-semibold nested ml-2.5\">\n                {CATEGORIES[page.version].map((category) => (\n                  <SidebarCategory key={category.href} category={category} />\n                ))}\n              </ul>\n            </div>\n          </div>\n        </nav>\n        <Partial name=\"docs-main\">\n          <div class=\"w-full min-w-0\">\n            <main class=\"lg:ml-[18rem] mt-4 min-w-0 mx-auto\">\n              <div class=\"flex gap-6 md:gap-8 xl:gap-[8%] flex-col xl:flex-row md:mx-8 lg:mx-16 2xl:mx-0 lg:justify-end\">\n                <TableOfContents headings={headings} />\n\n                <div class=\"lg:order-1 min-w-0 max-w-3xl w-full\">\n                  {isCanary\n                    ? (\n                      <div class=\"bg-[#F0900525] p-4 rounded-sm text-base text-yellow-700 dark:text-yellow-500 mb-8\">\n                        🚧 This documentation is work in progress and for an\n                        unreleased version of Fresh.\n                      </div>\n                    )\n                    : null}\n                  <h1 class=\"text-4xl text-foreground-primary tracking-tight font-bold md:mt-0 px-4 md:px-0 mb-4\">\n                    {page.title}\n                  </h1>\n                  <div\n                    class=\"markdown-body mb-8\"\n                    // deno-lint-ignore react-no-danger\n                    dangerouslySetInnerHTML={{ __html: html }}\n                  />\n\n                  <div class=\"mb-8\">\n                    <ForwardBackButtons\n                      slug={page.slug}\n                      version={page.version}\n                      prev={page.prevNav}\n                      next={page.nextNav}\n                    />\n                  </div>\n                  <hr />\n                  <div class=\"px-4 md:px-0 flex justify-between my-6\">\n                    <a\n                      href={`https://github.com/denoland/fresh/edit/main/${page.file}`}\n                      class=\"text-gray-700 dark:text-gray-200 text-md flex items-center bg-[#ebedf0] dark:bg-[#2c2d39] px-4 py-2 rounded-sm hover:bg-gray-200 dark:hover:bg-[#36394c] transition-colors\"\n                      target=\"_blank\"\n                      rel=\"noopener noreferrer\"\n                    >\n                      <span class=\"mr-2 inline-flex\">Edit this page</span>\n                      <Icons.GitHub />\n                    </a>\n                  </div>\n                </div>\n              </div>\n              <div class=\"xl:ml-[3.75rem]\">\n                <Footer />\n              </div>\n            </main>\n          </div>\n        </Partial>\n      </div>\n    </div>\n  );\n});\n\nfunction MobileSidebar({ page }: { page: Page }) {\n  return (\n    <div class=\"lg:hidden\">\n      <input\n        type=\"checkbox\"\n        class=\"hidden toggle\"\n        id=\"docs_sidebar\"\n        autocomplete=\"off\"\n      />\n      <div class=\"fixed inset-0 flex z-50 hidden toggled\">\n        <label\n          class=\"absolute inset-0 bg-gray-600 opacity-75\"\n          for=\"docs_sidebar\"\n        />\n        <div class=\"relative flex-1 flex flex-col w-[18rem] h-full bg-background-primary border-r-2 border-foreground-secondary\">\n          <nav class=\"pt-0 pb-16 overflow-x-auto\">\n            <div class=\"flex-1 h-screen overflow-y-auto pt-4 px-4\">\n              <SearchButton class=\"mr-4 sm:mr-0\" />\n              <div class=\"mb-4\">\n                <VersionSelect\n                  selectedVersion={page.version}\n                  versions={page.versionLinks}\n                />\n              </div>\n              <ul class=\"list-inside font-semibold nested ml-2.5\">\n                {CATEGORIES[page.version].map((category) => (\n                  <SidebarCategory key={category.href} category={category} />\n                ))}\n              </ul>\n            </div>\n          </nav>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction ForwardBackButtons(props: {\n  slug: string;\n  version: string;\n  prev?: NavEntry;\n  next?: NavEntry;\n}) {\n  const { prev, next } = props;\n\n  return (\n    <div class=\"px-4 md:px-0 mt-8 flex flex-col sm:flex-row gap-4 justify-between\">\n      {prev\n        ? (\n          <a\n            href={prev.href}\n            class=\"px-4 py-2 text-left rounded-sm border border-foreground-secondary/20 grid border-solid w-full hover:border-green-600 transition-colors\"\n          >\n            <span class=\"text-sm text-gray-600 dark:text-gray-500\">\n              Previous page\n            </span>\n            <span class=\"text-green-600 dark:text-green-400 font-medium\">\n              {prev.title}\n            </span>\n          </a>\n        )\n        : <div class=\"w-full\" />}\n      {next\n        ? (\n          <a\n            href={next.href}\n            class=\"px-4 py-2 text-left rounded-sm border border-foreground-secondary/20 grid border-solid w-full hover:border-green-600 transition-colors\"\n          >\n            <span class=\"text-sm text-gray-600 dark:text-gray-500\">\n              Next page\n            </span>\n            <span class=\"text-green-600 dark:text-green-400 font-medium\">\n              {next.title}\n            </span>\n          </a>\n        )\n        : <div class=\"w-full\" />}\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/routes/docs/_layout.tsx",
    "content": "import type { PageProps } from \"@fresh/core\";\n\nexport default function Layout({ Component }: PageProps) {\n  return (\n    <div class=\"layout\">\n      <div class=\"bg-background-primary text-foreground-primary\">\n        <Component />\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "www/routes/docs/_middleware.ts",
    "content": "import type { Context } from \"fresh\";\n\nconst REDIRECTS: Record<string, string> = {\n  \"/docs/getting-started/fetching-data\":\n    \"/docs/getting-started/custom-handlers\",\n  \"/docs/canary/examples/modifying-the-head\": \"/docs/advanced/head\",\n  \"/docs/canary/concepts/builder\": \"/docs/advanced/builder\",\n};\n\nexport async function handler<T>(ctx: Context<T>) {\n  // Redirect from old doc URLs to new ones\n  const redirect = REDIRECTS[ctx.url.pathname];\n  if (redirect) {\n    const url = new URL(redirect, ctx.url.origin);\n    return new Response(\"\", {\n      status: 307,\n      headers: { location: url.href },\n    });\n  }\n\n  return await ctx.next();\n}\n"
  },
  {
    "path": "www/routes/docs/index.tsx",
    "content": "import { define } from \"../../utils/state.ts\";\n\nexport const handler = define.handlers({\n  GET(ctx) {\n    return ctx.url.pathname === \"/concepts/architechture\"\n      ? ctx.redirect(\"/docs/concepts/architecture\")\n      : ctx.redirect(\"/docs/introduction\");\n  },\n});\n"
  },
  {
    "path": "www/routes/index.tsx",
    "content": "import { asset } from \"fresh/runtime\";\nimport { page } from \"fresh\";\nimport VERSIONS from \"../../versions.json\" with { type: \"json\" };\nimport Footer from \"../components/Footer.tsx\";\nimport Header from \"../components/Header.tsx\";\nimport { CTA } from \"../components/homepage/CTA.tsx\";\nimport { Hero } from \"../components/homepage/Hero.tsx\";\nimport { IslandsSection } from \"../components/homepage/IslandsSection.tsx\";\nimport { PartialsSection } from \"../components/homepage/PartialsSection.tsx\";\nimport { RenderingSection } from \"../components/homepage/RenderingSection.tsx\";\nimport { FormsSection } from \"../components/homepage/FormsSection.tsx\";\nimport { Simple } from \"../components/homepage/Simple.tsx\";\nimport { SocialProof } from \"../components/homepage/SocialProof.tsx\";\nimport { DenoSection } from \"../components/homepage/DenoSection.tsx\";\nimport { define } from \"../utils/state.ts\";\n\nexport const handler = define.handlers({\n  GET(ctx) {\n    const { req } = ctx;\n    const accept = req.headers.get(\"accept\");\n    const userAgent = req.headers.get(\"user-agent\");\n    if (userAgent?.includes(\"Deno/\") && !accept?.includes(\"text/html\")) {\n      const path = `https://deno.land/x/fresh@${VERSIONS[0]}/init.ts`;\n      return new Response(`Redirecting to ${path}`, {\n        headers: { \"Location\": path },\n        status: 307,\n      });\n    }\n\n    ctx.state.title =\n      \"Fresh - The simple, approachable, productive web framework.\";\n    ctx.state.description =\n      \"Fresh features just-in-time edge rendering, island based interactivity, and zero-configuration TypeScript support. Fast to write; fast to run.\";\n    ctx.state.ogImage = new URL(asset(\"/og-image.webp\"), ctx.url).href;\n\n    return page();\n  },\n  async POST(ctx) {\n    const headers = new Headers();\n    const form = await ctx.req.formData();\n    const treat = form.get(\"treat\");\n    headers.set(\"location\", `/thanks?vote=${treat}`);\n    return new Response(null, {\n      status: 303,\n      headers,\n    });\n  },\n});\n\nexport default define.page<typeof handler>(function MainPage() {\n  return (\n    <div class=\"flex flex-col min-h-screen bg-white\">\n      <div class=\"bg-transparent flex flex-col relative z-10\">\n        <HelloBar />\n        <Header title=\"\" active=\"/\" />\n      </div>\n      <div class=\"flex flex-col -mt-20 relative\">\n        <Hero />\n        <Simple />\n        <RenderingSection />\n        <IslandsSection />\n        <FormsSection />\n        <PartialsSection />\n        <SocialProof />\n        <DenoSection />\n        <CTA />\n      </div>\n      <Footer class=\"!mt-0\" />\n    </div>\n  );\n});\n\nfunction HelloBar() {\n  return (\n    <a\n      class=\"bg-gradient-to-r from-blue-200 to-yellow-200 via-green-300 text-black border-b border-green-400 p-4 text-center group\"\n      href=\"https://deno.com/blog/fresh-and-vite\"\n    >\n      Fresh 2 <b>released with Vite</b>{\" \"}\n      <span class=\"group-hover:underline\">→</span>\n    </a>\n  );\n}\n"
  },
  {
    "path": "www/routes/raw.ts",
    "content": "import type { RouteConfig } from \"fresh\";\nimport { format, parse } from \"@std/semver\";\nimport VERSIONS from \"../../versions.json\" with { type: \"json\" };\nimport { extname } from \"@std/path\";\nimport { define } from \"../utils/state.ts\";\n\nconst BASE_URL = \"https://raw.githubusercontent.com/denoland/fresh/\";\n\nconst contentTypes = new Map([\n  [\".html\", \"text/plain\"],\n  [\".ts\", \"application/typescript\"],\n  [\".js\", \"application/javascript\"],\n  [\".tsx\", \"text/tsx\"],\n  [\".jsx\", \"text/jsx\"],\n  [\".json\", \"application/json\"],\n  [\".wasm\", \"application/wasm\"],\n]);\n\nexport const handler = define.handlers({\n  async GET(ctx) {\n    const accept = ctx.req.headers.get(\"Accept\");\n    const isHTML = accept?.includes(\"text/html\");\n    const { version, path } = ctx.params;\n\n    const semver = parse(version);\n    if (!semver) {\n      return new Response(\"Invalid version\", { status: 400 });\n    }\n\n    if (!VERSIONS.includes(format(semver))) {\n      return new Response(\"Version not found\", { status: 404 });\n    }\n\n    const url = `${BASE_URL}${format(semver)}/${path}`;\n    const r = await fetch(url, { redirect: \"manual\" });\n    const response = new Response(r.body, r);\n    response.headers.delete(\"content-encoding\");\n\n    if (response.status === 404) {\n      return new Response(\n        \"404: Not Found. The requested Fresh release or file do not exist.\",\n        { status: 404 },\n      );\n    }\n\n    if (!response.ok) {\n      response.headers.set(\"Content-Type\", \"text/plain\");\n      return response;\n    }\n\n    const value = isHTML\n      ? \"text/plain\"\n      : contentTypes.get(extname(path)) ?? \"text/plain\";\n    response.headers.set(\"Content-Type\", value);\n\n    return response;\n  },\n});\n\nexport const config: RouteConfig = {\n  routeOverride: \"/@:version/:path*\",\n};\n"
  },
  {
    "path": "www/routes/recipes/_layout.tsx",
    "content": "import type { PageProps } from \"fresh\";\nimport Header from \"../../components/Header.tsx\";\n\nexport default function Layout({ Component }: PageProps) {\n  return (\n    <>\n      <Header active=\"\" title=\"\" />\n\n      <div class=\"w-full max-w-screen-xl px-4\">\n        <Component />\n        <p class=\"mt-16\">\n          This route is used only for demo purposes;{\" \"}\n          <a href=\"/\" class=\"underline\">see the home page</a>.\n        </p>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "www/routes/recipes/_middleware.ts",
    "content": "import { define } from \"../../utils/state.ts\";\n\nexport const handler = define.middleware((ctx) => {\n  ctx.state.noIndex = true;\n  return ctx.next();\n});\n"
  },
  {
    "path": "www/routes/recipes/lemon-honey-tea.tsx",
    "content": "import { Partial } from \"fresh/runtime\";\nimport type { RouteConfig } from \"fresh\";\n\nexport const config: RouteConfig = {\n  skipAppWrapper: true,\n  skipInheritedLayouts: true,\n};\n\nexport const LemonHoneyTea = () => (\n  <Partial name=\"recipe\">\n    <h2 class=\"mb-2 font-extrabold\">Lemon-honey tea</h2>\n    <ul>\n      <li>1 cup boiling water</li>\n      <li>¼ tsp black tea</li>\n      <li>2 tsp honey</li>\n      <li>1 tsp lemon juice</li>\n      <li>Lemon peel</li>\n    </ul>\n  </Partial>\n);\n\nexport default LemonHoneyTea;\n"
  },
  {
    "path": "www/routes/recipes/lemonade.tsx",
    "content": "import { Partial } from \"fresh/runtime\";\nimport type { RouteConfig } from \"fresh\";\n\nexport const config: RouteConfig = {\n  skipAppWrapper: true,\n  skipInheritedLayouts: true,\n};\n\nexport const Lemonade = () => (\n  <Partial name=\"recipe\">\n    <h2 class=\"mb-2 font-extrabold\">Lemonade</h2>\n    <ul>\n      <li>1 ¾ cups white sugar</li>\n      <li>1 cup water</li>\n      <li>9 lemons</li>\n      <li>7 cups ice water</li>\n      <li>Ice</li>\n    </ul>\n  </Partial>\n);\n\nexport default Lemonade;\n"
  },
  {
    "path": "www/routes/recipes/lemondrop.tsx",
    "content": "import { Partial } from \"fresh/runtime\";\nimport type { RouteConfig } from \"fresh\";\n\nexport const config: RouteConfig = {\n  skipAppWrapper: true,\n  skipInheritedLayouts: true,\n};\n\nexport const LemonDrop = () => (\n  <Partial name=\"recipe\">\n    <h2 class=\"mb-2 font-extrabold\">Lemondrop Martini</h2>\n    <ul>\n      <li>2 oz vodka</li>\n      <li>3/4 oz triple sec</li>\n      <li>\n        1 oz fresh lemon juice\n      </li>\n      <li>3/4 oz simple syrup</li>\n    </ul>\n  </Partial>\n);\n\nexport default LemonDrop;\n"
  },
  {
    "path": "www/routes/showcase-bak.tsx",
    "content": "import { asset } from \"fresh/runtime\";\nimport { page } from \"fresh\";\nimport Projects, { type Project } from \"../components/Projects.tsx\";\nimport Header from \"../components/Header.tsx\";\nimport Footer from \"../components/Footer.tsx\";\nimport projects from \"../data/showcase.json\" with { type: \"json\" };\nimport { define } from \"../utils/state.ts\";\n\nconst TITLE = \"Showcase | Fresh\";\nconst DESCRIPTION = \"Selection of projects that have been built with Fresh.\";\n\nexport const handler = define.handlers({\n  GET(ctx) {\n    ctx.state.title = TITLE;\n    ctx.state.description = DESCRIPTION;\n    ctx.state.ogImage = new URL(asset(\"/og-image.webp\"), ctx.url).href;\n    return page();\n  },\n});\n\nexport default define.page<typeof handler>(function ShowcasePage() {\n  return (\n    <>\n      <Header title=\"showcase\" active=\"/showcase\" />\n\n      <div class=\"flex flex-col min-h-screen bg-white text-black\">\n        <div class=\"flex-1\">\n          <Showcase items={projects} />\n\n          <section class=\"max-w-screen-lg mx-auto my-16 px-4 sm:px-6 md:px-8 space-y-4\">\n            <h2 class=\"text-3xl text-gray-600 font-bold\">\n              Badge\n            </h2>\n\n            <p class=\"text-gray-600\">\n              You can add these stylish badges to your project’s README to show\n              that it was built with Fresh.\n            </p>\n\n            <img\n              width=\"197\"\n              height=\"37\"\n              src=\"https://fresh.deno.dev/fresh-badge.svg\"\n              alt=\"Made with Fresh\"\n            />\n\n            <img\n              width=\"197\"\n              height=\"37\"\n              src=\"https://fresh.deno.dev/fresh-badge-dark.svg\"\n              alt=\"Made with Fresh\"\n            />\n\n            <p>\n              <a\n                href=\"https://github.com/denoland/fresh#badges\"\n                class=\"text-blue-600 hover:underline focus:underline\"\n              >\n                Usage instructions\n              </a>\n            </p>\n          </section>\n\n          <img\n            src=\"/illustration/deno-plush.svg\"\n            alt=\"a deno plush is holding a lemon\"\n            class=\"mx-auto w-48 mt-16\"\n          />\n        </div>\n\n        <Footer />\n      </div>\n    </>\n  );\n});\n\nfunction Showcase({ items }: { items: Project[] }) {\n  return (\n    <section class=\"max-w-screen-lg mx-auto my-16 px-4 sm:px-6 md:px-8 space-y-4\">\n      <h2 class=\"text-3xl text-foreground-primary font-bold \">\n        Showcase\n      </h2>\n      <p class=\"text-foreground-secondary\">\n        Below is a selection of projects that have been built with Fresh.{\" \"}\n        <a\n          href=\"https://github.com/denoland/fresh/blob/main/www/data/showcase.json\"\n          class=\"text-blue-600 hover:underline\"\n        >\n          Add yours!\n        </a>\n      </p>\n      <Projects items={items} class=\"gap-16\" />\n    </section>\n  );\n}\n"
  },
  {
    "path": "www/routes/showcase.tsx",
    "content": "import { asset } from \"fresh/runtime\";\nimport { page } from \"fresh\";\nimport Projects, { type Project } from \"../components/Projects.tsx\";\nimport Header from \"../components/Header.tsx\";\nimport Footer from \"../components/Footer.tsx\";\nimport projects from \"../data/showcase.json\" with { type: \"json\" };\nimport { define } from \"../utils/state.ts\";\n\nconst TITLE = \"Showcase | Fresh\";\nconst DESCRIPTION = \"Selection of projects that have been built with Fresh.\";\n\nexport const handler = define.handlers({\n  GET(ctx) {\n    ctx.state.title = TITLE;\n    ctx.state.description = DESCRIPTION;\n    ctx.state.ogImage = new URL(asset(\"/og-image.webp\"), ctx.url).href;\n    return page();\n  },\n});\n\nexport default define.page<typeof handler>(function ShowcasePage() {\n  return (\n    <div class=\"bg-white\">\n      <Header title=\"showcase\" active=\"/showcase\" />\n\n      <div class=\"flex flex-col min-h-screen\">\n        <div class=\"flex-1\">\n          <Showcase items={projects} />\n\n          <section class=\"max-w-screen-lg mx-auto my-16 px-4 sm:px-6 md:px-8 space-y-4\">\n            <h2 class=\"text-3xl text-gray-600 font-bold\">\n              Badge\n            </h2>\n\n            <p class=\"text-gray-600\">\n              You can add these stylish badges to your project’s README to show\n              that it was built with Fresh.\n            </p>\n\n            <img\n              width=\"197\"\n              height=\"37\"\n              src=\"https://fresh.deno.dev/fresh-badge.svg\"\n              alt=\"Made with Fresh\"\n            />\n\n            <img\n              width=\"197\"\n              height=\"37\"\n              src=\"https://fresh.deno.dev/fresh-badge-dark.svg\"\n              alt=\"Made with Fresh\"\n            />\n\n            <p>\n              <a\n                href=\"https://github.com/denoland/fresh#badges\"\n                class=\"text-blue-600 hover:underline focus:underline\"\n              >\n                Usage instructions\n              </a>\n            </p>\n          </section>\n\n          <img\n            src=\"/illustration/deno-plush.svg\"\n            alt=\"a deno plush is holding a lemon\"\n            class=\"mx-auto w-48 mt-16\"\n          />\n        </div>\n\n        <Footer />\n      </div>\n    </div>\n  );\n});\n\nfunction Showcase({ items }: { items: Project[] }) {\n  return (\n    <section class=\"max-w-screen-lg mx-auto my-16 px-4 sm:px-6 md:px-8 space-y-4\">\n      <h2 class=\"text-3xl text-gray-600 font-bold\">\n        Showcase\n      </h2>\n      <p class=\"text-gray-600\">\n        Below is a selection of projects that have been built with Fresh.{\" \"}\n        <a\n          href=\"https://github.com/denoland/fresh/blob/main/www/data/showcase.json\"\n          class=\"text-blue-600 hover:underline\"\n        >\n          Add yours!\n        </a>\n      </p>\n      <Projects items={items} class=\"gap-16\" />\n    </section>\n  );\n}\n"
  },
  {
    "path": "www/routes/thanks.tsx",
    "content": "import { page } from \"fresh\";\nimport { PageSection } from \"../components/PageSection.tsx\";\nimport { DemoBox } from \"../components/homepage/DemoBox.tsx\";\nimport { define } from \"../utils/state.ts\";\n\nexport const handler = define.handlers({\n  GET(ctx) {\n    const search = new URLSearchParams(ctx.url.search);\n    const vote = search.get(\"vote\");\n    return page({ vote });\n  },\n});\n\nexport default define.page<typeof handler>(function ThanksForSubscribing(\n  props,\n) {\n  const vote = props.data.vote ? props.data.vote.replaceAll(/-/g, \" \") : null;\n  return (\n    <>\n      <PageSection>\n        <DemoBox>\n          <div class=\"space-y-2\">\n            <h1 class=\"text-2xl md:text-3xl lg:text-4xl\">\n              {vote\n                ? `Thanks for voting for ${vote}!`\n                : `Form submitted successfully`}\n            </h1>\n            <p>\n              That was all handled server-side, with{\" \"}\n              <strong>no client-side JavaScript</strong>! Nifty, huh?\n            </p>\n            <p class=\"!mt-8\">\n              …Anyway, you probably want to{\" \"}\n              <a href=\"/#forms-section\" class=\"underline\">go back now</a>.\n            </p>\n            <a\n              href=\"/#forms-section\"\n              class=\"flex items-center gap-2 max-w-max !mt-4 mx-auto pt-3 pb-2 px-4 border-[1.5px] border-current rounded-sm\"\n            >\n              <svg\n                aria-hidden=\"true\"\n                xmlns=\"http://www.w3.org/2000/svg\"\n                class=\"icon icon-tabler icon-tabler-arrow-right relative bottom-[0.05em] transition-transform ease-out duration-150\"\n                width=\"1em\"\n                height=\"1em\"\n                viewBox=\"0 0 24 24\"\n                stroke-width=\"3\"\n                stroke=\"currentColor\"\n                fill=\"none\"\n                stroke-linecap=\"round\"\n                stroke-linejoin=\"round\"\n                style=\"transform:rotateY(180deg);\"\n              >\n                <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\n                <path d=\"M5 12l14 0\" />\n                <path d=\"M13 18l6 -6\" />\n                <path d=\"M13 6l6 6\" />\n              </svg>\n              Back\n            </a>\n          </div>\n        </DemoBox>\n      </PageSection>\n    </>\n  );\n});\n"
  },
  {
    "path": "www/routes/update.tsx",
    "content": "import { define } from \"../utils/state.ts\";\nimport VERSIONS from \"../../versions.json\" with { type: \"json\" };\n\nexport const handler = define.handlers({\n  GET({ req }) {\n    const accept = req.headers.get(\"accept\");\n    let path = \"/docs/concepts/updating\";\n    if (accept && !accept.includes(\"text/html\")) {\n      path = `https://deno.land/x/fresh@${VERSIONS[0]}/update.ts`;\n    }\n    return new Response(`Redirecting to ${path}`, {\n      headers: { \"Location\": path },\n      status: 307,\n    });\n  },\n});\n"
  },
  {
    "path": "www/static/docsearch.css",
    "content": "/**\n * Skipped minification because the original files appears to be already minified.\n * Original file: /npm/@docsearch/css@3.3.0/dist/style.css\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n/*! @docsearch/css 3.3.0 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */\n:root {\n  --docsearch-primary-color: #5468ff;\n  --docsearch-text-color: #1c1e21;\n  --docsearch-spacing: 12px;\n  --docsearch-icon-stroke-width: 1.4;\n  --docsearch-highlight-color: var(--docsearch-primary-color);\n  --docsearch-muted-color: #969faf;\n  --docsearch-container-background: rgba(101, 108, 133, 0.8);\n  --docsearch-logo-color: #5468ff;\n  --docsearch-modal-width: 560px;\n  --docsearch-modal-height: 600px;\n  --docsearch-modal-background: #f5f6f7;\n  --docsearch-modal-shadow:\n    inset 1px 1px 0 0 hsla(0, 0%, 100%, 0.5),0 3px 8px 0 #555a64;\n  --docsearch-searchbox-height: 56px;\n  --docsearch-searchbox-background: #ebedf0;\n  --docsearch-searchbox-focus-background: #fff;\n  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);\n  --docsearch-hit-height: 56px;\n  --docsearch-hit-color: #444950;\n  --docsearch-hit-active-color: #fff;\n  --docsearch-hit-background: #fff;\n  --docsearch-hit-shadow: 0 1px 3px 0 #d4d9e1;\n  --docsearch-key-gradient: linear-gradient(-225deg, #d5dbe4, #f8f8f8);\n  --docsearch-key-shadow:\n    inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(\n    30,\n    35,\n    90,\n    0.4\n  );\n  --docsearch-footer-height: 44px;\n  --docsearch-footer-background: #fff;\n  --docsearch-footer-shadow:\n    0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69, 98, 155, 0.12);\n}\nhtml[data-theme=\"dark\"] {\n  --docsearch-text-color: #f5f6f7;\n  --docsearch-container-background: rgba(9, 10, 17, 0.8);\n  --docsearch-modal-background: #15172a;\n  --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;\n  --docsearch-searchbox-background: #2b2d3c;\n  --docsearch-searchbox-focus-background: #000;\n  --docsearch-hit-color: #bec3c9;\n  --docsearch-hit-shadow: none;\n  --docsearch-hit-background: #090a11;\n  --docsearch-key-gradient: linear-gradient(-26.5deg, #565872, #31355b);\n  --docsearch-key-shadow:\n    inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(\n    3,\n    4,\n    9,\n    0.3\n  );\n  --docsearch-footer-background: #1e2136;\n  --docsearch-footer-shadow:\n    inset 0 1px 0 0 rgba(73, 76, 106, 0.5),0 -4px 8px 0 rgba(0, 0, 0, 0.2);\n  --docsearch-logo-color: #fff;\n  --docsearch-muted-color: #7f8497;\n}\n.DocSearch-Button {\n  align-items: center;\n  background: var(--docsearch-searchbox-background);\n  border: 0;\n  border-radius: 40px;\n  color: var(--docsearch-muted-color);\n  cursor: pointer;\n  display: flex;\n  font-weight: 500;\n  height: 36px;\n  justify-content: space-between;\n  margin: 0 0 0 16px;\n  padding: 0 8px;\n  user-select: none;\n}\n.DocSearch-Button:active, .DocSearch-Button:focus, .DocSearch-Button:hover {\n  background: var(--docsearch-searchbox-focus-background);\n  box-shadow: var(--docsearch-searchbox-shadow);\n  color: var(--docsearch-text-color);\n  outline: none;\n}\n.DocSearch-Button-Container {\n  align-items: center;\n  display: flex;\n}\n.DocSearch-Search-Icon {\n  stroke-width: 1.6;\n}\n.DocSearch-Button .DocSearch-Search-Icon {\n  color: var(--docsearch-text-color);\n}\n.DocSearch-Button-Placeholder {\n  font-size: 1rem;\n  padding: 0 12px 0 6px;\n}\n.DocSearch-Button-Keys {\n  display: flex;\n  min-width: calc(40px + 0.8em);\n}\n.DocSearch-Button-Key {\n  align-items: center;\n  background: var(--docsearch-key-gradient);\n  border-radius: 3px;\n  box-shadow: var(--docsearch-key-shadow);\n  color: var(--docsearch-muted-color);\n  display: flex;\n  height: 18px;\n  justify-content: center;\n  margin-right: 0.4em;\n  position: relative;\n  padding: 0 0 2px;\n  border: 0;\n  top: -1px;\n  width: 20px;\n}\n@media (max-width: 768px) {\n  .DocSearch-Button-Keys, .DocSearch-Button-Placeholder {\n    display: none;\n  }\n}\n.DocSearch--active {\n  overflow: hidden !important;\n}\n.DocSearch-Container, .DocSearch-Container * {\n  box-sizing: border-box;\n}\n.DocSearch-Container {\n  background-color: var(--docsearch-container-background);\n  height: 100vh;\n  left: 0;\n  position: fixed;\n  top: 0;\n  width: 100vw;\n  z-index: 200;\n}\n.DocSearch-Container a {\n  text-decoration: none;\n}\n.DocSearch-Link {\n  appearance: none;\n  background: none;\n  border: 0;\n  color: var(--docsearch-highlight-color);\n  cursor: pointer;\n  font: inherit;\n  margin: 0;\n  padding: 0;\n}\n.DocSearch-Modal {\n  background: var(--docsearch-modal-background);\n  border-radius: 6px;\n  box-shadow: var(--docsearch-modal-shadow);\n  flex-direction: column;\n  margin: 60px auto auto;\n  max-width: var(--docsearch-modal-width);\n  position: relative;\n}\n.DocSearch-SearchBar {\n  display: flex;\n  padding: var(--docsearch-spacing) var(--docsearch-spacing) 0;\n}\n.DocSearch-Form {\n  align-items: center;\n  background: var(--docsearch-searchbox-focus-background);\n  border-radius: 4px;\n  box-shadow: var(--docsearch-searchbox-shadow);\n  display: flex;\n  height: var(--docsearch-searchbox-height);\n  margin: 0;\n  padding: 0 var(--docsearch-spacing);\n  position: relative;\n  width: 100%;\n}\n.DocSearch-Input {\n  appearance: none;\n  background: transparent;\n  border: 0;\n  color: var(--docsearch-text-color);\n  flex: 1;\n  font: inherit;\n  font-size: 1.2em;\n  height: 100%;\n  outline: none;\n  padding: 0 0 0 8px;\n  width: 80%;\n}\n.DocSearch-Input::placeholder {\n  color: var(--docsearch-muted-color);\n  opacity: 1;\n}\n.DocSearch-Input::-webkit-search-cancel-button,\n.DocSearch-Input::-webkit-search-decoration,\n.DocSearch-Input::-webkit-search-results-button,\n.DocSearch-Input::-webkit-search-results-decoration {\n  display: none;\n}\n.DocSearch-LoadingIndicator, .DocSearch-MagnifierLabel, .DocSearch-Reset {\n  margin: 0;\n  padding: 0;\n}\n.DocSearch-MagnifierLabel, .DocSearch-Reset {\n  align-items: center;\n  color: var(--docsearch-highlight-color);\n  display: flex;\n  justify-content: center;\n}\n.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,\n.DocSearch-LoadingIndicator {\n  display: none;\n}\n.DocSearch-Container--Stalled .DocSearch-LoadingIndicator {\n  align-items: center;\n  color: var(--docsearch-highlight-color);\n  display: flex;\n  justify-content: center;\n}\n@media screen and (prefers-reduced-motion: reduce) {\n  .DocSearch-Reset {\n    animation: none;\n    appearance: none;\n    background: none;\n    border: 0;\n    border-radius: 50%;\n    color: var(--docsearch-icon-color);\n    cursor: pointer;\n    right: 0;\n    stroke-width: var(--docsearch-icon-stroke-width);\n  }\n}\n.DocSearch-Reset {\n  animation: fade-in 0.1s ease-in forwards;\n  appearance: none;\n  background: none;\n  border: 0;\n  border-radius: 50%;\n  color: var(--docsearch-icon-color);\n  cursor: pointer;\n  padding: 2px;\n  right: 0;\n  stroke-width: var(--docsearch-icon-stroke-width);\n}\n.DocSearch-Reset[hidden] {\n  display: none;\n}\n.DocSearch-Reset:focus {\n  outline: none;\n}\n.DocSearch-Reset:hover {\n  color: var(--docsearch-highlight-color);\n}\n.DocSearch-LoadingIndicator svg, .DocSearch-MagnifierLabel svg {\n  height: 24px;\n  width: 24px;\n}\n.DocSearch-Cancel {\n  display: none;\n}\n.DocSearch-Dropdown {\n  max-height: calc(\n    var(--docsearch-modal-height) - var(--docsearch-searchbox-height)\n      - var(--docsearch-spacing)\n      - var(--docsearch-footer-height)\n  );\n  min-height: var(--docsearch-spacing);\n  overflow-y: auto;\n  overflow-y: overlay;\n  padding: 0 var(--docsearch-spacing);\n  scrollbar-color: var(--docsearch-muted-color)\n    var(--docsearch-modal-background);\n  scrollbar-width: thin;\n}\n.DocSearch-Dropdown::-webkit-scrollbar {\n  width: 12px;\n}\n.DocSearch-Dropdown::-webkit-scrollbar-track {\n  background: transparent;\n}\n.DocSearch-Dropdown::-webkit-scrollbar-thumb {\n  background-color: var(--docsearch-muted-color);\n  border: 3px solid var(--docsearch-modal-background);\n  border-radius: 20px;\n}\n.DocSearch-Dropdown ul {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n.DocSearch-Label {\n  font-size: 0.75em;\n  line-height: 1.6em;\n}\n.DocSearch-Help, .DocSearch-Label {\n  color: var(--docsearch-muted-color);\n}\n.DocSearch-Help {\n  font-size: 0.9em;\n  margin: 0;\n  user-select: none;\n}\n.DocSearch-Title {\n  font-size: 1.2em;\n}\n.DocSearch-Logo a {\n  display: flex;\n}\n.DocSearch-Logo svg {\n  color: var(--docsearch-logo-color);\n  margin-left: 8px;\n}\n.DocSearch-Hits:last-of-type {\n  margin-bottom: 24px;\n}\n.DocSearch-Hits mark {\n  background: none;\n  color: var(--docsearch-highlight-color);\n}\n.DocSearch-HitsFooter {\n  color: var(--docsearch-muted-color);\n  display: flex;\n  font-size: 0.85em;\n  justify-content: center;\n  margin-bottom: var(--docsearch-spacing);\n  padding: var(--docsearch-spacing);\n}\n.DocSearch-HitsFooter a {\n  border-bottom: 1px solid;\n  color: inherit;\n}\n.DocSearch-Hit {\n  border-radius: 4px;\n  display: flex;\n  padding-bottom: 4px;\n  position: relative;\n}\n@media screen and (prefers-reduced-motion: reduce) {\n  .DocSearch-Hit--deleting {\n    transition: none;\n  }\n}\n.DocSearch-Hit--deleting {\n  opacity: 0;\n  transition: all 0.25s linear;\n}\n@media screen and (prefers-reduced-motion: reduce) {\n  .DocSearch-Hit--favoriting {\n    transition: none;\n  }\n}\n.DocSearch-Hit--favoriting {\n  transform: scale(0);\n  transform-origin: top center;\n  transition: all 0.25s linear;\n  transition-delay: 0.25s;\n}\n.DocSearch-Hit a {\n  background: var(--docsearch-hit-background);\n  border-radius: 4px;\n  box-shadow: var(--docsearch-hit-shadow);\n  display: block;\n  padding-left: var(--docsearch-spacing);\n  width: 100%;\n}\n.DocSearch-Hit-source {\n  background: var(--docsearch-modal-background);\n  color: var(--docsearch-highlight-color);\n  font-size: 0.85em;\n  font-weight: 600;\n  line-height: 32px;\n  margin: 0 -4px;\n  padding: 8px 4px 0;\n  position: sticky;\n  top: 0;\n  z-index: 10;\n}\n.DocSearch-Hit-Tree {\n  color: var(--docsearch-muted-color);\n  height: var(--docsearch-hit-height);\n  opacity: 0.5;\n  stroke-width: var(--docsearch-icon-stroke-width);\n  width: 24px;\n}\n.DocSearch-Hit[aria-selected=\"true\"] a {\n  background-color: var(--docsearch-highlight-color);\n}\n.DocSearch-Hit[aria-selected=\"true\"] mark {\n  text-decoration: underline;\n}\n.DocSearch-Hit-Container {\n  align-items: center;\n  color: var(--docsearch-hit-color);\n  display: flex;\n  flex-direction: row;\n  height: var(--docsearch-hit-height);\n  padding: 0 var(--docsearch-spacing) 0 0;\n}\n.DocSearch-Hit-icon {\n  height: 20px;\n  width: 20px;\n}\n.DocSearch-Hit-action, .DocSearch-Hit-icon {\n  color: var(--docsearch-muted-color);\n  stroke-width: var(--docsearch-icon-stroke-width);\n}\n.DocSearch-Hit-action {\n  align-items: center;\n  display: flex;\n  height: 22px;\n  width: 22px;\n}\n.DocSearch-Hit-action svg {\n  display: block;\n  height: 18px;\n  width: 18px;\n}\n.DocSearch-Hit-action + .DocSearch-Hit-action {\n  margin-left: 6px;\n}\n.DocSearch-Hit-action-button {\n  appearance: none;\n  background: none;\n  border: 0;\n  border-radius: 50%;\n  color: inherit;\n  cursor: pointer;\n  padding: 2px;\n}\nsvg.DocSearch-Hit-Select-Icon {\n  display: none;\n}\n.DocSearch-Hit[aria-selected=\"true\"] .DocSearch-Hit-Select-Icon {\n  display: block;\n}\n.DocSearch-Hit-action-button:focus, .DocSearch-Hit-action-button:hover {\n  background: rgba(0, 0, 0, 0.2);\n  transition: background-color 0.1s ease-in;\n}\n@media screen and (prefers-reduced-motion: reduce) {\n  .DocSearch-Hit-action-button:focus, .DocSearch-Hit-action-button:hover {\n    transition: none;\n  }\n}\n.DocSearch-Hit-action-button:focus path,\n.DocSearch-Hit-action-button:hover path {\n  fill: #fff;\n}\n.DocSearch-Hit-content-wrapper {\n  display: flex;\n  flex: 1 1 auto;\n  flex-direction: column;\n  font-weight: 500;\n  justify-content: center;\n  line-height: 1.2em;\n  margin: 0 8px;\n  overflow-x: hidden;\n  position: relative;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  width: 80%;\n}\n.DocSearch-Hit-title {\n  font-size: 0.9em;\n}\n.DocSearch-Hit-path {\n  color: var(--docsearch-muted-color);\n  font-size: 0.75em;\n}\n.DocSearch-Hit[aria-selected=\"true\"] .DocSearch-Hit-action,\n.DocSearch-Hit[aria-selected=\"true\"] .DocSearch-Hit-icon,\n.DocSearch-Hit[aria-selected=\"true\"] .DocSearch-Hit-path,\n.DocSearch-Hit[aria-selected=\"true\"] .DocSearch-Hit-text,\n.DocSearch-Hit[aria-selected=\"true\"] .DocSearch-Hit-title,\n.DocSearch-Hit[aria-selected=\"true\"] .DocSearch-Hit-Tree,\n.DocSearch-Hit[aria-selected=\"true\"] mark {\n  color: var(--docsearch-hit-active-color) !important;\n}\n@media screen and (prefers-reduced-motion: reduce) {\n  .DocSearch-Hit-action-button:focus, .DocSearch-Hit-action-button:hover {\n    background: rgba(0, 0, 0, 0.2);\n    transition: none;\n  }\n}\n.DocSearch-ErrorScreen, .DocSearch-NoResults, .DocSearch-StartScreen {\n  font-size: 0.9em;\n  margin: 0 auto;\n  padding: 36px 0;\n  text-align: center;\n  width: 80%;\n}\n.DocSearch-Screen-Icon {\n  color: var(--docsearch-muted-color);\n  padding-bottom: 12px;\n}\n.DocSearch-NoResults-Prefill-List {\n  display: inline-block;\n  padding-bottom: 24px;\n  text-align: left;\n}\n.DocSearch-NoResults-Prefill-List ul {\n  display: inline-block;\n  padding: 8px 0 0;\n}\n.DocSearch-NoResults-Prefill-List li {\n  list-style-position: inside;\n  list-style-type: \"» \";\n}\n.DocSearch-Prefill {\n  appearance: none;\n  background: none;\n  border: 0;\n  border-radius: 1em;\n  color: var(--docsearch-highlight-color);\n  cursor: pointer;\n  display: inline-block;\n  font-size: 1em;\n  font-weight: 700;\n  padding: 0;\n}\n.DocSearch-Prefill:focus, .DocSearch-Prefill:hover {\n  outline: none;\n  text-decoration: underline;\n}\n.DocSearch-Footer {\n  align-items: center;\n  background: var(--docsearch-footer-background);\n  border-radius: 0 0 8px 8px;\n  box-shadow: var(--docsearch-footer-shadow);\n  display: flex;\n  flex-direction: row-reverse;\n  flex-shrink: 0;\n  height: var(--docsearch-footer-height);\n  justify-content: space-between;\n  padding: 0 var(--docsearch-spacing);\n  position: relative;\n  user-select: none;\n  width: 100%;\n  z-index: 300;\n}\n.DocSearch-Commands {\n  color: var(--docsearch-muted-color);\n  display: flex;\n  list-style: none;\n  margin: 0;\n  padding: 0;\n}\n.DocSearch-Commands li {\n  align-items: center;\n  display: flex;\n}\n.DocSearch-Commands li:not(:last-of-type) {\n  margin-right: 0.8em;\n}\n.DocSearch-Commands-Key {\n  align-items: center;\n  background: var(--docsearch-key-gradient);\n  border-radius: 2px;\n  box-shadow: var(--docsearch-key-shadow);\n  display: flex;\n  height: 18px;\n  justify-content: center;\n  margin-right: 0.4em;\n  padding: 0 0 1px;\n  color: var(--docsearch-muted-color);\n  border: 0;\n  width: 20px;\n}\n@media (max-width: 768px) {\n  :root {\n    --docsearch-spacing: 10px;\n    --docsearch-footer-height: 40px;\n  }\n  .DocSearch-Dropdown {\n    height: 100%;\n  }\n  .DocSearch-Container {\n    height: 100vh;\n    height: -webkit-fill-available;\n    height: calc(var(--docsearch-vh, 1vh) * 100);\n    position: absolute;\n  }\n  .DocSearch-Footer {\n    border-radius: 0;\n    bottom: 0;\n    position: absolute;\n  }\n  .DocSearch-Hit-content-wrapper {\n    display: flex;\n    position: relative;\n    width: 80%;\n  }\n  .DocSearch-Modal {\n    border-radius: 0;\n    box-shadow: none;\n    height: 100vh;\n    height: -webkit-fill-available;\n    height: calc(var(--docsearch-vh, 1vh) * 100);\n    margin: 0;\n    max-width: 100%;\n    width: 100%;\n  }\n  .DocSearch-Dropdown {\n    max-height: calc(\n      var(--docsearch-vh, 1vh) * 100 - var(--docsearch-searchbox-height)\n        - var(--docsearch-spacing)\n        - var(--docsearch-footer-height)\n    );\n  }\n  .DocSearch-Cancel {\n    appearance: none;\n    background: none;\n    border: 0;\n    color: var(--docsearch-highlight-color);\n    cursor: pointer;\n    display: inline-block;\n    flex: none;\n    font: inherit;\n    font-size: 1em;\n    font-weight: 500;\n    margin-left: var(--docsearch-spacing);\n    outline: none;\n    overflow: hidden;\n    padding: 0;\n    user-select: none;\n    white-space: nowrap;\n  }\n  .DocSearch-Commands, .DocSearch-Hit-Tree {\n    display: none;\n  }\n}\n@keyframes fade-in {\n  0% {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n/* css for docs */\n.DocSearch-Button {\n  margin: 0;\n  width: 100%;\n  border-radius: 0.5rem;\n}\n.DocSearch-Button-Placeholder {\n  display: initial;\n}\n.DocSearch-Container {\n  cursor: auto;\n}\n"
  },
  {
    "path": "www/static/google40caa9e535ae39e9.html",
    "content": "google-site-verification: google40caa9e535ae39e9.html\n"
  },
  {
    "path": "www/static/markdown.css",
    "content": ":root {\n  --color-canvas-default-transparent: rgba(255, 255, 255, 0);\n  --color-prettylights-syntax-comment: #6e7781;\n  --color-prettylights-syntax-constant: #0550ae;\n  --color-prettylights-syntax-entity: #8250df;\n  --color-prettylights-syntax-storage-modifier-import: var(\n    --color-foreground-secondary\n  );\n  --color-prettylights-syntax-entity-tag: #22863a;\n  --color-prettylights-syntax-keyword: #cf222e;\n  --color-prettylights-syntax-string: var(--color-prettylights-syntax-constant);\n  --color-prettylights-syntax-variable: #953800;\n  --color-prettylights-syntax-string-regexp: #116329;\n  --color-prettylights-syntax-markup-deleted-text: #82071e;\n  --color-prettylights-syntax-markup-deleted-bg: #ffebe9;\n  --color-prettylights-syntax-markup-inserted-text: #116329;\n  --color-prettylights-syntax-markup-inserted-bg: #dafbe1;\n  --color-prettylights-syntax-constant-other-reference-link: #0a3069;\n  --color-fg-default: var(--color-foreground-secondary);\n  --color-fg-muted: var(--color-foreground-tertiary);\n  --color-canvas-default: var(--color-background-primary);\n  --color-canvas-subtle: var(--color-background-secondary);\n  --color-border-default: hsl(\n    from\n    var(--color-foreground-secondary)\n    h\n    s\n    l\n    /\n    0.3\n  );\n  --color-border-muted: hsl(from var(--color-foreground-secondary) h s l / 0.1);\n  --color-neutral-muted: rgba(175, 184, 193, 0.2);\n  --color-accent-fg: #02aa6f;\n  --color-accent-emphasis: #0969da;\n  --color-danger-fg: #cf222e;\n\n  --warn-border: #ff9100;\n  --warn-bg: #f0900525;\n  --warn-text: var(--color-fg-default);\n}\n\nhtml[data-theme=\"dark\"] {\n  --color-accent-fg: #75eea1;\n  --color-prettylights-syntax-keyword: #f86d76;\n  --color-prettylights-syntax-entity: #b392f0;\n  --color-prettylights-syntax-constant: #8fc0fb;\n  --color-prettylights-syntax-entity-tag: #7cb78a;\n  --color-prettylights-syntax-variable: #f7955c;\n  --color-prettylights-syntax-markup-deleted-text: #ffebe9;\n  --color-prettylights-syntax-markup-deleted-bg: #82071e;\n  --color-prettylights-syntax-markup-inserted-text: #dafbe1;\n  --color-prettylights-syntax-markup-inserted-bg: #116329;\n\n  --warn-border: #ff9100;\n  --warn-bg: #f0900525;\n  --warn-text: var(--color-fg-default);\n}\n\n.markdown-body {\n  word-wrap: break-word;\n  font-family: inherit;\n  font-size: 1rem;\n  line-height: 1.75;\n}\n.markdown-body:before {\n  content: \"\";\n  display: table;\n}\n.markdown-body:after {\n  clear: both;\n  content: \"\";\n  display: table;\n}\n.markdown-body > :first-child {\n  margin-top: 0 !important;\n}\n.markdown-body > :last-child {\n  margin-bottom: 0 !important;\n}\n.markdown-body a:not([href]) {\n  color: inherit;\n  text-decoration: none;\n}\n.markdown-body .absent {\n  color: var(--color-danger-fg);\n}\n.markdown-body .anchor {\n  float: left;\n  margin-left: -20px;\n  padding-right: 4px;\n  line-height: 1;\n}\n.markdown-body .anchor:focus {\n  outline: none;\n}\n.markdown-body p {\n  margin: 1rem;\n}\n.markdown-body p,\n.markdown-body blockquote,\n.markdown-body ul,\n.markdown-body ol,\n.markdown-body dl,\n.markdown-body table,\n.markdown-body pre,\n.markdown-body details {\n  margin-bottom: 1rem;\n}\n.markdown-body ul,\n.markdown-body ol {\n  margin-top: 1rem;\n}\n.markdown-body hr {\n  height: 0.25em;\n  background-color: var(--color-border-default);\n  border: 0;\n  margin: 24px 0;\n  padding: 0;\n}\n.markdown-body blockquote {\n  color: var(--color-fg-default);\n  border-left: 0.25em solid var(--color-border-default);\n  padding: 0 1em;\n}\n.markdown-body blockquote > :first-child {\n  margin-top: 0;\n}\n.markdown-body blockquote > :last-child {\n  margin-bottom: 0;\n}\n.markdown-body h1,\n.markdown-body h2,\n.markdown-body h3,\n.markdown-body h4,\n.markdown-body h5,\n.markdown-body h6 {\n  font-weight: var(--base-text-weight-semibold, 600);\n  margin-top: 24px;\n  margin-bottom: 16px;\n  line-height: 1.25;\n}\n.markdown-body h1 .octicon-link,\n.markdown-body h2 .octicon-link,\n.markdown-body h3 .octicon-link,\n.markdown-body h4 .octicon-link,\n.markdown-body h5 .octicon-link,\n.markdown-body h6 .octicon-link {\n  color: var(--color-fg-default);\n  vertical-align: middle;\n  visibility: hidden;\n}\n.markdown-body h1:hover .anchor,\n.markdown-body h2:hover .anchor,\n.markdown-body h3:hover .anchor,\n.markdown-body h4:hover .anchor,\n.markdown-body h5:hover .anchor,\n.markdown-body h6:hover .anchor {\n  text-decoration: none;\n}\n.markdown-body h1:hover .anchor .octicon-link,\n.markdown-body h2:hover .anchor .octicon-link,\n.markdown-body h3:hover .anchor .octicon-link,\n.markdown-body h4:hover .anchor .octicon-link,\n.markdown-body h5:hover .anchor .octicon-link,\n.markdown-body h6:hover .anchor .octicon-link {\n  visibility: visible;\n}\n.markdown-body h1 tt,\n.markdown-body h1 code,\n.markdown-body h2 tt,\n.markdown-body h2 code,\n.markdown-body h3 tt,\n.markdown-body h3 code,\n.markdown-body h4 tt,\n.markdown-body h4 code,\n.markdown-body h5 tt,\n.markdown-body h5 code,\n.markdown-body h6 tt,\n.markdown-body h6 code {\n  font-size: inherit;\n  padding: 0 0.2em;\n}\n.markdown-body h2 {\n  border-top: 1px solid var(--color-border-muted);\n  padding-top: 1.25rem;\n  margin-top: 2rem;\n  font-size: 1.5em;\n}\n.markdown-body h3 {\n  font-size: 1.25em;\n}\n.markdown-body h4 {\n  font-size: 1em;\n}\n.markdown-body h5 {\n  font-size: 0.875em;\n}\n.markdown-body h6 {\n  color: var(--color-fg-muted);\n  font-size: 0.85em;\n}\n.markdown-body summary h1,\n.markdown-body summary h2,\n.markdown-body summary h3,\n.markdown-body summary h4,\n.markdown-body summary h5,\n.markdown-body summary h6 {\n  display: inline-block;\n}\n.markdown-body summary h1 .anchor,\n.markdown-body summary h2 .anchor,\n.markdown-body summary h3 .anchor,\n.markdown-body summary h4 .anchor,\n.markdown-body summary h5 .anchor,\n.markdown-body summary h6 .anchor {\n  margin-left: -40px;\n}\n.markdown-body summary h1,\n.markdown-body summary h2 {\n  border-bottom: 0;\n  padding-bottom: 0;\n}\n.markdown-body strong {\n  font-weight: 600;\n}\n.markdown-body ul,\n.markdown-body ol {\n  padding-left: 1em;\n}\n.markdown-body ul.no-list,\n.markdown-body ol.no-list {\n  padding: 0;\n  list-style-type: none;\n}\n.markdown-body ol[type=\"a\"] {\n  list-style-type: lower-alpha;\n}\n.markdown-body ol[type=\"A\"] {\n  list-style-type: upper-alpha;\n}\n.markdown-body ol[type=\"i\"] {\n  list-style-type: lower-roman;\n}\n.markdown-body ol[type=\"I\"] {\n  list-style-type: upper-roman;\n}\n.markdown-body ol[type=\"1\"] {\n  list-style-type: decimal;\n}\n.markdown-body div > ol:not([type]) {\n  list-style-type: decimal;\n}\n.markdown-body ul ul,\n.markdown-body ul ol,\n.markdown-body ol ol,\n.markdown-body ol ul {\n  margin-top: 0;\n  margin-bottom: 0;\n}\n.markdown-body li > p {\n  margin-top: 16px;\n}\n.markdown-body li + li {\n  margin-top: 0.25em;\n}\n.markdown-body dl {\n  padding: 0;\n}\n.markdown-body dl dt {\n  font-size: 1em;\n  font-style: italic;\n  font-weight: var(--base-text-weight-semibold, 600);\n  margin-top: 16px;\n  padding: 0;\n}\n.markdown-body dl dd {\n  margin-bottom: 16px;\n  padding: 0 16px;\n}\n.markdown-body table {\n  width: 100%;\n  width: -webkit-max-content;\n  width: -webkit-max-content;\n  width: max-content;\n  max-width: 100%;\n  display: block;\n  overflow: auto;\n}\n.markdown-body table th {\n  font-weight: var(--base-text-weight-semibold, 600);\n}\n.markdown-body table th,\n.markdown-body table td {\n  border: 1px solid var(--color-border-default);\n  padding: 6px 13px;\n}\n.markdown-body table td > :last-child {\n  margin-bottom: 0;\n}\n.markdown-body table tr {\n  background-color: var(--color-canvas-default);\n  border-top: 1px solid var(--color-border-muted);\n}\n.markdown-body table tr:nth-child(2n) {\n  background-color: var(--color-canvas-subtle);\n}\n.markdown-body table img {\n  background-color: transparent;\n}\n.markdown-body img {\n  max-width: 100%;\n  box-sizing: content-box;\n  background-color: var(--color-canvas-default);\n}\n.markdown-body img[align=\"right\"] {\n  padding-left: 20px;\n}\n.markdown-body img[align=\"left\"] {\n  padding-right: 20px;\n}\n.markdown-body .emoji {\n  max-width: none;\n  vertical-align: text-top;\n  background-color: transparent;\n}\n.markdown-body span.frame {\n  display: block;\n  overflow: hidden;\n}\n.markdown-body span.frame > span {\n  float: left;\n  width: auto;\n  border: 1px solid var(--color-border-default);\n  margin: 13px 0 0;\n  padding: 7px;\n  display: block;\n  overflow: hidden;\n}\n.markdown-body span.frame span img {\n  float: left;\n  display: block;\n}\n.markdown-body span.frame span span {\n  clear: both;\n  color: var(--color-fg-default);\n  padding: 5px 0 0;\n  display: block;\n}\n.markdown-body span.align-center {\n  clear: both;\n  display: block;\n  overflow: hidden;\n}\n.markdown-body span.align-center > span {\n  text-align: center;\n  margin: 13px auto 0;\n  display: block;\n  overflow: hidden;\n}\n.markdown-body span.align-center span img {\n  text-align: center;\n  margin: 0 auto;\n}\n.markdown-body span.align-right {\n  clear: both;\n  display: block;\n  overflow: hidden;\n}\n.markdown-body span.align-right > span {\n  text-align: right;\n  margin: 13px 0 0;\n  display: block;\n  overflow: hidden;\n}\n.markdown-body span.align-right span img {\n  text-align: right;\n  margin: 0;\n}\n.markdown-body span.float-left {\n  float: left;\n  margin-right: 13px;\n  display: block;\n  overflow: hidden;\n}\n.markdown-body span.float-left span {\n  margin: 13px 0 0;\n}\n.markdown-body span.float-right {\n  float: right;\n  margin-left: 13px;\n  display: block;\n  overflow: hidden;\n}\n.markdown-body span.float-right > span {\n  text-align: right;\n  margin: 13px auto 0;\n  display: block;\n  overflow: hidden;\n}\n.markdown-body code,\n.markdown-body tt {\n  white-space: break-spaces;\n  background-color: var(--color-neutral-muted);\n  border-radius: 6px;\n  margin: 0;\n  padding: 0.1875rem 0.375rem;\n  font-size: 0.875rem;\n}\n.markdown-body code br,\n.markdown-body tt br {\n  display: none;\n}\n.markdown-body del code {\n  -webkit-text-decoration: inherit;\n  -webkit-text-decoration: inherit;\n  text-decoration: inherit;\n}\n.markdown-body samp {\n  font-size: 85%;\n}\n.markdown-body pre {\n  word-wrap: normal;\n}\n.markdown-body pre code {\n  font-size: 100%;\n}\n.markdown-body pre > code {\n  word-break: normal;\n  white-space: pre;\n  background: 0 0;\n  border: 0;\n  margin: 0;\n  padding: 0;\n}\n.markdown-body .highlight {\n  margin-bottom: 16px;\n}\n.markdown-body .highlight pre {\n  word-break: normal;\n  margin-bottom: 0;\n}\n.markdown-body .highlight pre,\n.markdown-body pre {\n  background-color: var(--color-canvas-subtle);\n  border-radius: 6px;\n  padding: 1.25rem 1.5rem;\n  font-size: 85%;\n  line-height: 1.75;\n  overflow: auto;\n}\n.markdown-body pre code,\n.markdown-body pre tt {\n  max-width: auto;\n  line-height: inherit;\n  word-wrap: normal;\n  background-color: transparent;\n  border: 0;\n  margin: 0;\n  padding: 0;\n  display: inline;\n  overflow: visible;\n}\n.markdown-body .csv-data td,\n.markdown-body .csv-data th {\n  text-align: left;\n  white-space: nowrap;\n  padding: 5px;\n  font-size: 0.75rem;\n  line-height: 1;\n  overflow: hidden;\n}\n.markdown-body .csv-data .blob-num {\n  text-align: right;\n  background: var(--color-canvas-default);\n  border: 0;\n  padding: 10px 8px 9px;\n}\n.markdown-body .csv-data tr {\n  border-top: 0;\n}\n.markdown-body .csv-data th {\n  font-weight: var(--base-text-weight-semibold, 600);\n  background: var(--color-canvas-subtle);\n  border-top: 0;\n}\n.markdown-body [data-footnote-ref]:before {\n  content: \"[\";\n}\n.markdown-body [data-footnote-ref]:after {\n  content: \"]\";\n}\n.markdown-body .footnotes {\n  color: var(--color-fg-muted);\n  border-top: 1px solid var(--color-border-default);\n  font-size: 0.75rem;\n}\n.markdown-body .footnotes ol {\n  padding-left: 16px;\n}\n.markdown-body .footnotes ol ul {\n  margin-top: 16px;\n  padding-left: 16px;\n  display: inline-block;\n}\n.markdown-body .footnotes li {\n  position: relative;\n}\n.markdown-body .footnotes li:target:before {\n  pointer-events: none;\n  content: \"\";\n  border: 2px solid var(--color-accent-emphasis);\n  border-radius: 6px;\n  position: absolute;\n  top: -8px;\n  bottom: -8px;\n  left: -24px;\n  right: -8px;\n}\n.markdown-body .footnotes li:target {\n  color: var(--color-fg-default);\n}\n.markdown-body .footnotes .data-footnote-backref g-emoji {\n  font-family: monospace;\n}\n.markdown-body {\n  background-color: var(--color-canvas-default);\n  color: var(--color-fg-default);\n}\n.markdown-body a {\n  color: var(--color-accent-fg);\n  text-decoration: underline;\n  text-underline-offset: 2px;\n}\n.markdown-body a:hover {\n  text-decoration: underline;\n}\n.markdown-body img[align=\"center\"] {\n  margin: 0 auto;\n}\n.markdown-body iframe {\n  background-color: #fff;\n  border: 0;\n  margin-bottom: 16px;\n}\n.markdown-body svg.octicon {\n  fill: currentColor;\n}\n.markdown-body .anchor > .octicon {\n  display: inline;\n}\n.markdown-body figcaption {\n  text-align: center;\n  padding-top: 2px;\n}\n.markdown-body .highlight .token.keyword,\n.gfm-highlight .token.keyword {\n  color: var(--color-prettylights-syntax-keyword);\n}\n.highlight .atrule .rule {\n  color: var(--color-prettylights-syntax-keyword);\n}\n.markdown-body .highlight .token.tag .token.class-name,\n.markdown-body .highlight .token.tag .token.script .token.punctuation,\n.gfm-highlight .token.tag .token.class-name,\n.gfm-highlight .token.tag .token.script .token.punctuation {\n  color: var(--color-prettylights-syntax-storage-modifier-import);\n}\n.markdown-body .highlight .token.operator,\n.markdown-body .highlight .token.number,\n.markdown-body .highlight .token.boolean,\n.markdown-body .highlight .token.tag .token.punctuation,\n.markdown-body .highlight .token.tag .token.script .token.script-punctuation,\n.markdown-body .highlight .token.tag .token.attr-name,\n.gfm-highlight .token.operator,\n.gfm-highlight .token.number,\n.gfm-highlight .token.boolean,\n.gfm-highlight .token.tag .token.punctuation,\n.gfm-highlight .token.tag .token.script .token.script-punctuation,\n.gfm-highlight .token.tag .token.attr-name {\n  color: var(--color-prettylights-syntax-constant);\n}\n.markdown-body .highlight .token.function,\n.gfm-highlight .token.function {\n  color: var(--color-prettylights-syntax-entity);\n}\n.markdown-body .highlight .token.string,\n.gfm-highlight .token.string {\n  color: var(--color-prettylights-syntax-string);\n}\n.markdown-body .highlight .token.comment,\n.gfm-highlight .token.comment {\n  color: var(--color-prettylights-syntax-comment);\n}\n.markdown-body .highlight .token.class-name,\n.gfm-highlight .token.class-name {\n  color: var(--color-prettylights-syntax-variable);\n}\n.markdown-body .highlight .token.regex,\n.gfm-highlight .token.regex {\n  color: var(--color-prettylights-syntax-string);\n}\n.markdown-body .highlight .token.regex .regex-delimiter,\n.gfm-highlight .token.regex .regex-delimiter {\n  color: var(--color-prettylights-syntax-constant);\n}\n.markdown-body .highlight .token.tag .token.tag,\n.markdown-body .highlight .token.property,\n.gfm-highlight .token.tag .token.tag,\n.gfm-highlight .token.property {\n  color: var(--color-prettylights-syntax-entity-tag);\n}\n.markdown-body .highlight .token.deleted,\n.gfm-highlight .token.deleted {\n  color: var(--color-prettylights-syntax-markup-deleted-text);\n  background-color: var(--color-prettylights-syntax-markup-deleted-bg);\n}\n.markdown-body .highlight .token.inserted,\n.gfm-highlight .token.inserted {\n  color: var(--color-prettylights-syntax-markup-inserted-text);\n  background-color: var(--color-prettylights-syntax-markup-inserted-bg);\n}\n\n.markdown-body h1,\n.markdown-body h2,\n.markdown-body h3,\n.markdown-body h4,\n.markdown-body h5,\n.markdown-body h6,\n.markdown-body ul,\n.markdown-body ol,\n.markdown-body p,\n.markdown-body details,\n.markdown-body blockquote {\n  margin-left: 1rem;\n  margin-right: 1rem;\n}\n\n.markdown-body blockquote p {\n  margin-left: 0;\n}\n\n.markdown-body .admonition {\n  padding: 1rem;\n  margin: 1.5rem;\n}\n.markdown-body .admonition-header {\n  font-weight: bold;\n  display: flex;\n  align-items: center;\n  font-size: 1rem;\n}\n.markdown-body .admonition .icon {\n  display: inline-flex;\n  margin-right: 0.25rem;\n  width: 1.2em;\n  height: 1.2em;\n  margin-bottom: 0.25rem;\n}\n.markdown-body blockquote.warn {\n  border-color: var(--warn-border);\n  background: var(--warn-bg);\n  color: var(--warn-text);\n}\n\n.markdown-body .admonition .fenced-code {\n  margin-left: 0;\n  margin-right: 0;\n  border: 1px solid #d9d2d2;\n  border-radius: 0.5rem 0.5rem 0.25rem 0.25rem;\n}\n\n.markdown-body .admonition .highlight {\n  margin-bottom: 0;\n}\n\n.markdown-body .admonition.tip .admonition-header {\n  color: rgb(0, 148, 0);\n}\nhtml[data-theme=\"dark\"] .markdown-body .admonition.tip {\n  background-color: #1f331f;\n  border-color: var(--color-accent-fg);\n}\nhtml[data-theme=\"dark\"] .markdown-body .admonition.tip .admonition-header {\n  color: var(--color-accent-fg);\n}\n.markdown-body .admonition.tip {\n  background-color: rgb(230, 246, 230);\n  border-color: rgb(0, 148, 0);\n}\n.markdown-body .admonition.info .admonition-header {\n  color: rgb(25 146 184);\n}\n.markdown-body .admonition.info {\n  background-color: rgb(238, 249, 253);\n  border-color: rgb(25 146 184);\n}\n\nhtml[data-theme=\"dark\"] .markdown-body .admonition.info {\n  background-color: hsl(194 76% 41% / 0.1);\n  color: var(--color-foreground-primary);\n}\n.markdown-body .admonition.warn .admonition-header {\n  color: #dd6f04;\n}\n.markdown-body .admonition.warn {\n  color: var(--warn-text);\n  background-color: var(--warn-bg);\n  border-color: var(--warn-border);\n}\n\n@media screen and (min-width: 768px) {\n  .markdown-body table {\n    margin-left: 1rem;\n    margin-right: 1rem;\n  }\n\n  .markdown-body details .fenced-code {\n    margin-left: 0;\n    margin-right: 0;\n  }\n\n  .markdown-body .admonition {\n    margin-left: 0;\n    margin-right: 0;\n  }\n\n  .markdown-body .fenced-code,\n  .markdown-body ol,\n  .markdown-body ul,\n  .markdown-body h1,\n  .markdown-body h2,\n  .markdown-body h4,\n  .markdown-body h5,\n  .markdown-body h6,\n  .markdown-body p {\n    margin-left: 0;\n    margin-right: 0;\n  }\n  .markdown-body h3 {\n    margin: 2rem 0 1rem 0;\n  }\n}\n\nol.nested {\n  counter-reset: item;\n}\n\nol.nested li {\n  display: block;\n}\n\nol.nested li:before {\n  font-feature-settings: \"kern\" 1, \"tnum\" 1;\n  -webkit-font-feature-settings: \"kern\" 1, \"tnum\" 1;\n  -ms-font-feature-settings: \"kern\" 1, \"tnum\" 1;\n  -moz-font-feature-settings: \"kern\" 1, \"tnum\" 1;\n  content: counters(item, \".\") \". \";\n  counter-increment: item;\n  counter-increment: item;\n}\n\n.markdown-body ul {\n  list-style: disc;\n}\n\n.markdown-body ol {\n  list-style: numeric;\n}\n\n.markdown-body .md-anchor {\n  color: inherit;\n  text-decoration: none !important;\n}\n.markdown-body .md-anchor span {\n  color: var(--color-accent-fg);\n  display: inline-block;\n  margin-left: 0.25rem;\n  opacity: 0;\n  visibility: hidden;\n}\n.markdown-body .md-anchor:hover span {\n  opacity: 1;\n  visibility: visible;\n}\n\n.markdown-body .highlight {\n  border-radius: 0.5rem;\n}\n\n.toggle:checked + .toggled {\n  display: block;\n}\n\n.fenced-code {\n  margin-bottom: 1rem;\n}\n.fenced-code pre {\n  margin-bottom: 0;\n}\n.fenced-code-header {\n  border-top-left-radius: 0.5rem;\n  border-top-right-radius: 0.5rem;\n  padding-left: 0.5rem;\n  padding-right: 0.5rem;\n  background: var(--color-background-tertiary);\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n.fenced-code-header + pre {\n  border-top-left-radius: 0 !important;\n  border-top-right-radius: 0 !important;\n}\n.fenced-code-title {\n  display: flex;\n  padding-top: 0.5rem;\n  padding-bottom: 0.5rem;\n  font-family:\n    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n    \"Courier New\", monospace;\n  font-size: 0.8125rem;\n  line-height: 1;\n  justify-content: space-between;\n  align-items: center;\n  gap: 0.625rem;\n}\n.fenced-code-title > img {\n  background-color: transparent;\n  margin: -0.2rem 0;\n  width: 1.25rem;\n}\n\n.highlight-source-yml .atrule {\n  color: var(--color-prettylights-syntax-entity);\n}\n.highlight-source-yml .string {\n  color: var(--color-prettylights-syntax-string);\n}\n\n.markdown-body pre.highlight-source-txt-files {\n  line-height: 1.35;\n}\n.highlight-source-txt-files .function {\n  font-weight: 600;\n}\n"
  },
  {
    "path": "www/static/prism.css",
    "content": "/* PrismJS 1.29.0\nhttps://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clike+javascript */\ncode[class*=\"language-\"], pre[class*=\"language-\"] {\n  color: #f8f8f2;\n  background: 0 0;\n  text-shadow: 0 1px rgba(0, 0, 0, 0.3);\n  font-family: Consolas, Monaco, \"Andale Mono\", \"Ubuntu Mono\", monospace;\n  font-size: 1em;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  -moz-tab-size: 4;\n  -o-tab-size: 4;\n  tab-size: 4;\n  -webkit-hyphens: none;\n  -moz-hyphens: none;\n  -ms-hyphens: none;\n  hyphens: none;\n}\npre[class*=\"language-\"] {\n  padding: 1em;\n  margin: 0.5em 0;\n  overflow: auto;\n  border-radius: 0.3em;\n}\n:not(pre) > code[class*=\"language-\"], pre[class*=\"language-\"] {\n  background: #272822;\n}\n:not(pre) > code[class*=\"language-\"] {\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n}\n.token.cdata, .token.comment, .token.doctype, .token.prolog {\n  color: #8292a2;\n}\n.token.punctuation {\n  color: #f8f8f2;\n}\n.token.namespace {\n  opacity: 0.7;\n}\n.token.constant, .token.deleted, .token.property, .token.symbol, .token.tag {\n  color: #f92672;\n}\n.token.boolean, .token.number {\n  color: #ae81ff;\n}\n.token.attr-name,\n.token.builtin,\n.token.char,\n.token.inserted,\n.token.selector,\n.token.string {\n  color: #a6e22e;\n}\n.language-css .token.string,\n.style .token.string,\n.token.entity,\n.token.operator,\n.token.url,\n.token.variable {\n  color: #f8f8f2;\n}\n.token.atrule, .token.attr-value, .token.class-name, .token.function {\n  color: #e6db74;\n}\n.token.keyword {\n  color: #66d9ef;\n}\n.token.important, .token.regex {\n  color: #fd971f;\n}\n.token.bold, .token.important {\n  font-weight: 700;\n}\n.token.italic {\n  font-style: italic;\n}\n.token.entity {\n  cursor: help;\n}\n"
  },
  {
    "path": "www/utils/markdown.ts",
    "content": "export { extractYaml as frontMatter } from \"@std/front-matter\";\n\nimport * as Marked from \"marked\";\nimport { HttpError } from \"fresh\";\nimport { asset } from \"fresh/runtime\";\nimport { escape as escapeHtml } from \"@std/html\";\nimport { mangle } from \"marked-mangle\";\nimport GitHubSlugger from \"github-slugger\";\nimport { Prism } from \"./prism.ts\";\n\nMarked.marked.use(mangle());\n\nconst ADMISSION_REG = /^\\[(info|warn|tip)\\]:\\s/;\n\nconst LOGOS = [\n  {\n    lang: /^tsx?$/,\n    file: /\\.tsx?$/,\n    src: asset(\"/logos/typescript.svg\"),\n    text: \"Typescript\",\n  },\n  {\n    lang: /^css$/,\n    file: /\\.css$/,\n    src: asset(\"/logos/css.svg\"),\n    text: \"CSS\",\n  },\n  {\n    lang: /^html$/,\n    file: /\\.html$/,\n    src: asset(\"/logos/html.svg\"),\n    text: \"HTML\",\n  },\n  {\n    lang: /^jsonc?$/,\n    file: /\\.jsonc?$/,\n    src: asset(\"/logos/json.svg\"),\n    text: \"JSON\",\n  },\n  {\n    lang: /^(sh|bash)$/,\n    file: /\\.sh$/,\n    src: asset(\"/logos/shell.svg\"),\n    text: \"Terminal (Shell/Bash)\",\n  },\n  {\n    lang: /^md$/,\n    file: /\\.md$/,\n    src: asset(\"/logos/markdown.svg\"),\n    text: \"Markdown\",\n  },\n  {\n    lang: /^txt(-files)?$/,\n    file: /\\.txt$/,\n    src: asset(\"/logos/text.svg\"),\n    text: \"Text\",\n  },\n  {\n    lang: /^diff$/,\n    file: /\\.diff$/,\n    src: asset(\"/logos/diff.svg\"),\n    text: \"File diff\",\n  },\n  {\n    lang: /^gitignore$/,\n    file: /^\\.gitignore$/,\n    src: asset(\"/logos/git.svg\"),\n    text: \"Git\",\n  },\n  {\n    lang: /^dockerfile$/,\n    file: /^Dockerfile$/,\n    src: asset(\"/logos/docker.svg\"),\n    text: \"Docker\",\n  },\n];\n\nexport interface MarkdownHeading {\n  id: string;\n  html: string;\n  level: number;\n}\n\nclass DefaultRenderer extends Marked.Renderer {\n  headings: MarkdownHeading[] = [];\n  slugger = new GitHubSlugger();\n\n  override text(\n    token: Marked.Tokens.Text | Marked.Tokens.Escape | Marked.Tokens.Tag,\n  ): string {\n    if (\n      token.type === \"text\" && \"tokens\" in token && token.tokens !== undefined\n    ) {\n      return this.parser.parseInline(token.tokens);\n    }\n\n    // Smartypants typography enhancement\n    return token.text\n      .replaceAll(\"...\", \"&#8230;\")\n      .replaceAll(\"--\", \"&#8212;\")\n      .replaceAll(\"---\", \"&#8211;\")\n      .replaceAll(/(\\w)'(\\w)/g, \"$1&#8217;$2\")\n      .replaceAll(/s'/g, \"s&#8217;\")\n      .replaceAll(\"&#39;\", \"&#8217;\")\n      .replaceAll(/[\"](.*?)[\"]/g, \"&#8220;$1&#8221\")\n      .replaceAll(/&quot;(.*?)&quot;/g, \"&#8220;$1&#8221\")\n      .replaceAll(/['](.*?)[']/g, \"&#8216;$1&#8217;\");\n  }\n\n  override heading({ tokens, depth }: Marked.Tokens.Heading): string {\n    this.#assert(tokens.length > 0, \"Markdown heading tokens unexpected value\");\n\n    let content = \"\";\n    for (let i = 0; i < tokens.length; i++) {\n      const token = tokens[i];\n      switch (token.type) {\n        case \"text\":\n          content += token.text;\n          continue;\n        case \"codespan\":\n          content += token.text.replaceAll(/[<>]/g, \"\");\n          continue;\n        default:\n          this.#assert(\n            false,\n            \"Markdown heading tokens unexpected value\",\n          );\n      }\n    }\n\n    let slugInput = content;\n\n    // Rewrites e.g. `.get()` to `get`\n    if (/^\\..*\\(\\)$/.test(slugInput)) {\n      slugInput = slugInput.slice(1, -2);\n    }\n\n    const slug = this.slugger.slug(slugInput);\n    const text = this.parser.parseInline(tokens);\n    this.headings.push({ id: slug, html: text, level: depth });\n    return `<h${depth} id=\"${slug}\"><a class=\"md-anchor\" tabindex=\"-1\" href=\"#${slug}\">${text}<span aria-hidden=\"true\">#</span></a></h${depth}>`;\n  }\n\n  override link({ href, title, tokens }: Marked.Tokens.Link) {\n    const text = this.parser.parseInline(tokens);\n    const titleAttr = title ? ` title=\"${title}\"` : \"\";\n    if (href.startsWith(\"#\")) {\n      return `<a href=\"${href}\"${titleAttr}>${text}</a>`;\n    }\n\n    return `<a href=\"${href}\"${titleAttr} rel=\"noopener noreferrer\">${text}</a>`;\n  }\n\n  override image({ href, text, title }: Marked.Tokens.Image) {\n    return `<img src=\"${href}\" alt=\"${text ?? \"\"}\" title=\"${title ?? \"\"}\" />`;\n  }\n\n  override code({ lang: info, text }: Marked.Tokens.Code): string {\n    // format: tsx\n    // format: tsx my/file.ts\n    // format: tsx \"This is my title\"\n    let lang = \"\";\n    let title = \"\";\n    const match = info?.match(/^([\\w_-]+)\\s*(.*)?$/);\n    if (match) {\n      lang = match[1].toLocaleLowerCase();\n      title = match[2] ?? \"\";\n    }\n\n    // Find icon by filename first, then by markdown block language.\n    const icon = LOGOS.find((l) => l.file.test(title)) ??\n      LOGOS.find((l) => l.lang.test(lang));\n\n    let out = `<div class=\"fenced-code\">`;\n\n    if (title || icon) {\n      const image = icon\n        ? `<img src=\"${icon.src}\" alt=\"${icon.text}\" title=\"${icon.text}\" width=\"20\" height=\"20\">`\n        : \"\";\n\n      out += `<div class=\"fenced-code-header\">\n        <span class=\"fenced-code-title lang-${lang} w-full\">\n          <span class=\"flex items-center gap-2\">\n            ${image}\n            ${title ? escapeHtml(String(title)) : \"&nbsp;\"}\n          </span>\n        </span>\n        ${\n        icon && icon.text !== \"Text\"\n          ? `<button\n            type=\"button\"\n            data-code=\"${escapeHtml(text)}\"\n            aria-label=\"Copy to Clipboard\"\n            class=\"rounded-sm flex items-center justify-center border border-foreground-secondary/30 hover:bg-foreground-secondary/20 dark:hover:bg-foreground-secondary/70 data-copied:text-green-700 dark:data-copied:text-green-300 relative group cursor-pointer w-7 h-7 dark:text-white\"\n          >\n            <span class=\"group-copied\">\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                class=\"h-4 w-4\"\n                fill=\"none\"\n                viewBox=\"0 0 24 24\"\n                stroke=\"currentColor\"\n                aria-hidden=\"true\"\n              >\n                <path\n                  stroke-width={3}\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                  d=\"M5 13l4 4L19 7\"\n                />\n              </svg>\n            </span>\n            <span class=\"group-not-copied\">\n              <svg\n                class=\"h-4 w-4\"\n                viewBox=\"0 0 15 15\"\n                fill=\"none\"\n                xmlns=\"http://www.w3.org/2000/svg\"\n                aria-hidden=\"true\"\n              >\n                <path\n                  d=\"M1.55566 2.7C1.55566 2.03726 2.09292 1.5 2.75566 1.5H8.75566C9.41841 1.5 9.95566 2.03726 9.95566 2.7V5.1H12.3557C13.0184 5.1 13.5557 5.63726 13.5557 6.3V12.3C13.5557 12.9627 13.0184 13.5 12.3557 13.5H6.35566C5.69292 13.5 5.15566 12.9627 5.15566 12.3V9.9H2.75566C2.09292 9.9 1.55566 9.36274 1.55566 8.7V2.7ZM6.35566 9.9V12.3H12.3557V6.3H9.95566V8.7C9.95566 9.36274 9.41841 9.9 8.75566 9.9H6.35566ZM8.75566 8.7V2.7L2.75566 2.7V8.7H8.75566Z\"\n                  fill=\"currentColor\"\n                />\n              </svg>\n            </span>\n          </button>`\n          : \"\"\n      }\n      </div>`;\n    }\n\n    const grammar = lang && Object.hasOwnProperty.call(Prism.languages, lang)\n      ? Prism.languages[lang]\n      : undefined;\n\n    if (grammar === undefined) {\n      out += `<pre><code class=\"notranslate\">${escapeHtml(text)}</code></pre>`;\n    } else {\n      const html = Prism.highlight(text, grammar, lang);\n      out +=\n        `<pre class=\"highlight highlight-source-${lang} notranslate lang-${lang}\"><code>${html}</code></pre>`;\n    }\n\n    out += `</div>`;\n    return out;\n  }\n\n  override blockquote({ text, tokens }: Marked.Tokens.Blockquote): string {\n    const match = text.match(ADMISSION_REG);\n\n    if (match) {\n      const label: Record<string, string> = {\n        tip: \"Tip\",\n        warn: \"Warning\",\n        info: \"Info\",\n      };\n      Marked.walkTokens(tokens, (token) => {\n        if (token.type === \"text\" && token.text.startsWith(match[0])) {\n          token.text = token.text.slice(match[0].length);\n        }\n      });\n      const type = match[1];\n      const icon = `<svg class=\"icon\"><use href=\"/icons.svg#${type}\" /></svg>`;\n      return `<blockquote class=\"admonition ${type}\">\\n<span class=\"admonition-header\">${icon}${\n        label[type]\n      }</span>${this.parser.parse(tokens)}</blockquote>\\n`;\n    }\n    return `<blockquote>\\n${this.parser.parse(tokens)}</blockquote>\\n`;\n  }\n\n  #assert(expr: unknown, msg: string): asserts expr {\n    if (!expr) throw new Error(msg);\n  }\n}\n\nexport interface MarkdownOptions {\n  inline?: boolean;\n}\nexport function renderMarkdown(\n  input: string,\n  opts: MarkdownOptions = {},\n): { headings: MarkdownHeading[]; html: string } {\n  const renderer = new DefaultRenderer();\n  const markedOpts: Marked.MarkedOptions & { async: false } = {\n    gfm: true,\n    async: false,\n    renderer,\n  };\n\n  try {\n    const html = opts.inline\n      ? Marked.parseInline(input, markedOpts)\n      : Marked.parse(input, markedOpts);\n\n    return { headings: renderer.headings, html };\n  } catch (err) {\n    throw new HttpError(500, \"Markdown parsing error\", {\n      cause: err,\n    });\n  }\n}\n"
  },
  {
    "path": "www/utils/prism.ts",
    "content": "import Prism from \"prismjs\";\nimport \"prismjs/components/prism-jsx.js\";\nimport \"prismjs/components/prism-typescript.js\";\nimport \"prismjs/components/prism-tsx.js\";\nimport \"prismjs/components/prism-diff.js\";\nimport \"prismjs/components/prism-json.js\";\nimport \"prismjs/components/prism-bash.js\";\nimport \"prismjs/components/prism-yaml.js\";\nimport \"prismjs/components/prism-ignore.js\";\n\n// deno-lint-ignore no-explicit-any\nconst languages = Prism.languages as any;\n\n/** Extends `sh` with `deno` as a function token in Shell/Bash languages */\nlanguages.sh.deno = {\n  pattern: /(^|[\\s;|&]|[<>]\\()(?:deno)(?=$|[)\\s;|&])/,\n  lookbehind: true,\n  alias: \"function\",\n};\nlanguages.bash.deno = languages.sh.deno;\n\n/**\n * Adds `txt-files` language for file-structure code blocks.\n *\n * - Comments: Makes `#` and everything after a comment\n * - Operator: Matches the file structure symbols like `├──`, `└──`, and `│ `\n * - Root: Matches `<root>` or `<project root>` to indicate the root of the file structure\n * - Remaining text is rendered as plain text\n *\n * @example\n * ```txt-files\n * <project root>\n * ├── routes/             # File system based routes\n * │   ├── _app.tsx        # Renders the outer <html> content structure\n * │   └── index.tsx       # Renders /\n * ├── dev.ts     # Run this during development\n * └── main.ts    # Run this for production\n * ```\n */\nPrism.languages[\"txt-files\"] = {\n  comment: {\n    pattern: /#.*|$/,\n    greedy: true,\n  },\n  operator: /├──|└──|│\\s+ /,\n  root: {\n    pattern: /<root>|<project root>/,\n    alias: \"function\",\n  },\n};\n\nexport { Prism };\n"
  },
  {
    "path": "www/utils/screenshot.ts",
    "content": "import { launch } from \"@astral/astral\";\nimport { Image } from \"imagescript\";\n\nexport function validateArgs(args: string[]): [string, string] {\n  if (args.length !== 2) {\n    throw new Error(\"Usage: screenshot <url> <id>\");\n  }\n  return [args[0], args[1]];\n}\n\nexport function validateUrl(url: string): URL {\n  const parsedUrl = new URL(url);\n  if (parsedUrl.protocol !== \"http:\" && parsedUrl.protocol !== \"https:\") {\n    throw new Error(\"Invalid URL\");\n  }\n  return parsedUrl;\n}\n\nexport function generateFilePaths(\n  id: string,\n): { image2x: string; image1x: string } {\n  return {\n    image2x: `./www/static/showcase/${id}2x.jpg`,\n    image1x: `./www/static/showcase/${id}1x.jpg`,\n  };\n}\n\nexport async function captureScreenshot(\n  url: string,\n  id: string,\n): Promise<void> {\n  const browser = await launch();\n  try {\n    const page = await browser.newPage(url);\n    await page.waitForNetworkIdle();\n    const raw = await page.screenshot();\n\n    const image2x = await Image.decode(raw);\n    const { image2x: path2x, image1x: path1x } = generateFilePaths(id);\n\n    await Deno.writeFile(path2x, await image2x.encodeJPEG(80));\n    const image1x = image2x.resize(image2x.width / 2, Image.RESIZE_AUTO);\n    await Deno.writeFile(path1x, await image1x.encodeJPEG(80));\n  } finally {\n    await browser.close();\n  }\n}\n\n// only run the script if it's the main module\nif (import.meta.main) {\n  const [url, id] = validateArgs(Deno.args);\n  validateUrl(url);\n  await captureScreenshot(url, id);\n  // deno-lint-ignore no-console\n  console.log(`Screenshot saved as ${id}1x.jpg and ${id}2x.jpg`);\n}\n"
  },
  {
    "path": "www/utils/screenshot_test.ts",
    "content": "import { expect } from \"@std/expect/expect\";\nimport { generateFilePaths, validateArgs, validateUrl } from \"./screenshot.ts\";\n\nDeno.test(\"validateArgs - accepts 2 arguments\", () => {\n  const result = validateArgs([\"https://example.com\", \"test-id\"]);\n  expect(result).toEqual([\"https://example.com\", \"test-id\"]);\n});\n\nDeno.test(\"validateArgs - should throw error for incorrect number of arguments\", () => {\n  expect(() => validateArgs([\"only-one\"])).toThrow(\n    \"Usage: screenshot <url> <id>\",\n  );\n  expect(() => validateArgs([\"one\", \"two\", \"three\"])).toThrow(\n    \"Usage: screenshot <url> <id>\",\n  );\n});\n\nDeno.test(\"validateUrl - should accept valid HTTP URLs\", () => {\n  const url = validateUrl(\"http://example.com\");\n  expect(url).toEqual(new URL(\"http://example.com\"));\n});\n\nDeno.test(\"validateUrl - should accept valid HTTPS URLs\", () => {\n  const url = validateUrl(\"https://example.com\");\n  expect(url).toEqual(new URL(\"https://example.com\"));\n});\n\nDeno.test(\"validateUrl - should reject invalid protocols\", () => {\n  expect(() => validateUrl(\"ftp://example.com\")).toThrow(\"Invalid URL\");\n  expect(() => validateUrl(\"file:///path/to/file\")).toThrow(\"Invalid URL\");\n});\n\nDeno.test(\"generateFilePaths - should generate correct file paths\", () => {\n  const paths = generateFilePaths(\"test-id\");\n  expect(paths).toEqual({\n    image2x: \"./www/static/showcase/test-id2x.jpg\",\n    image1x: \"./www/static/showcase/test-id1x.jpg\",\n  });\n});\n\nDeno.test(\"generateFilePaths - should handle special characters in ID\", () => {\n  const paths = generateFilePaths(\"test-id_123\");\n  expect(paths).toEqual({\n    image2x: \"./www/static/showcase/test-id_1232x.jpg\",\n    image1x: \"./www/static/showcase/test-id_1231x.jpg\",\n  });\n});\n"
  },
  {
    "path": "www/utils/state.ts",
    "content": "import { createDefine } from \"fresh\";\n\nexport interface State {\n  title?: string;\n  description?: string;\n  ogImage?: string;\n  noIndex?: boolean;\n}\n\nexport const define = createDefine<State>();\n"
  },
  {
    "path": "www/vite.config.ts",
    "content": "import { defineConfig, type Plugin } from \"vite\";\nimport { fresh } from \"@fresh/plugin-vite\";\nimport tailwindcss from \"@tailwindcss/vite\";\nimport inspect from \"vite-plugin-inspect\";\nimport { copy } from \"@std/fs\";\nimport * as path from \"@std/path\";\nimport { pathWithRoot } from \"../packages/plugin-vite/src/utils.ts\";\n\nexport default defineConfig({\n  plugins: [\n    fresh(),\n    tailwindcss(),\n    inspect(),\n    copyDocs(),\n  ],\n});\n\nfunction copyDocs(): Plugin {\n  let serverOutDir = \"\";\n\n  return {\n    name: \"fresh:copy-docs\",\n    configResolved(config) {\n      serverOutDir = pathWithRoot(\n        config.environments.ssr.build.outDir,\n        config.root,\n      );\n    },\n    async writeBundle() {\n      const branches = [\"canary\", \"latest\", \"1.x\"];\n\n      for (const branch of branches) {\n        const source = path.join(import.meta.dirname!, \"..\", \"docs\", branch);\n        const target = path.join(serverOutDir, \"docs\", branch);\n\n        try {\n          await Deno.remove(target, { recursive: true });\n        } catch (err) {\n          if (!(err instanceof Deno.errors.NotFound)) {\n            throw err;\n          }\n        }\n\n        try {\n          await Deno.mkdir(path.dirname(target), { recursive: true });\n        } catch {\n          // Ignore\n        }\n\n        await copy(source, target);\n      }\n    },\n  };\n}\n"
  }
]