diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..9d08a1a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6f28e15
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+* text=auto
+*.lockb binary diff=lockb
diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..fba57ea
--- /dev/null
+++ b/.github/CODE_OF_CONDUCT.md
@@ -0,0 +1,83 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at chris@meema.xyz. All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 0000000..233e599
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,190 @@
+# Contributing
+
+First off, thank you for taking the time to contribute to the Stacks ecosystem ❤️
+
+> **Note**
+> The likelihood is high that the repo you are working on is either a Stack or Stacks itself. In both cases, you will be exposed to a meshup of technologies, like [Vue][vue], [Vite][vite], [Tauri][tauri], [Nitro][nitro], and [Bun][bun].
+
+## 💭 Knowledge
+
+### TypeScript
+
+It's important to note early on that these projects are written with [TypeScript][typescript]. If you're unfamiliar with it (or any strongly typed languages such as Java) then this may feel like a slight roadblock. However, there's never a truly perfect time to start learning it, so ... why not today using well-written codebases as your playground?
+
+_Don't be discouraged. You will get by learning TypeScript on-the-fly as you review some of the examples within the codebase. It's easy to get started—the code is, we hope, very approachable (and readable)._
+
+### Architecture
+
+An understanding of the framework architecture and design will help if you're looking to contribute long-term, or if you are working on a "more complex" PR. Browse the source and read our documentation to get a better sense of how it is structured. The documentation is very thorough and can be used as your progressive guide as you're learning more about Stacks.
+
+Feel free to ask any question _(Twitter, Discord, or GitHub Discussions)_, we would love to elaborate & collaborate.
+
+### Stacks/Core Setup
+
+Are you interested in contributing to the Stacks codebase?
+
+**Working on your first Pull Request?** You can learn how from this free series [How to Contribute to an Open Source Project on GitHub][pr-beginner-series].
+
+Head over to the [repository][stacks] on GitHub and click the Fork button in the top right corner. After the project has been forked, run the following commands in your terminal:
+
+```bash
+# Replace {github-username} with your GitHub username.
+git clone https://github.com/{github-username}/stacks --depth=1
+
+cd stacks
+
+# Create a branch for your PR, replace {issue-no} with the GitHub issue number.
+git checkout -b issue-{issue-no}
+```
+
+Now it'll help if we keep our `main` branch pointing at the original repository and make
+pull requests from the forked branch.
+
+```bash
+# Add the original repository as a "remote" called "upstream".
+git remote add upstream git@github.com:stacksjs/stacks.git
+
+# Fetch the git information from the remote.
+git fetch upstream
+
+# Set your local main branch to use the upstream main branch whenever you run `git pull`.
+git branch --set-upstream-to=upstream/main main
+
+# Run this when we want to update our version of main.
+git pull
+```
+
+_You may also use GitHub Desktop or any other GUI—if that is your preference._
+
+### Buddy Toolkit
+
+The following list of commands is one of the most common ways to interact with the Stacks API. Meet Buddy:
+
+```bash
+buddy install # installs all dependencies
+buddy dev # starts one of the dev servers (components, functions, views, desktop or docs)
+buddy build # follow CLI prompts to select which library (or server) to build
+buddy commit # follow CLI prompts for committing changes
+buddy release # creates the releases of the stack & consequently, publishes them (to npm)
+buddy upgrade # auto-update deps & the Stacks framework
+
+buddy make:component HelloWorld # scaffolds a component
+buddy make:function HelloWorld # scaffolds a function
+buddy make:page hello-world # scaffolds a page (https://my-project.test/hello-world)
+
+buddy help
+```
+
+
+View the complete Buddy Toolkit
+
+```bash
+buddy --help # view help menu
+buddy install # installs your dependencies
+buddy fresh # fresh reinstall of all deps
+buddy update # auto-update deps & the Stacks framework
+
+buddy --version # get the Stacks version
+buddy --help # view help menu
+
+# if you need any more info to any command listed here, you may suffix
+# any of them via the "help option", i.e. `buddy command --help`
+
+buddy dev # starts one of the dev servers (components, functions, views, or docs)
+buddy dev:components # starts local playground dev server
+buddy dev:desktop # starts the Desktop playground
+buddy dev:views # starts local playground views dev server
+buddy dev:functions # stubs local the functions
+buddy dev:docs # starts local docs dev server
+
+# for Laravel folks, `serve` may ring more familiar than the `dev` name. Hence, we aliased it:
+buddy serve # starts one of the dev servers (components, functions, viewsviews, or docs)
+buddy serve:components # starts local playground dev server
+buddy serve:views # starts local playground views dev server
+buddy serve:functions # stubs local the functions
+buddy serve:docs # starts local docs dev server
+
+# sets your application key
+buddy key:generate
+
+buddy make:stack project
+buddy make:component HelloWorld
+buddy make:function hello-world
+buddy make:page hello-world
+buddy make:lang de
+buddy make:database cars
+buddy make:table brands
+buddy make:migration create_cars_table
+buddy make:factory cars
+
+buddy lint # runs linter
+buddy lint:fix # runs linter and fixes issues
+
+buddy commit # follow CLI prompts for committing staged changes
+buddy release # creates the releases for the stack & triggers the Release Action (workflow)
+buddy changelog # generates CHANGELOG.md
+
+# building for production (e.g. npm, Vercel, Netlify, et al.)
+buddy build # select a specific build (follow CLI prompts)
+buddy build:components # builds Vue component library & Web Component library
+buddy build:functions # builds function library
+buddy build:vue-components # builds Vue 2 & 3-ready Component library
+buddy build:web-components # builds framework agnostic Web Component library (i.e. Custom Elements)
+buddy build:views # builds views
+
+# when deploying your app/s
+buddy deploy:docs
+buddy deploy:functions
+buddy deploy:views
+
+# select the example to run (follow CLI prompts)
+buddy example
+
+# test your stack
+buddy test # runs test suite (unit & e2e)
+buddy test:coverage # runs test coverage
+buddy test:types # runs typecheck
+```
+
+
+
+## 🧪 Testing
+
+All the framework tests are stored within the `./.stacks/tests` project folder. When adding or updating functionality, please ensure it is covered through our test suite. Ensure so by running `buddy test`.
+
+When working on an individual Stack, tests are stored within the `./tests` project folder & it is recommended to write tests (when useful). Bu
+
+## ✍️ Commit
+
+Stacks uses [semantic commit messages][semantic-commit-style] to automate package releases. No worries, you may not be aware what this is or how it works—just let Buddy guide you. Stacks automated the commit process for you, simply run `buddy commit` in your terminal and follow the instructions.
+
+For example,
+
+```bash
+# Add all changes to staging to be committed.
+git add .
+
+# Commit changes.
+buddy commit
+
+# Push changes up to GitHub.
+git push
+```
+
+_By following these minor steps, Stacks is able to automatically release new versions & generate relating local & remote changelogs._
+
+## 🎉 Pull Request
+
+When you're all done, head over to the [repository][stacks], and click the big green `Compare & Pull Request` button that should appear after you've pushed changes to your fork.
+
+Don't expect your PR to be accepted immediately or even accepted at all. Give the community time to vet it and see if it should be merged. Please don't be disheartened if it's not accepted. Your contribution is appreciated more than you can imagine, and even a unmerged PR can teach us a lot ❤️
+
+[typescript]: https://www.typescriptlang.org
+[vue]: https://vuejs.org/
+[vite]: https://vitejs.dev/
+[tauri]: https://tauri.app/
+[nitro]: https://nitro.unjs.io/
+[bun]: https://bun.sh/
+[stacks]: https://github.com/stacksjs/stacks
+[semantic-commit-style]: https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716
+[pr-beginner-series]: https://app.egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 0000000..744d5a6
--- /dev/null
+++ b/.github/SECURITY.md
@@ -0,0 +1,70 @@
+# Security Policy
+
+**PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, [SEE BELOW](#reporting-a-vulnerability).**
+
+## Supported Versions
+
+Only the latest major version receives security fixes.
+
+## Reporting a Vulnerability
+
+If you discover a security vulnerability within this package, please send an email to Chris Breuer at chris@meema.xyz. All security vulnerabilities will be promptly addressed.
+
+### Public PGP Key
+
+```
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: OpenPGP v2.0.8
+Comment: https://sela.io/pgp/
+
+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
+HF/6SiI=
+=le/X
+-----END PGP PUBLIC KEY BLOCK-----
+```
diff --git a/.github/art/cover.jpg b/.github/art/cover.jpg
new file mode 100644
index 0000000..d70f6bf
Binary files /dev/null and b/.github/art/cover.jpg differ
diff --git a/.github/art/github_social_preview.png b/.github/art/github_social_preview.png
new file mode 100644
index 0000000..1a2adf3
Binary files /dev/null and b/.github/art/github_social_preview.png differ
diff --git a/.github/renovate.json b/.github/renovate.json
new file mode 100644
index 0000000..66937d2
--- /dev/null
+++ b/.github/renovate.json
@@ -0,0 +1,3 @@
+{
+ "extends": ["@ow3"]
+}
diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 0000000..ece9846
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,14 @@
+daysUntilStale: 60
+daysUntilClose: 7
+exemptLabels:
+ - pinned
+ - security
+ - no-stale
+ - no stale
+ - pr welcome
+staleLabel: stale
+markComment: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs.
+ Thank you for your contributions.
+closeComment: false
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
new file mode 100644
index 0000000..79d40a0
--- /dev/null
+++ b/.github/workflows/README.md
@@ -0,0 +1,13 @@
+# GitHub Actions
+
+This folder contains the following GitHub Actions:
+
+- [CI][CI] - all CI jobs for the project
+ - lints the code
+ - `typecheck`s the code
+ - runs test suite
+ - runs on `ubuntu-latest`
+- [Release][Release] - automates the release process & changelog generation
+
+[CI]: ./workflows/ci.yml
+[Release]: ./workflows/release.yml
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..d0ab6fe
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,84 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+
+ pull_request:
+ branches:
+ - main
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Bun
+ uses: oven-sh/setup-bun@v2
+
+ - name: Use cached node_modules
+ uses: actions/cache@v4
+ with:
+ path: node_modules
+ key: node-modules-${{ hashFiles('**/bun.lockb') }}
+ restore-keys: |
+ node-modules-
+
+ - name: Install Dependencies
+ run: bun install
+
+ - name: Lint
+ run: bun run lint
+
+ typecheck:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Bun
+ uses: oven-sh/setup-bun@v2
+
+ - name: Use cached node_modules
+ uses: actions/cache@v4
+ with:
+ path: node_modules
+ key: node-modules-${{ hashFiles('**/bun.lockb') }}
+ restore-keys: |
+ node-modules-
+
+ - name: Install Dependencies
+ run: bun install
+
+ - name: Typecheck
+ run: bun --bun run typecheck
+
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Bun
+ uses: oven-sh/setup-bun@v2
+
+ - name: Use cached node_modules
+ uses: actions/cache@v4
+ with:
+ path: node_modules
+ key: node-modules-${{ hashFiles('**/bun.lockb') }}
+ restore-keys: |
+ node-modules-
+
+ - name: Install Dependencies
+ run: bun install
+
+ - name: Unit Test
+ run: bun test
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..1c546f4
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,40 @@
+name: CI
+
+on:
+ push:
+ tags:
+ - 'v*'
+
+jobs:
+ release:
+ name: release
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Install Bun
+ uses: oven-sh/setup-bun@v2
+
+ - name: Use cached node_modules
+ uses: actions/cache@v4
+ with:
+ path: node_modules
+ key: node-modules-${{ hashFiles('**/bun.lockb') }}
+ restore-keys: |
+ node-modules-
+
+ - name: Install Dependencies
+ run: bun install
+
+ - name: Publish to npm
+ run: bun publish --access public
+ env:
+ BUN_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
+
+ - name: Create GitHub release
+ run: bunx changelogithub
+ env:
+ GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f6a33e1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+.cache
+.DS_Store
+.idea
+*.log
+*.tgz
+coverage
+dist
+lib-cov
+logs
+node_modules
+temp
+docs/.vitepress/cache
diff --git a/.vscode/dictionary.txt b/.vscode/dictionary.txt
new file mode 100644
index 0000000..947c248
--- /dev/null
+++ b/.vscode/dictionary.txt
@@ -0,0 +1,41 @@
+antfu
+biomejs
+booleanish
+bumpp
+bunx
+changelogen
+changelogithub
+codecov
+commitlint
+commitlintrc
+composables
+davidanson
+degit
+deps
+destructurable
+dtsx
+entrypoints
+heroicons
+lockb
+openweb
+outdir
+pausable
+Postcardware
+prefetch
+preinstall
+quickfix
+socio
+Solana
+Spatie
+stacksjs
+tlsx
+typecheck
+unplugin
+unref
+upath
+vite
+vitebook
+vitejs
+vitepress
+vue-demi
+vueus
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..e8f046b
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,7 @@
+{
+ "recommendations": [
+ "dbaeumer.vscode-eslint",
+ "davidanson.vscode-markdownlint",
+ "streetsidesoftware.code-spell-checker"
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..0a512b3
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,153 @@
+{
+ // Disable the default formatter, use eslint instead
+ "prettier.enable": false,
+ "biome.enabled": false,
+ "editor.formatOnSave": false,
+ // Auto fix
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit",
+ "source.organizeImports": "never"
+ },
+ // Silent the stylistic rules in you IDE, but still auto fix them
+ "eslint.rules.customizations": [
+ {
+ "rule": "style/*",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "format/*",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-indent",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-spacing",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-spaces",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-order",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-dangle",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-newline",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*quotes",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*semi",
+ "severity": "off",
+ "fixable": true
+ }
+ ],
+ // Enable eslint for all supported languages
+ "eslint.validate": [
+ "javascript",
+ "javascriptreact",
+ "typescript",
+ "typescriptreact",
+ "vue",
+ "html",
+ "markdown",
+ "json",
+ "jsonc",
+ "yaml",
+ "toml",
+ "xml",
+ "gql",
+ "graphql",
+ "astro",
+ "svelte",
+ "css",
+ "less",
+ "scss",
+ "pcss",
+ "postcss"
+ ],
+ "[stx]": {
+ "editor.defaultFormatter": "dbaeumer.vscode-eslint"
+ },
+ // Shell
+ "[shellscript]": {
+ "editor.defaultFormatter": "foxundermoon.shell-format"
+ },
+ // Markdown
+ "[markdown]": {
+ "editor.defaultFormatter": "DavidAnson.vscode-markdownlint",
+ "editor.formatOnSave": true
+ },
+ "[dockerfile]": {
+ "editor.defaultFormatter": "foxundermoon.shell-format"
+ },
+ "markdownlint.config": {
+ "default": true,
+ "MD033": {
+ "allowed_elements": [
+ "details",
+ "summary",
+ "VPTeamPage",
+ "VPTeamPageTitle",
+ "VPTeamMembers",
+ "script"
+ ]
+ },
+ "MD041": false
+ },
+ "typescript.preferGoToSourceDefinition": true,
+ "editor.quickSuggestions": {
+ "strings": true
+ },
+ "git.enableSmartCommit": true,
+ "npm.enableRunFromFolder": true,
+ "npm.packageManager": "bun",
+ "editor.gotoLocation.multipleDefinitions": "goto",
+ "search.exclude": {
+ "**/node_modules": true,
+ "**/cdk.out": true,
+ "**/dist": true,
+ "CHANGELOG.md": true
+ },
+
+ "cSpell.ignorePaths": ["node_modules"],
+ "cSpell.dictionaries": ["custom-dictionary"],
+ "cSpell.diagnosticLevel": "Hint",
+ "cSpell.customDictionaries": {
+ "bun-plugin-env": {
+ "name": "custom-dictionary",
+ "path": "./.vscode/dictionary.txt",
+ "scope": "user",
+ "addWords": true
+ },
+ "custom": true // enable the `custom` dictionary
+ },
+ "terminal.integrated.scrollback": 10000,
+ "grammarly.files.include": [
+ "**/README.md",
+ "**/readme.md",
+ "**/*.txt"
+ ],
+ "grammarly.files.exclude": [
+ "**/dictionary.txt"
+ ]
+}
diff --git a/.zed/settings.json b/.zed/settings.json
new file mode 100644
index 0000000..30b4eb3
--- /dev/null
+++ b/.zed/settings.json
@@ -0,0 +1,139 @@
+// For a full list of overridable settings, and general information on folder-specific settings,
+// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
+{
+ "languages": {
+ "JavaScript": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ },
+ "TypeScript": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ },
+ "HTML": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ },
+ "CSS": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ },
+ "Markdown": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ },
+ "JSON": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ },
+ "JSONC": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ },
+ "YAML": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ },
+ "XML": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ },
+ "TOML": {
+ "formatter": {
+ "code_actions": {
+ "source.fixAll.eslint": true
+ }
+ }
+ }
+ },
+ "lsp": {
+ "eslint": {
+ "settings": {
+ "rulesCustomizations": [
+ {
+ "rule": "style/*",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "format/*",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-indent",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-spacing",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-spaces",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-order",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-dangle",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*-newline",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*quotes",
+ "severity": "off",
+ "fixable": true
+ },
+ {
+ "rule": "*semi",
+ "severity": "off",
+ "fixable": true
+ }
+ ]
+ }
+ }
+ },
+ "file_types": {
+ "JavaScript": [
+ "buddy"
+ ]
+ }
+}
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..1aef080
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,21 @@
+# MIT License
+
+Copyright (c) 2024 Open Web Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a9cbbab
--- /dev/null
+++ b/README.md
@@ -0,0 +1,98 @@
+
+
+[![npm version][npm-version-src]][npm-version-href]
+[![GitHub Actions][github-actions-src]][github-actions-href]
+[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
+
+
+
+# vite-plugin-local
+
+This is an opinionated TypeScript Starter kit to help kick-start development of your next Bun package.
+
+## Get Started
+
+It's rather simple to get your package development started:
+
+```bash
+# you may use this GitHub template or the following command:
+bunx degit stacksjs/ts-starter my-pkg
+cd my-pkg
+
+ # if you don't have pnpm installed, run `npm i -g pnpm`
+bun i # install all deps
+bun run build # builds the library for production-ready use
+
+# how to create a git commit?
+git add . # select the changes you want to commit
+bun run commit # then simply answer the questions
+
+# after you have successfully committed, you may create a "release"
+bun run release # automates git commits, versioning, and changelog generations
+```
+
+_Check out the package.json scripts for more commands._
+
+### Developer Experience (DX)
+
+This Starter Kit comes pre-configured with the following:
+
+- [Powerful Build Process](https://github.com/oven-sh/bun) - via Bun
+- [Fully Typed APIs](https://www.typescriptlang.org/) - via TypeScript
+- [Be a Good Commitizen](https://www.npmjs.com/package/git-cz) - pre-configured Commitizen & git-cz setup to simplify semantic git commits, versioning, and changelog generations
+- [Built With Testing In Mind](https://bun.sh/docs/cli/test) - pre-configured unit-testing powered by [Bun](https://bun.sh/docs/cli/test)
+- [Renovate](https://renovatebot.com/) - optimized & automated PR dependency updates
+- [Biome](https://biomejs.dev/) - an instant, fast linter & formatter for JavaScript, TypeScript, and CSS
+- [GitHub Actions](https://github.com/features/actions) - runs your CI _(fixes code style issues, tags releases & creates its changelogs, runs the test suite, etc.)_
+
+## Testing
+
+```bash
+bun test
+```
+
+## Changelog
+
+Please see our [releases](https://github.com/stackjs/bun-ts-starter/releases) page for more information on what has changed recently.
+
+## Contributing
+
+Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.
+
+## Community
+
+For help, discussion about best practices, or any other conversation that would benefit from being searchable:
+
+[Discussions on GitHub](https://github.com/stacksjs/ts-starter/discussions)
+
+For casual chit-chat with others using this package:
+
+[Join the Stacks Discord Server](https://discord.gg/stacksjs)
+
+## Postcardware
+
+Stacks OSS will always stay open-sourced, and we will always love to receive postcards from wherever Stacks is used! _And we also publish them on our website. Thank you, Spatie._
+
+Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States 🌎
+
+## Sponsors
+
+We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
+
+- [JetBrains](https://www.jetbrains.com/)
+- [The Solana Foundation](https://solana.com/)
+
+## 📄 License
+
+The MIT License (MIT). Please see [LICENSE](LICENSE.md) for more information.
+
+Made with 💙
+
+
+[npm-version-src]: https://img.shields.io/npm/v/bun-ts-starter?style=flat-square
+[npm-version-href]: https://npmjs.com/package/bun-ts-starter
+[github-actions-src]: https://img.shields.io/github/actions/workflow/status/stacksjs/ts-starter/ci.yml?style=flat-square&branch=main
+[github-actions-href]: https://github.com/stacksjs/ts-starter/actions?query=workflow%3Aci
+
+
diff --git a/build.ts b/build.ts
new file mode 100644
index 0000000..4e4163a
--- /dev/null
+++ b/build.ts
@@ -0,0 +1,7 @@
+import { dts } from 'bun-plugin-dts-auto'
+
+await Bun.build({
+ entrypoints: ['src/index.ts'],
+ outdir: './dist',
+ plugins: [dts()],
+})
diff --git a/bun.lockb b/bun.lockb
new file mode 100755
index 0000000..de6eca0
Binary files /dev/null and b/bun.lockb differ
diff --git a/bunfig.toml b/bunfig.toml
new file mode 100644
index 0000000..bbfe9c4
--- /dev/null
+++ b/bunfig.toml
@@ -0,0 +1,2 @@
+[install]
+registry = { url = "https://registry.npmjs.org/", token = "$BUN_AUTH_TOKEN" }
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
new file mode 100644
index 0000000..6747575
--- /dev/null
+++ b/docs/.vitepress/config.ts
@@ -0,0 +1,37 @@
+import { defineConfig } from 'vitepress'
+import LocalSetup from '../../src/index'
+
+// https://vitepress.dev/reference/site-config
+export default defineConfig({
+ title: 'vite-plugin-local',
+ description: 'A better developer environment.',
+ themeConfig: {
+ // https://vitepress.dev/reference/default-theme-config
+ nav: [
+ { text: 'Home', link: '/' },
+ { text: 'Examples', link: '/markdown-examples' },
+ ],
+
+ sidebar: [
+ {
+ text: 'Examples',
+ items: [
+ { text: 'Markdown Examples', link: '/markdown-examples' },
+ { text: 'Runtime API Examples', link: '/api-examples' },
+ ],
+ },
+ ],
+
+ socialLinks: [{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }],
+ },
+ vite: {
+ plugins: [
+ LocalSetup({
+ target: 'my-app.localhost', // Simple string usage
+ // OR with full options:
+ // target: { to: 'my-app.localhost' },
+ https: true, // Will use default SSL config
+ }),
+ ],
+ },
+})
diff --git a/docs/api-examples.md b/docs/api-examples.md
new file mode 100644
index 0000000..6bd8bb5
--- /dev/null
+++ b/docs/api-examples.md
@@ -0,0 +1,49 @@
+---
+outline: deep
+---
+
+# Runtime API Examples
+
+This page demonstrates usage of some of the runtime APIs provided by VitePress.
+
+The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files:
+
+```md
+
+
+## Results
+
+### Theme Data
+{{ theme }}
+
+### Page Data
+{{ page }}
+
+### Page Frontmatter
+{{ frontmatter }}
+```
+
+
+
+## Results
+
+### Theme Data
+{{ theme }}
+
+### Page Data
+{{ page }}
+
+### Page Frontmatter
+{{ frontmatter }}
+
+## More
+
+Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata).
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..1f6c833
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,24 @@
+---
+# https://vitepress.dev/reference/default-theme-home-page
+layout: home
+
+hero:
+ name: "vite-plugin-local"
+ text: "A better developer environment."
+ tagline: My great project tagline
+ actions:
+ - theme: brand
+ text: Markdown Examples
+ link: /markdown-examples
+ - theme: alt
+ text: API Examples
+ link: /api-examples
+
+features:
+ - title: Feature A
+ details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
+ - title: Feature B
+ details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
+ - title: Feature C
+ details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
+---
diff --git a/docs/markdown-examples.md b/docs/markdown-examples.md
new file mode 100644
index 0000000..f9258a5
--- /dev/null
+++ b/docs/markdown-examples.md
@@ -0,0 +1,85 @@
+# Markdown Extension Examples
+
+This page demonstrates some of the built-in markdown extensions provided by VitePress.
+
+## Syntax Highlighting
+
+VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting:
+
+**Input**
+
+````md
+```js{4}
+export default {
+ data () {
+ return {
+ msg: 'Highlighted!'
+ }
+ }
+}
+```
+````
+
+**Output**
+
+```js{4}
+export default {
+ data () {
+ return {
+ msg: 'Highlighted!'
+ }
+ }
+}
+```
+
+## Custom Containers
+
+**Input**
+
+```md
+::: info
+This is an info box.
+:::
+
+::: tip
+This is a tip.
+:::
+
+::: warning
+This is a warning.
+:::
+
+::: danger
+This is a dangerous warning.
+:::
+
+::: details
+This is a details block.
+:::
+```
+
+**Output**
+
+::: info
+This is an info box.
+:::
+
+::: tip
+This is a tip.
+:::
+
+::: warning
+This is a warning.
+:::
+
+::: danger
+This is a dangerous warning.
+:::
+
+::: details
+This is a details block.
+:::
+
+## More
+
+Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown).
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..d24b632
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,15 @@
+import stacks from '@stacksjs/eslint-config'
+
+export default stacks({
+ stylistic: {
+ indent: 2,
+ quotes: 'single',
+ },
+
+ typescript: true,
+ jsonc: true,
+ yaml: true,
+ ignores: [
+ 'fixtures/**',
+ ],
+})
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..033f86e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,63 @@
+{
+ "name": "vite-plugin-local",
+ "type": "module",
+ "version": "0.0.0",
+ "description": "A Vite plugin to enable a development environment for minimalists.",
+ "author": "Chris Breuer ",
+ "license": "MIT",
+ "homepage": "https://github.com/stacksjs/vite-plugin-local#readme",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/stacksjs/vite-plugin-local.git"
+ },
+ "bugs": {
+ "url": "https://github.com/stacksjs/vite-plugin-local/issues"
+ },
+ "keywords": ["typescript", "vite", "plugin", "local", "vite-plugin-local", "development", "environment", "reverse-proxy", "ssl", "https", "rpx", "tlsx", "minimalists"],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ },
+ "./*": {
+ "import": "./dist/*"
+ }
+ },
+ "module": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "files": ["dist"],
+ "scripts": {
+ "build": "bun --bun build.ts",
+ "lint": "bunx eslint .",
+ "lint:fix": "bunx eslint . --fix",
+ "fresh": "bunx rimraf node_modules/ bun.lock && bun i",
+ "commit": "git cz",
+ "changelog": "bunx changelogen --output CHANGELOG.md",
+ "prepublishOnly": "bun --bun run build",
+ "release": "bun run changelog && bunx bumpp package.json --all",
+ "test": "bun test",
+ "typecheck": "bun --bun tsc --noEmit",
+ "dev:docs": "vitepress dev docs",
+ "build:docs": "vitepress build docs",
+ "preview:docs": "vitepress preview docs"
+ },
+ "devDependencies": {
+ "@stacksjs/eslint-config": "^3.8.1-beta.2",
+ "@stacksjs/rpx": "^0.4.1",
+ "@types/bun": "^1.1.14",
+ "bumpp": "^9.8.1",
+ "bun-plugin-dtsx": "^0.21.9",
+ "changelogen": "^0.5.7",
+ "lint-staged": "^15.2.10",
+ "simple-git-hooks": "^2.11.1",
+ "typescript": "^5.7.2",
+ "vite": "^6.0.1",
+ "vitepress": "^1.5.0"
+ },
+ "simple-git-hooks": {
+ "pre-commit": "bun lint-staged"
+ },
+ "lint-staged": {
+ "*.{js,ts}": "bunx eslint . --fix"
+ }
+}
diff --git a/pkgx.yaml b/pkgx.yaml
new file mode 100644
index 0000000..d115106
--- /dev/null
+++ b/pkgx.yaml
@@ -0,0 +1,2 @@
+dependencies:
+ bun.sh: ^1.1.38
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..d009ce9
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,66 @@
+/* eslint-disable no-console */
+import type { Plugin } from 'vite'
+import type { VitePluginLocalOptions } from './types'
+import { cleanup, startProxies } from '@stacksjs/rpx'
+import { buildConfig } from './utils'
+
+export function VitePluginLocal(options: VitePluginLocalOptions): Plugin {
+ const {
+ enabled = true,
+ verbose = false,
+ etcHostsCleanup = true,
+ } = options
+
+ let domains: string[] | undefined
+
+ return {
+ name: 'vite-plugin-local',
+
+ async configureServer(server) {
+ if (!enabled)
+ return
+
+ try {
+ const host = server.config.server.host || 'localhost'
+ const port = server.config.server.port || 5173
+ const serverUrl = `${host}:${port}`
+
+ const config = buildConfig(options, serverUrl)
+ domains = [config.to]
+
+ await startProxies(config)
+
+ if (verbose) {
+ console.log('\nProxy Configuration:')
+ console.log('-------------------')
+ console.log('From:', serverUrl)
+ console.log('To:', config.to)
+ console.log('HTTPS:', !!config.https)
+ console.log('Cleanup enabled:', config.etcHostsCleanup)
+ }
+
+ console.log(`🔄 Reverse proxy active: ${serverUrl} → ${config.to}`)
+ if (config.https)
+
+ console.log('🔒 HTTPS enabled with automatic certificate management')
+ }
+ catch (error) {
+ console.error('Failed to start reverse proxy:', error)
+ }
+ },
+
+ async closeBundle() {
+ if (domains?.length) {
+ await cleanup({
+ domains,
+ etcHostsCleanup,
+ verbose,
+ })
+ domains = undefined
+ }
+ },
+ }
+}
+
+export type { VitePluginLocalOptions }
+export default VitePluginLocal
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..e526c04
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,38 @@
+import type { CustomTlsConfig } from '@stacksjs/rpx'
+
+export interface VitePluginLocalOptions {
+ /**
+ * Enable/disable the plugin
+ * @default true
+ */
+ enabled?: boolean
+
+ /**
+ * The target domain to proxy to (e.g., 'my-app.localhost')
+ * @example 'my-app.localhost'
+ */
+ to: string
+
+ /**
+ * SSL/TLS configuration
+ * - true: uses default SSL config
+ * - false: disables SSL
+ * - object: custom SSL configuration
+ * @default false
+ */
+ https?: boolean | CustomTlsConfig
+
+ /**
+ * Whether to cleanup /etc/hosts entries on shutdown
+ * @default true
+ */
+ etcHostsCleanup?: boolean
+
+ /**
+ * Enable verbose logging
+ * @default false
+ */
+ verbose?: boolean
+}
+
+export type { CustomTlsConfig }
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..153b435
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,22 @@
+import type { SingleReverseProxyConfig } from '@stacksjs/rpx'
+import type { VitePluginLocalOptions } from './types'
+import os from 'node:os'
+import path from 'node:path'
+
+export function getDefaultSSLConfig(): { caCertPath: string, certPath: string, keyPath: string } {
+ return {
+ caCertPath: path.join(os.homedir(), '.stacks', 'ssl', `stacks.localhost.ca.crt`),
+ certPath: path.join(os.homedir(), '.stacks', 'ssl', `stacks.localhost.crt`),
+ keyPath: path.join(os.homedir(), '.stacks', 'ssl', `stacks.localhost.crt.key`),
+ }
+}
+
+export function buildConfig(options: VitePluginLocalOptions, serverUrl: string): SingleReverseProxyConfig {
+ return {
+ from: serverUrl,
+ to: options.to,
+ https: options.https === true ? getDefaultSSLConfig() : options.https || false,
+ etcHostsCleanup: options.etcHostsCleanup ?? true,
+ verbose: options.verbose ?? false,
+ }
+}
diff --git a/test/index.test.ts b/test/index.test.ts
new file mode 100644
index 0000000..e4e6ab5
--- /dev/null
+++ b/test/index.test.ts
@@ -0,0 +1,128 @@
+import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock, spyOn, test } from 'bun:test'
+
+// Example function to test
+function add(a: number, b: number): number {
+ return a + b
+}
+
+// Example async function to test
+async function fetchData(): Promise {
+ return new Promise(resolve => setTimeout(() => resolve('data'), 100))
+}
+
+// Example module to mock
+const apiModule = {
+ fetchUserData: async (id: number) => {
+ // Simulating an API call
+ return { id, name: 'John Doe', email: 'john@example.com' }
+ },
+}
+
+describe('my awesome package', () => {
+ let testValue: number
+
+ beforeAll(() => {
+ // eslint-disable-next-line no-console
+ console.log('Running before all tests')
+ })
+
+ afterAll(() => {
+ // eslint-disable-next-line no-console
+ console.log('Running after all tests')
+ })
+
+ beforeEach(() => {
+ testValue = 10
+ })
+
+ afterEach(() => {
+ testValue = 0
+ })
+
+ it('should demonstrate basic assertion', () => {
+ expect(1).toBe(1)
+ })
+
+ it('should test the add function', () => {
+ expect(add(2, 3)).toBe(5)
+ expect(add(-1, 1)).toBe(0)
+ })
+
+ it('should work with various matchers', () => {
+ expect(true).toBeTruthy()
+ expect(false).toBeFalsy()
+ expect(null).toBeNull()
+ expect(undefined).toBeUndefined()
+ expect([1, 2, 3]).toContain(2)
+ expect({ name: 'John' }).toHaveProperty('name')
+ expect(() => {
+ throw new Error('Test error')
+ }).toThrow('Test error')
+ })
+
+ it('should handle async tests', async () => {
+ const result = await fetchData()
+ expect(result).toBe('data')
+ })
+
+ it('should use beforeEach and afterEach', () => {
+ expect(testValue).toBe(10)
+ testValue += 5
+ expect(testValue).toBe(15)
+ })
+
+ test('should work with test function as well', () => {
+ expect(true).toBe(true)
+ })
+
+ it('should demonstrate spy functionality', () => {
+ const consoleSpy = spyOn(console, 'log')
+ // eslint-disable-next-line no-console
+ console.log('Test message')
+ expect(consoleSpy).toHaveBeenCalledWith('Test message')
+ consoleSpy.mockRestore()
+ })
+
+ it.todo('should implement this test later')
+
+ it.skip('should skip this test', () => {
+ // This test will be skipped
+ expect(true).toBe(false)
+ })
+
+ describe('Mocking example', () => {
+ it('should demonstrate mock functionality', async () => {
+ // Create a mock function
+ const mockFetchUserData = mock((id: number) => {
+ return Promise.resolve({ id, name: 'Mocked User', email: 'mocked@example.com' })
+ })
+
+ // Replace the original function with the mock
+ apiModule.fetchUserData = mockFetchUserData
+
+ // Use the mocked function
+ const result = await apiModule.fetchUserData(1)
+
+ // Assert the mock was called with the correct argument
+ expect(mockFetchUserData).toHaveBeenCalledWith(1)
+
+ // Assert the mock returned the expected result
+ expect(result).toEqual({ id: 1, name: 'Mocked User', email: 'mocked@example.com' })
+
+ // Check how many times the mock was called
+ expect(mockFetchUserData).toHaveBeenCalledTimes(1)
+
+ // Reset the mock
+ mockFetchUserData.mockReset()
+
+ // Provide a new implementation for the mock
+ mockFetchUserData.mockImplementation((id: number) => {
+ return Promise.resolve({ id, name: 'New Mock', email: 'new@example.com' })
+ })
+
+ // Use the mock with the new implementation
+ const newResult = await apiModule.fetchUserData(2)
+ expect(newResult).toEqual({ id: 2, name: 'New Mock', email: 'new@example.com' })
+ })
+ })
+})
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..473fc0c
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "lib": ["esnext"],
+ "moduleDetection": "force",
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "types": ["bun"],
+ "allowImportingTsExtensions": true,
+ "strict": true,
+ "strictNullChecks": true,
+ "noFallthroughCasesInSwitch": true,
+ "declaration": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "isolatedDeclarations": true,
+ "isolatedModules": true,
+ "verbatimModuleSyntax": true,
+ "skipDefaultLibCheck": true,
+ "skipLibCheck": true
+ }
+}