diff --git a/.gitignore b/.gitignore index ceaea36..8dd2e64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ # ---> Node +bun.lock +.nx +.nx/cache +.nx/worksapce-data # Logs logs *.log diff --git a/.versionrc.json b/.versionrc.json new file mode 100644 index 0000000..0c69c73 --- /dev/null +++ b/.versionrc.json @@ -0,0 +1,16 @@ +{ + "types": [ + { "type": "feat", "section": "🌟 Enhancements" }, + { "type": "fix", "section": "🐛 Bug fixes" }, + { "type": "chore", "hidden": false, "section": "📝 Chore" }, + { "type": "docs", "section": "📚 Documentation" }, + { "type": "style", "hidden": true }, + { "type": "refactor", "section": "🛠️ Code Refactor" }, + { "type": "perf", "hidden": false, "section": "🚀 Code Refactor" }, + { "type": "test", "section": "📝 Testing Code" }, + { "type": "ci", "section": "📈 Project changes" } + ], + "commitUrlFormat": "https://git.tuffraid.net/cowch/Lst_Backend/commits{{hash}}", + "compareUrlFormat": "https://git.tuffraid.net/cowch/Lst_Backend/compare/{{previousTag}}...{{currentTag}}", + "header": "# All notable changes to the LST project will be documented in this file.\n`" +} diff --git a/apps/frontend/.gitignore b/apps/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/apps/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/apps/frontend/README.md b/apps/frontend/README.md new file mode 100644 index 0000000..74872fd --- /dev/null +++ b/apps/frontend/README.md @@ -0,0 +1,50 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default tseslint.config({ + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Optionally add `...tseslint.configs.stylisticTypeChecked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: + +```js +// eslint.config.js +import react from 'eslint-plugin-react' + +export default tseslint.config({ + // Set the react version + settings: { react: { version: '18.3' } }, + plugins: { + // Add the react plugin + react, + }, + rules: { + // other rules... + // Enable its recommended rules + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + }, +}) +``` diff --git a/apps/frontend/eslint.config.js b/apps/frontend/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/apps/frontend/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/apps/frontend/index.html b/apps/frontend/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/apps/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/apps/frontend/package.json b/apps/frontend/package.json new file mode 100644 index 0000000..ac24bac --- /dev/null +++ b/apps/frontend/package.json @@ -0,0 +1,29 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "bunx --bun vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@eslint/js": "^9.19.0", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react-swc": "^3.5.0", + "eslint": "^9.19.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.18", + "globals": "^15.14.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.22.0", + "vite": "^6.1.0" + } +} diff --git a/apps/frontend/public/vite.svg b/apps/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/apps/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/frontend/src/App.css b/apps/frontend/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/apps/frontend/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/apps/frontend/src/App.tsx b/apps/frontend/src/App.tsx new file mode 100644 index 0000000..fe91cd4 --- /dev/null +++ b/apps/frontend/src/App.tsx @@ -0,0 +1,36 @@ +import { useState } from "react"; +import reactLogo from "./assets/react.svg"; +import viteLogo from "/vite.svg"; +import "./App.css"; +import { funnyFunction } from "@shared/lib"; + +function App() { + const [count, setCount] = useState(0); + funnyFunction(); + return ( + <> +
+ + Vite logo + + + React logo + +
+

Vite + React

+
+ +

+ Edit src/App.tsx and save to test HMR +

+
+

+ Click on the Vite and React logos to learn more +

