diff --git a/.github/instructions/bun.instructions.md b/.github/instructions/bun.instructions.md new file mode 100644 index 0000000..3080747 --- /dev/null +++ b/.github/instructions/bun.instructions.md @@ -0,0 +1,109 @@ +--- +applyTo: "**/*.{js,ts,jsx,tsx}" +--- + +Default to using Bun instead of Node.js. + +- Use `bun ` instead of `node ` or `ts-node ` +- Use `bun test` instead of `jest` or `vitest` +- Use `bun build ` instead of `webpack` or `esbuild` +- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install` +- Use `bun run + + +``` + +With the following `frontend.tsx`: + +```tsx#frontend.tsx +import React from "react"; + +// import .css files directly and it works +import './index.css'; + +import { createRoot } from "react-dom/client"; + +const root = createRoot(document.body); + +export default function Frontend() { + return

Hello, world!

; +} + +root.render(); +``` + +Then, run index.ts + +```sh +bun --hot ./index.ts +``` + +For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`. diff --git a/bun.lock b/bun.lock index d091cc9..09a6660 100644 --- a/bun.lock +++ b/bun.lock @@ -8,7 +8,7 @@ "name": "attache", "dependencies": { "@workshop/shared": "workspace:*", - "hono": "^4.8.0", + "hono": "catalog:", "nanoid": "^5.1.5", }, "devDependencies": { @@ -59,6 +59,20 @@ "typescript": "^5", }, }, + "packages/project-whitespace": { + "name": "@workspace/project-whitespace", + "dependencies": { + "@workshop/nano-remix": "workspace:*", + "@workshop/shared": "workspace:*", + "hono": "catalog:", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, "packages/query": { "name": "@workshop/query", "dependencies": { @@ -109,6 +123,7 @@ "@lezer/generator": "^1.7.3", "@lezer/highlight": "^1.2.1", "@lezer/lr": "^1.4.2", + "@workshop/nano-remix": "workspace:*", "hono": "catalog:", "luxon": "^3.6.1", "zzfx": "^1.3.0", @@ -117,7 +132,6 @@ "@types/bun": "latest", }, "peerDependencies": { - "hono": "^4", "typescript": "^5", }, }, @@ -125,7 +139,8 @@ "name": "@workshop/werewolf-ui", "version": "0.1.0", "dependencies": { - "hono": "^4.8.3", + "@workshop/nano-remix": "workspace:*", + "hono": "catalog:", "lucide-static": "^0.525.0", "tailwindcss": "^4.0.6", }, @@ -222,6 +237,8 @@ "@workshop/werewolf-ui": ["@workshop/werewolf-ui@workspace:packages/werewolf-ui"], + "@workspace/project-whitespace": ["@workspace/project-whitespace@workspace:packages/project-whitespace"], + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], "ajv": ["ajv@6.10.0", "", { "dependencies": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg=="], @@ -782,6 +799,8 @@ "@workshop/nano-remix/hono": ["hono@4.8.4", "", {}, "sha512-KOIBp1+iUs0HrKztM4EHiB2UtzZDTBihDtOF5K6+WaJjCPeaW4Q92R8j63jOhvJI5+tZSMuKD9REVEXXY9illg=="], + "@workshop/shared/@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], + "@workshop/todo/@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], "@workshop/todo/hono": ["hono@4.8.4", "", {}, "sha512-KOIBp1+iUs0HrKztM4EHiB2UtzZDTBihDtOF5K6+WaJjCPeaW4Q92R8j63jOhvJI5+tZSMuKD9REVEXXY9illg=="], @@ -790,12 +809,20 @@ "@workshop/werewolf-ui/hono": ["hono@4.8.4", "", {}, "sha512-KOIBp1+iUs0HrKztM4EHiB2UtzZDTBihDtOF5K6+WaJjCPeaW4Q92R8j63jOhvJI5+tZSMuKD9REVEXXY9illg=="], + "@workspace/project-whitespace/@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], + + "@workspace/project-whitespace/hono": ["hono@4.8.4", "", {}, "sha512-KOIBp1+iUs0HrKztM4EHiB2UtzZDTBihDtOF5K6+WaJjCPeaW4Q92R8j63jOhvJI5+tZSMuKD9REVEXXY9illg=="], + "ajv/fast-deep-equal": ["fast-deep-equal@2.0.1", "", {}, "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w=="], "amqplib/readable-stream": ["readable-stream@1.1.14", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", "isarray": "0.0.1", "string_decoder": "~0.10.x" } }, "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ=="], "amqplib/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "attache/@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], + + "attache/hono": ["hono@4.8.4", "", {}, "sha512-KOIBp1+iUs0HrKztM4EHiB2UtzZDTBihDtOF5K6+WaJjCPeaW4Q92R8j63jOhvJI5+tZSMuKD9REVEXXY9illg=="], + "babel-runtime/regenerator-runtime": ["regenerator-runtime@0.11.1", "", {}, "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="], "body-parser/content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], @@ -896,14 +923,20 @@ "@workshop/nano-remix/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], + "@workshop/shared/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], + "@workshop/todo/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], "@workshop/werewolf-ui/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], + "@workspace/project-whitespace/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], + "amqplib/readable-stream/inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], "amqplib/readable-stream/string_decoder": ["string_decoder@0.10.31", "", {}, "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="], + "attache/@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], + "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], diff --git a/main.ts b/main.ts index 33a3e26..21bcd56 100644 --- a/main.ts +++ b/main.ts @@ -24,7 +24,13 @@ const run = async (cmd: string[]) => { } try { - await Promise.all([run(["bun", "run", "--filter=@workshop/http", "start"]), run(["bun", "bot:discord"])]) + const isDev = process.env.NODE_ENV !== "production" + const noElide = isDev ? "--elide-lines=0" : "" + + await Promise.all([ + run(["bun", "run", noElide, "--filter=@workshop/http", "start"]), + run(["bun", "run", noElide, "bot:discord"]), + ]) console.log("โœ… All processes completed successfully") } catch (error) { console.error("โŒ One or more processes failed:", error) diff --git a/packages/attache/package.json b/packages/attache/package.json index 2dd607b..977152e 100644 --- a/packages/attache/package.json +++ b/packages/attache/package.json @@ -14,8 +14,8 @@ "typescript": "^5" }, "dependencies": { - "hono": "^4.8.0", + "hono": "catalog:", "nanoid": "^5.1.5", "@workshop/shared": "workspace:*" } -} +} \ No newline at end of file diff --git a/packages/http/src/orchestrator.ts b/packages/http/src/orchestrator.ts index f181007..cb5c37e 100644 --- a/packages/http/src/orchestrator.ts +++ b/packages/http/src/orchestrator.ts @@ -3,6 +3,43 @@ import { basename, join } from "node:path" export const startSubdomainServers = async () => { const portMap: Record = {} + const spawnedProcesses: { proc: any; name: string }[] = [] + + // Cleanup function to kill all spawned processes + const cleanup = () => { + console.log("๐Ÿงน Cleaning up spawned processes...") + spawnedProcesses.forEach(({ proc, name }) => { + try { + if (!proc.killed) { + console.log(`๐Ÿ”ช Killing process: ${name} (PID: ${proc.pid})`) + proc.kill("SIGTERM") // Just send SIGTERM and let the OS handle the rest + } + } catch (err) { + console.warn(`โš ๏ธ Error killing process ${name}:`, err) + } + }) + } + + // Register cleanup handlers + process.on("exit", cleanup) + process.on("SIGINT", () => { + cleanup() + process.exit(0) + }) + process.on("SIGTERM", () => { + cleanup() + process.exit(0) + }) + process.on("uncaughtException", (err) => { + console.error("โŒ Uncaught exception:", err) + cleanup() + process.exit(1) + }) + process.on("unhandledRejection", (reason) => { + console.error("โŒ Unhandled rejection:", reason) + cleanup() + process.exit(1) + }) try { const packageInfo = await subdomainPackageInfo() @@ -12,17 +49,24 @@ export const startSubdomainServers = async () => { const port = currentPort++ portMap[info.dirName] = port - return run(["bun", "run", `--filter=${info.packageName}`, "serve-subdomain"], { - env: { PORT: port.toString() }, - }) + return run( + ["bun", "run", `--filter=${info.packageName}`, "serve-subdomain"], + { + env: { PORT: port.toString() }, + }, + spawnedProcesses, + info.packageName + ) }) Promise.all(processes).catch((err) => { console.log(`โŒ Error starting subdomain servers:`, err) + cleanup() process.exit(1) }) } catch (error) { console.error("โŒ Error starting subdomain servers:", error) + cleanup() process.exit(1) } @@ -50,7 +94,12 @@ export const subdomainPackageInfo = async () => { return packagePaths } -const run = async (cmd: string[], options: { env?: Record } = {}) => { +const run = async ( + cmd: string[], + options: { env?: Record } = {}, + processTracker?: { proc: any; name: string }[], + processName?: string +) => { const commandText = cmd.join(" ") const proc = Bun.spawn(cmd, { stdout: "inherit", @@ -58,6 +107,12 @@ const run = async (cmd: string[], options: { env?: Record } = {} env: { ...process.env, ...options.env }, }) + // Track the process if tracker is provided + if (processTracker && processName) { + processTracker.push({ proc, name: processName }) + console.log(`๐Ÿš€ Started process: ${processName} (PID: ${proc.pid})`) + } + const status = await proc.exited if (status !== 0) { diff --git a/packages/http/src/server.tsx b/packages/http/src/server.tsx index b903680..39d996f 100644 --- a/packages/http/src/server.tsx +++ b/packages/http/src/server.tsx @@ -9,7 +9,7 @@ import { join } from "node:path" * 1. Starts multiple subdomain servers on different ports (3001+) * 2. Main server (port 3000) acts as a proxy that routes requests based on subdomain * 3. All requests are protected with Basic HTTP auth + persistent cookies - * 4. Once authenticated, cookies work across all subdomains (*.yourdomain.com) + * 4. Once authenticated, cookies work across all subdomains (*.thedomain.com) * * This server is designed to be run in production with NODE_ENV=production. On development, * it allows unauthenticated access because YOLO. diff --git a/packages/nano-remix/src/nanoRemix.ts b/packages/nano-remix/src/nanoRemix.ts index eedf0ff..0423e93 100644 --- a/packages/nano-remix/src/nanoRemix.ts +++ b/packages/nano-remix/src/nanoRemix.ts @@ -5,7 +5,7 @@ import { join, extname } from "node:path" type Options = { routesDir?: string distDir?: string - disableCache?: boolean // Disable caching for development + disableCache?: boolean } export const nanoRemix = async (req: Request, options: Options = {}) => { const nanoRemixDir = join(process.cwd(), ".nano-remix") diff --git a/packages/project-whiteboard/.gitignore b/packages/project-whiteboard/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/packages/project-whiteboard/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/packages/project-whiteboard/README.md b/packages/project-whiteboard/README.md new file mode 100644 index 0000000..28abc6c --- /dev/null +++ b/packages/project-whiteboard/README.md @@ -0,0 +1,15 @@ +# project-whitespace + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.2.18. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/packages/project-whiteboard/package.json b/packages/project-whiteboard/package.json new file mode 100644 index 0000000..d8448fa --- /dev/null +++ b/packages/project-whiteboard/package.json @@ -0,0 +1,20 @@ +{ + "name": "@workspace/project-whiteboard", + "module": "index.ts", + "type": "module", + "private": true, + "dependencies": { + "@workshop/shared": "workspace:*", + "@workshop/nano-remix": "workspace:*", + "hono": "catalog:" + }, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + }, + "scripts": { + "serve-subdomain": "bun run src/server.ts" + } +} \ No newline at end of file diff --git a/packages/project-whiteboard/public/whiteboard.jpeg b/packages/project-whiteboard/public/whiteboard.jpeg new file mode 100644 index 0000000..d58dbd9 Binary files /dev/null and b/packages/project-whiteboard/public/whiteboard.jpeg differ diff --git a/packages/project-whiteboard/src/routes/index.tsx b/packages/project-whiteboard/src/routes/index.tsx new file mode 100644 index 0000000..0d21187 --- /dev/null +++ b/packages/project-whiteboard/src/routes/index.tsx @@ -0,0 +1,8 @@ +export default function Index() { + return ( +
+

PROJECT WHITEBOARD

+ Project Whiteboard +
+ ) +} diff --git a/packages/project-whiteboard/src/server.ts b/packages/project-whiteboard/src/server.ts new file mode 100644 index 0000000..51e37b8 --- /dev/null +++ b/packages/project-whiteboard/src/server.ts @@ -0,0 +1,15 @@ +import { nanoRemix } from "@workshop/nano-remix" +import { join } from "path" + +Bun.serve({ + routes: { + "/": { + GET: (req) => { + return nanoRemix(req, { + routePath: join(import.meta.dir, "routes"), + publicDir: join(import.meta.dir, "..", "public"), + }) + }, + }, + }, +}) diff --git a/packages/project-whiteboard/tsconfig.json b/packages/project-whiteboard/tsconfig.json new file mode 100644 index 0000000..4410a1d --- /dev/null +++ b/packages/project-whiteboard/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false, + + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/packages/project-whitespace/src/server.ts b/packages/project-whitespace/src/server.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/todo/package.json b/packages/todo/package.json index c374974..a0abd80 100644 --- a/packages/todo/package.json +++ b/packages/todo/package.json @@ -9,6 +9,7 @@ "serve-subdomain": "bun run src/server.tsx" }, "dependencies": { + "@workshop/nano-remix": "workspace:*", "@codemirror/autocomplete": "^6.18.6", "@codemirror/commands": "^6.8.1", "@codemirror/language": "^6.11.1", @@ -30,7 +31,6 @@ "@types/bun": "latest" }, "peerDependencies": { - "typescript": "^5", - "hono": "^4" + "typescript": "^5" } } \ No newline at end of file diff --git a/packages/werewolf-ui/package.json b/packages/werewolf-ui/package.json index f21edd5..ffb9f97 100644 --- a/packages/werewolf-ui/package.json +++ b/packages/werewolf-ui/package.json @@ -14,7 +14,8 @@ "printWidth": 110 }, "dependencies": { - "hono": "^4.8.3", + "@workshop/nano-remix": "workspace:*", + "hono": "catalog:", "lucide-static": "^0.525.0", "tailwindcss": "^4.0.6" }, diff --git a/packages/werewolf-ui/tsconfig.json b/packages/werewolf-ui/tsconfig.json index cd90ae8..4410a1d 100644 --- a/packages/werewolf-ui/tsconfig.json +++ b/packages/werewolf-ui/tsconfig.json @@ -1,18 +1,35 @@ { "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext", "DOM"], "target": "ESNext", "module": "Preserve", - "moduleResolution": "bundler", + "moduleDetection": "force", "jsx": "react-jsx", "jsxImportSource": "hono/jsx", "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices "strict": true, "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false, + "baseUrl": ".", "paths": { "@/*": ["./src/*"] } - }, - "exclude": ["dist", "node_modules"] + } }