+ + ); +} + +export default App; diff --git a/apps/frontend/src/assets/react.svg b/apps/frontend/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/apps/frontend/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/frontend/src/index.css b/apps/frontend/src/index.css new file mode 100644 index 0000000..6119ad9 --- /dev/null +++ b/apps/frontend/src/index.css @@ -0,0 +1,68 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/apps/frontend/src/main.tsx b/apps/frontend/src/main.tsx new file mode 100644 index 0000000..bef5202 --- /dev/null +++ b/apps/frontend/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/apps/frontend/src/vite-env.d.ts b/apps/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/apps/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/apps/frontend/tsconfig.app.json b/apps/frontend/tsconfig.app.json new file mode 100644 index 0000000..358ca9b --- /dev/null +++ b/apps/frontend/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/apps/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/apps/frontend/tsconfig.node.json b/apps/frontend/tsconfig.node.json new file mode 100644 index 0000000..db0becc --- /dev/null +++ b/apps/frontend/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/apps/frontend/vite.config.ts b/apps/frontend/vite.config.ts new file mode 100644 index 0000000..88aa67a --- /dev/null +++ b/apps/frontend/vite.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react-swc"; +import path from "path"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + build: { + outDir: path.resolve(__dirname, "../../dist/frontend/dist"), + emptyOutDir: true, + }, + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, + server: { + proxy: { + "/api": { target: "http://localhost:4000", changeOrigin: true }, + }, + }, +}); diff --git a/apps/ocme/index.ts b/apps/ocme/index.ts new file mode 100644 index 0000000..5c030ea --- /dev/null +++ b/apps/ocme/index.ts @@ -0,0 +1,14 @@ +import app from "./src/app"; +const port = process.env.OCME_PORT || 5001; //bun will automatically grab this as well. +Bun.serve({ + port, + fetch: app.fetch, + hostname: "0.0.0.0", +}); + +await Bun.build({ + entrypoints: ["./index.js"], + outdir: "../../dist/ocme", +}); + +console.log(`ocme is running on port ${port}`); diff --git a/apps/ocme/package.json b/apps/ocme/package.json new file mode 100644 index 0000000..96fb95e --- /dev/null +++ b/apps/ocme/package.json @@ -0,0 +1,13 @@ +{ + "name": "lstv2-ocme", + "version": "1.0.0", + "description": "", + "private": true, + "scripts": { + "dev": "bun --watch index.ts", + "build": "bun build ./index.ts" + }, + "devDependencies": { + "typescript": "^5.7.3" + } +} diff --git a/apps/ocme/src/app.ts b/apps/ocme/src/app.ts new file mode 100644 index 0000000..0b27d08 --- /dev/null +++ b/apps/ocme/src/app.ts @@ -0,0 +1,27 @@ +import { funnyFunction } from "@shared/lib"; +import { Hono } from "hono"; +import { serveStatic } from "hono/bun"; +import { logger } from "hono/logger"; +//import { expensesRoute } from "./routes/expenses"; + +const app = new Hono(); + +app.use("*", logger()); + +// running the hi function +funnyFunction(); +app.get("/", (c) => { + return c.json({ success: true, message: "hello from bun on the ocmeserver" }); +}); + +app.get("/api", (c) => { + return c.json({ success: true, message: "first api for ocme" }); +}); +//const apiRoute = app.basePath("/api").route("/expenses", expensesRoute); + +app.get("*", serveStatic({ root: "./frontend/dist" })); +app.get("*", serveStatic({ path: "./frontend/dist/index.html" })); + +export default app; + +//export type ApiRoute = typeof apiRoute; diff --git a/apps/ocme/tsconfig.json b/apps/ocme/tsconfig.json new file mode 100644 index 0000000..b925aba --- /dev/null +++ b/apps/ocme/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "index.ts"] +} diff --git a/apps/server/index.ts b/apps/server/index.ts new file mode 100644 index 0000000..c854ac3 --- /dev/null +++ b/apps/server/index.ts @@ -0,0 +1,14 @@ +import app from "./src/app"; +const port = process.env.SERVER_PORT || 4000; +Bun.serve({ + port, + fetch: app.fetch, + hostname: "0.0.0.0", +}); + +// await Bun.build({ +// entrypoints: ["./index.js"], +// outdir: "../../dist/server", +// }); + +console.log(`server is running on port ${port}`); diff --git a/apps/server/package.json b/apps/server/package.json new file mode 100644 index 0000000..e57ff76 --- /dev/null +++ b/apps/server/package.json @@ -0,0 +1,13 @@ +{ + "name": "lstv2-server", + "version": "1.0.0", + "description": "", + "private": true, + "scripts": { + "dev": "bun --watch index.ts", + "build": "bun build ./index.ts" + }, + "devDependencies": { + "typescript": "^5.7.3" + } +} diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts new file mode 100644 index 0000000..963c990 --- /dev/null +++ b/apps/server/src/app.ts @@ -0,0 +1,27 @@ +import { Hono } from "hono"; +import { serveStatic } from "hono/bun"; +import { logger } from "hono/logger"; +import { ocmeService } from "./services/ocmeServer"; +//import { expensesRoute } from "./routes/expenses"; + +const app = new Hono(); + +app.use("*", logger()); + +// as we dont want to change ocme again well use a proxy to this +app.all("/ocme/*", async (c) => { + return ocmeService(c); +}); + +app.get("/test", (c) => { + return c.json({ success: true, message: "hello from bun" }); +}); + +//const apiRoute = app.basePath("/api").route("/expenses", expensesRoute); + +app.get("*", serveStatic({ root: "../frontend/dist" })); +app.get("*", serveStatic({ path: "../frontend/dist/index.html" })); + +export default app; + +//export type ApiRoute = typeof apiRoute; diff --git a/apps/server/src/services/ocmeServer.ts b/apps/server/src/services/ocmeServer.ts new file mode 100644 index 0000000..f9ea8f5 --- /dev/null +++ b/apps/server/src/services/ocmeServer.ts @@ -0,0 +1,23 @@ +import { Context } from "hono"; +export const ocmeService = async (c: Context) => { + const url = new URL(c.req.url); + + const ocmeUrl = `http://localhost:${ + process.env.OCME_PORT + }${url.pathname.replace("/ocme", "")}`; + + console.log(ocmeUrl); + const ocmeResponse = await fetch(ocmeUrl, { + method: c.req.method, + headers: c.req.raw.headers, + body: + c.req.method !== "GET" && c.req.method !== "HEAD" + ? await c.req.text() + : undefined, + }); + + return new Response(ocmeResponse.body, { + status: ocmeResponse.status, + headers: ocmeResponse.headers, + }); +}; diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json new file mode 100644 index 0000000..b925aba --- /dev/null +++ b/apps/server/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "index.ts"] +} diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000..b2a8784 --- /dev/null +++ b/lerna.json @@ -0,0 +1,9 @@ +{ + "packages": ["apps/*", "packages/*"], + "version": "independent", + "command": { + "publish": { + "conventionalCommits": true + } + } +} diff --git a/nx.json b/nx.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nx.json @@ -0,0 +1 @@ +{} diff --git a/package.json b/package.json index b59d692..a333b31 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,70 @@ { - "name": "lstv2", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "dev": "dotenvx run -- pnpm run -r --parallel --aggregate-output dev", - "build": "pnpm run -r build", - "test": "pnpm run -r --parallel test", - "lint": "pnpm run -r --parallel lint", - "deploy": "pnpm build && pnpm run -r deploy" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@dotenvx/dotenvx": "^1.35.0", - "concurrently": "^9.1.2", - "cors": "^2.8.5", - "dotenv": "^16.4.7", - "express": "^5.0.1", - "hono": "^4.7.0", - "http-proxy-middleware": "^3.0.3", - "nodemon": "^3.1.9" - }, - "devDependencies": { - "@types/express": "^5.0.0", - "@types/node": "^22.10.2", - "eslint": "^9.19.0", - "typescript": "~5.7.3" + "name": "lstv2", + "version": "1.0.0", + "description": "", + "main": "index.ts", + "scripts": { + "dev": "dotenvx run -- bun run -r --parallel --aggregate-output dev", + "dev:all": "concurrently -n 'server,ocme,frontend' -c '#007755,#2f6da3,#c61cb8' 'cd apps/server && bun run dev' 'cd apps/ocme && bun run dev' 'cd apps/frontend && bun run dev'", + "dev:server": "bun --watch apps/server/index.ts", + "dev:ocme": "bun --watch apps/ocme/index.ts", + "dev:frontend": "cd apps/frontend && bunx --bun vite", + "build:server": "nx exec -- rimraf dist/server && cd apps/server && bun build index.js --outdir ../../dist/server", + "build:ocme": "nx exec -- rimraf dist/ocme && cd apps/ocme && bun build index.js --outdir ../../dist/ocme", + "build:front": "nx exec -- rimraf dist/frontend && cd apps/frontend && bun run build", + "start": "cd dist/server && bun run ./index.js", + "commit": "cz", + "clean": "rimraf dist/server" + }, + "workspaces": [ + "apps/*", + "packages/*" + ], + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@dotenvx/dotenvx": "^1.35.0", + "@types/bun": "^1.2.2", + "concurrently": "^9.1.2", + "cors": "^2.8.5", + "dotenv": "^16.4.7", + "hono": "^4.7.0", + "http-proxy-middleware": "^3.0.3", + "@shared/lib": "*" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "devDependencies": { + "cz-conventional-changelog": "^3.3.0", + "nx": "^20.4.4", + "rimraf": "^6.0.1", + "standard-version": "^9.5.0", + "typescript": "~5.7.3" + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" } + }, + "nx": { + "targets": { + "build": { + "cache": true, + "inputs": [ + "{projectRoot}/**/*.ts", + "{projectRoot}/tsconfig.json", + { + "externalDependencies": [ + "typescript" + ] + } + ], + "outputs": [ + "{projectRoot}/dist" + ] + } + } + } } diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 0000000..aa79810 --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,18 @@ +{ + "name": "@shared/lib", + "version": "0.0.1", + "main": "src/index.js", + "types": "src/index.d.ts", + "module": "src/index.js", + "type": "module", + "scripts": { + "build": "bun run tsc", + "clean": "rm -rf dist" + }, + "dependencies": { + "bun-types": "latest" + }, + "devDependencies": { + "typescript": "^5.7.3" + } +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts new file mode 100644 index 0000000..5519ff7 --- /dev/null +++ b/packages/shared/src/index.ts @@ -0,0 +1,5 @@ +export function funnyFunction() { + setInterval(() => { + console.log("hi"); + }, 1000); +} diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json new file mode 100644 index 0000000..5fca610 --- /dev/null +++ b/packages/shared/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "declaration": true + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..aaf4ecc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": true, + "noEmit": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "removeComments": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "types": [ + "bun-types" // add Bun global + ] + } +}