diff --git a/README.md b/README.md new file mode 100644 index 0000000..deb23a8 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ + # Playground + +A repository to play around in diff --git a/React/DoItJust/src/App.tsx b/React/DoItJust/src/App.tsx index 0d19ca3..5a54b8c 100644 --- a/React/DoItJust/src/App.tsx +++ b/React/DoItJust/src/App.tsx @@ -1,8 +1,10 @@ -import {useAppSelector} from './store'; -import {TodoItem} from './entities/TodoItem'; +import {selectTodoItems} from "./store/slices"; import {TodoItemComponent} from './components/TodoItem/TodoItem'; +import { useAppSelector } from "./store"; + export default function App() { - const items = useAppSelector(state => state.todo.items); + const items = useAppSelector(selectTodoItems); + return (
diff --git a/React/DoItJust/src/components/TodoItem/TodoItem.tsx b/React/DoItJust/src/components/TodoItem/TodoItem.tsx index 8735f90..7791a75 100644 --- a/React/DoItJust/src/components/TodoItem/TodoItem.tsx +++ b/React/DoItJust/src/components/TodoItem/TodoItem.tsx @@ -6,7 +6,14 @@ export interface ITodoItemProps { } export const TodoItemComponent = ({todo: {id, title, notes, createdAt, updatedAt, dueAt, isArchived, isDone}}: ITodoItemProps): JSX.Element => { - return
+ return
{title}
diff --git a/React/DoItJust/src/store/slices/TodoSlice.ts b/React/DoItJust/src/store/slices/TodoSlice.ts index 37e8272..c040a07 100644 --- a/React/DoItJust/src/store/slices/TodoSlice.ts +++ b/React/DoItJust/src/store/slices/TodoSlice.ts @@ -1,5 +1,5 @@ import type {Nullable} from '../../types/definedTypes'; -import {createSlice} from '@reduxjs/toolkit'; +import {createSelector, createSlice} from '@reduxjs/toolkit'; import {TodoItem} from '../../entities/TodoItem'; interface ITodoSliceState { @@ -20,6 +20,7 @@ const initialTodoState: ITodoSliceState = { error: null }; + export const TodoSlice = createSlice({ name: 'todo', initialState: initialTodoState, @@ -61,5 +62,8 @@ export const TodoSlice = createSlice({ //} }); + +const selectTodos = (state: {todo: ITodoSliceState}) => state.todo; +export const selectTodoItems = createSelector([selectTodos], todo => todo.items); export const {appendTodoItem} = TodoSlice.actions; export const todoSliceStateReducer = TodoSlice.reducer; diff --git a/RequireJs/require-js-node/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc b/RequireJs/require-js-node/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc new file mode 120000 index 0000000..6100270 --- /dev/null +++ b/RequireJs/require-js-node/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc @@ -0,0 +1 @@ +../../CLAUDE.md \ No newline at end of file diff --git a/RequireJs/require-js-node/.gitignore b/RequireJs/require-js-node/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/RequireJs/require-js-node/.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/Web/WebSocket/websocket/frontend/.npmrc b/RequireJs/require-js-node/.npmrc similarity index 100% rename from Web/WebSocket/websocket/frontend/.npmrc rename to RequireJs/require-js-node/.npmrc diff --git a/Web/WebSocket/websocket/server/.prettierrc b/RequireJs/require-js-node/.prettierrc similarity index 100% rename from Web/WebSocket/websocket/server/.prettierrc rename to RequireJs/require-js-node/.prettierrc diff --git a/RequireJs/require-js-node/CLAUDE.md b/RequireJs/require-js-node/CLAUDE.md new file mode 100644 index 0000000..b8100b7 --- /dev/null +++ b/RequireJs/require-js-node/CLAUDE.md @@ -0,0 +1,111 @@ +--- +description: Use Bun instead of Node.js, npm, pnpm, or vite. +globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json" +alwaysApply: false +--- + +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/RequireJs/require-js-node/README.md b/RequireJs/require-js-node/README.md new file mode 100644 index 0000000..1404cbf --- /dev/null +++ b/RequireJs/require-js-node/README.md @@ -0,0 +1,15 @@ +# require-js-node + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run src/index.ts +``` + +This project was created using `bun init` in bun v1.2.21. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. diff --git a/RequireJs/require-js-node/bun.lock b/RequireJs/require-js-node/bun.lock new file mode 100644 index 0000000..2cbc9f4 --- /dev/null +++ b/RequireJs/require-js-node/bun.lock @@ -0,0 +1,37 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "require-js-node", + "dependencies": { + "requirejs": "2.3.7", + }, + "devDependencies": { + "@types/bun": "latest", + "@types/requirejs": "2.1.37", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="], + + "@types/node": ["@types/node@24.3.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g=="], + + "@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="], + + "@types/requirejs": ["@types/requirejs@2.1.37", "", {}, "sha512-jmFgr3mwN2NSmtRP6IpZ2nfRS7ufSXuDYQ6YyPFArN8x5dARQcD/DXzT0J6NYbvquVT4pg9K9HWdi6e6DZR9iQ=="], + + "bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "requirejs": ["requirejs@2.3.7", "", { "bin": { "r.js": "bin/r.js", "r_js": "bin/r.js" } }, "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw=="], + + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + + "undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], + } +} diff --git a/RequireJs/require-js-node/package.json b/RequireJs/require-js-node/package.json new file mode 100644 index 0000000..06dac0e --- /dev/null +++ b/RequireJs/require-js-node/package.json @@ -0,0 +1,16 @@ +{ + "name": "require-js-node", + "module": "src/index.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest", + "@types/requirejs": "2.1.37" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "requirejs": "2.3.7" + } +} diff --git a/RequireJs/require-js-node/src/index.ts b/RequireJs/require-js-node/src/index.ts new file mode 100644 index 0000000..7bd03a5 --- /dev/null +++ b/RequireJs/require-js-node/src/index.ts @@ -0,0 +1,7 @@ +// requireJs + +import {requirejs} from 'requirejs'; + +requirejs.config({ + +}) diff --git a/RequireJs/require-js-node/tsconfig.json b/RequireJs/require-js-node/tsconfig.json new file mode 100644 index 0000000..81b477a --- /dev/null +++ b/RequireJs/require-js-node/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "es6", + "module": "AMD", + "moduleDetection": "force", + "jsx": "react-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 + }, + "include": ["src"], + "exclude": [], +} diff --git a/Web/WebSocket/package.json b/Web/WebSocket/package.json new file mode 100644 index 0000000..5cd2d7a --- /dev/null +++ b/Web/WebSocket/package.json @@ -0,0 +1,5 @@ +{ + "name": "@techniker-me/websocket-chat", + "version": "0.0.0", + "workspaces": ["server", "frontend"] +} diff --git a/Web/WebSocket/websocket/server/.npmrc b/Web/WebSocket/websocket/.npmrc similarity index 100% rename from Web/WebSocket/websocket/server/.npmrc rename to Web/WebSocket/websocket/.npmrc diff --git a/Web/WebSocket/websocket/server/.nvmrc b/Web/WebSocket/websocket/.nvmrc similarity index 100% rename from Web/WebSocket/websocket/server/.nvmrc rename to Web/WebSocket/websocket/.nvmrc diff --git a/Web/WebSocket/websocket/.prettierrc b/Web/WebSocket/websocket/.prettierrc new file mode 100644 index 0000000..caa814d --- /dev/null +++ b/Web/WebSocket/websocket/.prettierrc @@ -0,0 +1,12 @@ +{ + "arrowParens": "avoid", + "bracketSameLine": true, + "bracketSpacing": false, + "printWidth": 160, + "semi": true, + "singleAttributePerLine": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none", + "useTabs": false +} diff --git a/Web/WebSocket/websocket/README.md b/Web/WebSocket/websocket/README.md new file mode 100644 index 0000000..dee02dd --- /dev/null +++ b/Web/WebSocket/websocket/README.md @@ -0,0 +1,60 @@ +# WebSocket Chat Monorepo + +A real-time WebSocket chat application built with TypeScript, Express, and Vite. + +## Project Structure + +``` +├── apps/ +│ ├── server/ # Express WebSocket server +│ └── frontend/ # React/Vite frontend +├── packages/ +│ └── shared-types/ # Shared TypeScript types +└── package.json # Root workspace configuration +``` + +## Quick Start + +```bash +# Install all dependencies +npm install + +# Start both server and frontend in development mode +npm start + +# Or run individually: +npm run dev:server # Start server only +npm run dev:frontend # Start frontend only +``` + +## Available Scripts + +- `npm start` - Build shared types and start both server and frontend +- `npm run dev:all` - Start both server and frontend concurrently +- `npm run dev:server` - Start server only +- `npm run dev:frontend` - Start frontend only +- `npm run build` - Build all packages +- `npm run typecheck` - Type check all packages +- `npm run lint` - Lint all packages +- `npm run clean` - Clean all build outputs + +## Development + +The monorepo uses npm workspaces for dependency management and TypeScript project references for type checking across packages. + +### Server +- Runs on `http://localhost:3000` +- WebSocket endpoint: `ws://localhost:3000/ws` +- Health check: `http://localhost:3000/ping` + +### Frontend +- Runs on `http://localhost:5173` (Vite default) +- Connects to WebSocket server for real-time messaging + +## Features + +- Real-time WebSocket communication +- Ping/pong latency measurement +- Shared TypeScript types across packages +- Hot reload for development +- Monorepo workspace management diff --git a/Web/WebSocket/websocket/frontend/bun.lock b/Web/WebSocket/websocket/apps/frontend/bun.lock similarity index 100% rename from Web/WebSocket/websocket/frontend/bun.lock rename to Web/WebSocket/websocket/apps/frontend/bun.lock diff --git a/Web/WebSocket/websocket/frontend/index.html b/Web/WebSocket/websocket/apps/frontend/index.html similarity index 100% rename from Web/WebSocket/websocket/frontend/index.html rename to Web/WebSocket/websocket/apps/frontend/index.html diff --git a/Web/WebSocket/websocket/frontend/package-lock.json b/Web/WebSocket/websocket/apps/frontend/package-lock.json similarity index 100% rename from Web/WebSocket/websocket/frontend/package-lock.json rename to Web/WebSocket/websocket/apps/frontend/package-lock.json diff --git a/Web/WebSocket/websocket/apps/frontend/package.json b/Web/WebSocket/websocket/apps/frontend/package.json new file mode 100644 index 0000000..c46a731 --- /dev/null +++ b/Web/WebSocket/websocket/apps/frontend/package.json @@ -0,0 +1,22 @@ +{ + "name": "@techniker-me/websocket-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "typecheck": "tsc", + "clean": "rm -rf dist", + "lint": "echo 'No linting configured for frontend'" + }, + "devDependencies": { + "typescript": "5.9.2", + "vite": "7.1.7" + }, + "dependencies": { + "@techniker-me/logger": "0.0.15", + "@techniker-me/tools": "2025.0.16" + } +} diff --git a/Web/WebSocket/websocket/frontend/src/main.ts b/Web/WebSocket/websocket/apps/frontend/src/main.ts similarity index 96% rename from Web/WebSocket/websocket/frontend/src/main.ts rename to Web/WebSocket/websocket/apps/frontend/src/main.ts index ba52720..77e3cbe 100644 --- a/Web/WebSocket/websocket/frontend/src/main.ts +++ b/Web/WebSocket/websocket/apps/frontend/src/main.ts @@ -25,6 +25,11 @@ messagesElement.id = 'messages-container'; const messageInput = document.createElement('input'); messageInput.type = 'text'; messageInput.disabled = true; +messageInput.onkeydown = ({target}) => { + if (target.value === 'enter') { + sendMessage() + } +}; const actionButton = document.createElement('button'); actionButton.innerText = 'Set your username!'; @@ -32,8 +37,8 @@ actionButton.innerText = 'Set your username!'; disposables.add(websocket.on('message', (message: any) => { if (message.pong) { console.log('[WebSocket] Received message', message); - // Use server-calculated RTT const rtt = message.pong.rtt || 0; + rttContainer.innerHTML = `RTT: [${rtt.toFixed(2)}] ms`; } else { messagesElement.innerHTML += `
[${new Date(message.sentAt).toLocaleString("en-US", {timeStyle: 'short'})}]${message.message.author}: ${message.message.payload}
`; @@ -50,9 +55,11 @@ chatContainer.append(actionButton); appContainer.append(chatContainer); actionButton.onclick = () => { const usernamePrompt = prompt('Enter your name:'); + if (!usernamePrompt) { return; } + username = usernamePrompt; messageInput.disabled = false; messageInput.placeholder = 'Enter a message...'; @@ -62,6 +69,7 @@ actionButton.onclick = () => { function sendMessage() { console.log('[actionButton] Set username'); + if (!messageInput.value) { return; } diff --git a/Web/WebSocket/websocket/frontend/src/messages/MessageFactory.ts b/Web/WebSocket/websocket/apps/frontend/src/messages/MessageFactory.ts similarity index 100% rename from Web/WebSocket/websocket/frontend/src/messages/MessageFactory.ts rename to Web/WebSocket/websocket/apps/frontend/src/messages/MessageFactory.ts diff --git a/Web/WebSocket/websocket/frontend/src/styles.css b/Web/WebSocket/websocket/apps/frontend/src/styles.css similarity index 100% rename from Web/WebSocket/websocket/frontend/src/styles.css rename to Web/WebSocket/websocket/apps/frontend/src/styles.css diff --git a/Web/WebSocket/websocket/frontend/src/websockets/WebSocketConnection.ts b/Web/WebSocket/websocket/apps/frontend/src/websockets/WebSocketConnection.ts similarity index 100% rename from Web/WebSocket/websocket/frontend/src/websockets/WebSocketConnection.ts rename to Web/WebSocket/websocket/apps/frontend/src/websockets/WebSocketConnection.ts diff --git a/Web/WebSocket/websocket/frontend/src/websockets/WebSocketConnectionStatus.ts b/Web/WebSocket/websocket/apps/frontend/src/websockets/WebSocketConnectionStatus.ts similarity index 100% rename from Web/WebSocket/websocket/frontend/src/websockets/WebSocketConnectionStatus.ts rename to Web/WebSocket/websocket/apps/frontend/src/websockets/WebSocketConnectionStatus.ts diff --git a/Web/WebSocket/websocket/apps/frontend/tsconfig.json b/Web/WebSocket/websocket/apps/frontend/tsconfig.json new file mode 100644 index 0000000..a9ed56a --- /dev/null +++ b/Web/WebSocket/websocket/apps/frontend/tsconfig.json @@ -0,0 +1,30 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": false, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"], + "references": [ + { "path": "../../packages/shared-types" } + ] +} diff --git a/Web/WebSocket/websocket/frontend/types/Units.ts b/Web/WebSocket/websocket/apps/frontend/types/Units.ts similarity index 100% rename from Web/WebSocket/websocket/frontend/types/Units.ts rename to Web/WebSocket/websocket/apps/frontend/types/Units.ts diff --git a/Web/WebSocket/websocket/server/assets/favicon/about.txt b/Web/WebSocket/websocket/apps/server/assets/favicon/about.txt similarity index 100% rename from Web/WebSocket/websocket/server/assets/favicon/about.txt rename to Web/WebSocket/websocket/apps/server/assets/favicon/about.txt diff --git a/Web/WebSocket/websocket/server/assets/favicon/android-chrome-192x192.png b/Web/WebSocket/websocket/apps/server/assets/favicon/android-chrome-192x192.png similarity index 100% rename from Web/WebSocket/websocket/server/assets/favicon/android-chrome-192x192.png rename to Web/WebSocket/websocket/apps/server/assets/favicon/android-chrome-192x192.png diff --git a/Web/WebSocket/websocket/server/assets/favicon/android-chrome-512x512.png b/Web/WebSocket/websocket/apps/server/assets/favicon/android-chrome-512x512.png similarity index 100% rename from Web/WebSocket/websocket/server/assets/favicon/android-chrome-512x512.png rename to Web/WebSocket/websocket/apps/server/assets/favicon/android-chrome-512x512.png diff --git a/Web/WebSocket/websocket/server/assets/favicon/apple-touch-icon.png b/Web/WebSocket/websocket/apps/server/assets/favicon/apple-touch-icon.png similarity index 100% rename from Web/WebSocket/websocket/server/assets/favicon/apple-touch-icon.png rename to Web/WebSocket/websocket/apps/server/assets/favicon/apple-touch-icon.png diff --git a/Web/WebSocket/websocket/server/assets/favicon/favicon-16x16.png b/Web/WebSocket/websocket/apps/server/assets/favicon/favicon-16x16.png similarity index 100% rename from Web/WebSocket/websocket/server/assets/favicon/favicon-16x16.png rename to Web/WebSocket/websocket/apps/server/assets/favicon/favicon-16x16.png diff --git a/Web/WebSocket/websocket/server/assets/favicon/favicon-32x32.png b/Web/WebSocket/websocket/apps/server/assets/favicon/favicon-32x32.png similarity index 100% rename from Web/WebSocket/websocket/server/assets/favicon/favicon-32x32.png rename to Web/WebSocket/websocket/apps/server/assets/favicon/favicon-32x32.png diff --git a/Web/WebSocket/websocket/server/assets/favicon/favicon.ico b/Web/WebSocket/websocket/apps/server/assets/favicon/favicon.ico similarity index 100% rename from Web/WebSocket/websocket/server/assets/favicon/favicon.ico rename to Web/WebSocket/websocket/apps/server/assets/favicon/favicon.ico diff --git a/Web/WebSocket/websocket/server/assets/favicon/site.webmanifest b/Web/WebSocket/websocket/apps/server/assets/favicon/site.webmanifest similarity index 100% rename from Web/WebSocket/websocket/server/assets/favicon/site.webmanifest rename to Web/WebSocket/websocket/apps/server/assets/favicon/site.webmanifest diff --git a/Web/WebSocket/websocket/server/eslint.config.ts b/Web/WebSocket/websocket/apps/server/eslint.config.ts similarity index 100% rename from Web/WebSocket/websocket/server/eslint.config.ts rename to Web/WebSocket/websocket/apps/server/eslint.config.ts diff --git a/Web/WebSocket/websocket/server/package.json b/Web/WebSocket/websocket/apps/server/package.json similarity index 93% rename from Web/WebSocket/websocket/server/package.json rename to Web/WebSocket/websocket/apps/server/package.json index a21ca16..f4420d4 100644 --- a/Web/WebSocket/websocket/server/package.json +++ b/Web/WebSocket/websocket/apps/server/package.json @@ -2,15 +2,17 @@ "name": "@techniker-me/websocket-server", "version": "0.0.0", "type": "module", + "private": true, "scripts": { - "preinstall": "npm prune", "format": "prettier --write .", "prelint": "npm install", "lint": "eslint --max-warnings 0 .", "prelint:fix": "npm run format", "lint:fix": "eslint --fix .", "dev": "tsx watch --clear-screen=false src/index.ts", - "typecheck": "tsc" + "build": "tsc", + "typecheck": "tsc", + "clean": "rm -rf dist" }, "author": "Alexander Zinn", "license": "ISC", @@ -44,6 +46,7 @@ "@techniker-me/tools": "2025.0.16", "body-parser": "2.2.0", "cors": "2.8.5", + "express": "5.1.0", "lru-cache": "11.2.2", "moment": "2.30.1", "morgan": "1.10.1", diff --git a/Web/WebSocket/websocket/server/src/health/HealthCheck.ts b/Web/WebSocket/websocket/apps/server/src/health/HealthCheck.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/health/HealthCheck.ts rename to Web/WebSocket/websocket/apps/server/src/health/HealthCheck.ts diff --git a/Web/WebSocket/websocket/server/src/health/HealthCheckRoute.ts b/Web/WebSocket/websocket/apps/server/src/health/HealthCheckRoute.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/health/HealthCheckRoute.ts rename to Web/WebSocket/websocket/apps/server/src/health/HealthCheckRoute.ts diff --git a/Web/WebSocket/websocket/server/src/health/IHealthCheck.ts b/Web/WebSocket/websocket/apps/server/src/health/IHealthCheck.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/health/IHealthCheck.ts rename to Web/WebSocket/websocket/apps/server/src/health/IHealthCheck.ts diff --git a/Web/WebSocket/websocket/server/src/index.ts b/Web/WebSocket/websocket/apps/server/src/index.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/index.ts rename to Web/WebSocket/websocket/apps/server/src/index.ts diff --git a/Web/WebSocket/websocket/server/src/lang/Assert.ts b/Web/WebSocket/websocket/apps/server/src/lang/Assert.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/lang/Assert.ts rename to Web/WebSocket/websocket/apps/server/src/lang/Assert.ts diff --git a/Web/WebSocket/websocket/server/src/lang/Strings.ts b/Web/WebSocket/websocket/apps/server/src/lang/Strings.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/lang/Strings.ts rename to Web/WebSocket/websocket/apps/server/src/lang/Strings.ts diff --git a/Web/WebSocket/websocket/server/src/net/http/HttpServer.ts b/Web/WebSocket/websocket/apps/server/src/net/http/HttpServer.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/net/http/HttpServer.ts rename to Web/WebSocket/websocket/apps/server/src/net/http/HttpServer.ts diff --git a/Web/WebSocket/websocket/server/src/net/http/IRoutes.ts b/Web/WebSocket/websocket/apps/server/src/net/http/IRoutes.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/net/http/IRoutes.ts rename to Web/WebSocket/websocket/apps/server/src/net/http/IRoutes.ts diff --git a/Web/WebSocket/websocket/server/src/net/websocket/WebSocketServer.ts b/Web/WebSocket/websocket/apps/server/src/net/websocket/WebSocketServer.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/net/websocket/WebSocketServer.ts rename to Web/WebSocket/websocket/apps/server/src/net/websocket/WebSocketServer.ts diff --git a/Web/WebSocket/websocket/server/src/types/Units.ts b/Web/WebSocket/websocket/apps/server/src/types/Units.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/types/Units.ts rename to Web/WebSocket/websocket/apps/server/src/types/Units.ts diff --git a/Web/WebSocket/websocket/server/src/types/modules.d.ts b/Web/WebSocket/websocket/apps/server/src/types/modules.d.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/types/modules.d.ts rename to Web/WebSocket/websocket/apps/server/src/types/modules.d.ts diff --git a/Web/WebSocket/websocket/server/src/types/optional.ts b/Web/WebSocket/websocket/apps/server/src/types/optional.ts similarity index 100% rename from Web/WebSocket/websocket/server/src/types/optional.ts rename to Web/WebSocket/websocket/apps/server/src/types/optional.ts diff --git a/Web/WebSocket/websocket/server/tsconfig.json b/Web/WebSocket/websocket/apps/server/tsconfig.json similarity index 67% rename from Web/WebSocket/websocket/server/tsconfig.json rename to Web/WebSocket/websocket/apps/server/tsconfig.json index 8e5e450..0d63722 100644 --- a/Web/WebSocket/websocket/server/tsconfig.json +++ b/Web/WebSocket/websocket/apps/server/tsconfig.json @@ -1,17 +1,13 @@ { + "extends": "../../tsconfig.json", "compilerOptions": { "target": "es2022", "lib": ["es2022"], "moduleResolution": "node", - "allowSyntheticDefaultImports": true, - "skipLibCheck": true, - "moduleDetection": "force", "module": "Preserve", "resolveJsonModule": true, "allowJs": false, - "esModuleInterop": true, "isolatedModules": true, - "strict": true, "noImplicitReturns": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, @@ -20,9 +16,15 @@ "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, "noUnusedParameters": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true + "noEmit": false, + "outDir": "./dist", + "rootDir": "./src", + "composite": true, + "declaration": true }, "include": ["src/**/*"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "dist"], + "references": [ + { "path": "../../packages/shared-types" } + ] } diff --git a/Web/WebSocket/websocket/package-lock.json b/Web/WebSocket/websocket/package-lock.json deleted file mode 100644 index 0f7f675..0000000 --- a/Web/WebSocket/websocket/package-lock.json +++ /dev/null @@ -1,1032 +0,0 @@ -{ - "name": "websocket", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "body-parser": "^2.2.0", - "compression": "^1.8.1", - "express": "^5.1.0" - }, - "devDependencies": { - "@types/compression": "^1.8.1", - "@types/express": "^5.0.3" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", - "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", - "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", - "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.12.0" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.1.0", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/compression/node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.7.0", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/undici-types": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", - "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - } - } -} diff --git a/Web/WebSocket/websocket/package.json b/Web/WebSocket/websocket/package.json index ce14af2..6faf409 100644 --- a/Web/WebSocket/websocket/package.json +++ b/Web/WebSocket/websocket/package.json @@ -1,11 +1,26 @@ { - "dependencies": { - "body-parser": "^2.2.0", - "compression": "^1.8.1", - "express": "^5.1.0" + "name": "@techniker-me/websocket-chat", + "version": "0.0.0", + "private": true, + "workspaces": [ + "apps/*", + "packages/*" + ], + "scripts": { + "build": "npm run build --workspaces", + "build:types": "npm run build -w @techniker-me/websocket-shared-types", + "dev": "npm run dev --workspaces --if-present", + "dev:server": "npm run dev -w @techniker-me/websocket-server", + "dev:frontend": "npm run dev -w @techniker-me/websocket-frontend", + "dev:all": "concurrently \"npm run dev:server\" \"npm run dev:frontend\"", + "clean": "npm run clean --workspaces --if-present", + "lint": "npm run lint --workspaces --if-present", + "typecheck": "npm run typecheck --workspaces --if-present", + "start": "npm run build:types && npm run dev:all" }, "devDependencies": { - "@types/compression": "^1.8.1", - "@types/express": "^5.0.3" - } + "typescript": "5.9.2", + "concurrently": "8.2.2" + }, + "author": "Alexander Zinn" } diff --git a/Web/WebSocket/websocket/packages/shared-types/package.json b/Web/WebSocket/websocket/packages/shared-types/package.json new file mode 100644 index 0000000..da8f94d --- /dev/null +++ b/Web/WebSocket/websocket/packages/shared-types/package.json @@ -0,0 +1,17 @@ +{ + "name": "@techniker-me/websocket-shared-types", + "version": "0.0.0", + "type": "module", + "private": true, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "build": "tsc", + "clean": "rm -rf dist", + "typecheck": "tsc" + }, + "devDependencies": { + "typescript": "~5.8.3" + }, + "dependencies": {} +} diff --git a/Web/WebSocket/websocket/packages/shared-types/src/index.ts b/Web/WebSocket/websocket/packages/shared-types/src/index.ts new file mode 100644 index 0000000..300addd --- /dev/null +++ b/Web/WebSocket/websocket/packages/shared-types/src/index.ts @@ -0,0 +1,41 @@ +// Shared types for WebSocket communication + +export type Milliseconds = number; +export type Seconds = number; + +export interface PingMessage { + ping: { + sentAt: number; + }; +} + +export interface PongMessage { + pong: { + sentAt: number; + rtt: number; + }; +} + +export interface ChatMessage { + message: { + author: string; + recipient: string; + payload: string; + }; +} + +export type WebSocketMessage = PingMessage | PongMessage | ChatMessage; + +export enum WebSocketConnectionStatus { + Connecting = 'Connecting', + Open = 'Open', + Closed = 'Closed', + Error = 'Error' +} + +export interface ExtendedWebSocket { + id: string; + remoteAddress: string; + isOpen(): boolean; + isClosed(): boolean; +} diff --git a/Web/WebSocket/websocket/packages/shared-types/tsconfig.json b/Web/WebSocket/websocket/packages/shared-types/tsconfig.json new file mode 100644 index 0000000..9b931e5 --- /dev/null +++ b/Web/WebSocket/websocket/packages/shared-types/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "allowJs": true, + "strict": true, + "noEmit": false, + "declaration": true, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/Web/WebSocket/websocket/tsconfig.json b/Web/WebSocket/websocket/tsconfig.json new file mode 100644 index 0000000..b051dfb --- /dev/null +++ b/Web/WebSocket/websocket/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "allowJs": true, + "strict": true, + "noEmit": true, + "skipLibCheck": true, + "composite": true, + "incremental": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo" + }, + "references": [ + { "path": "./packages/shared-types" }, + { "path": "./apps/server" }, + { "path": "./apps/frontend" } + ], + "files": [] +} diff --git a/Web/bfcache/.gitignore b/Web/bfcache/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/Web/bfcache/.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/Web/bfcache/frontend/bun.lock b/Web/bfcache/frontend/bun.lock new file mode 100644 index 0000000..a9d1759 --- /dev/null +++ b/Web/bfcache/frontend/bun.lock @@ -0,0 +1,133 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "bfcache", + "devDependencies": { + "typescript": "~5.8.3", + "vite": "^7.1.2", + }, + }, + }, + "packages": { + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.2", "", { "os": "android", "cpu": "arm" }, "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.50.2", "", { "os": "android", "cpu": "arm64" }, "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.50.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.50.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.50.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.50.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.50.2", "", { "os": "linux", "cpu": "arm" }, "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.50.2", "", { "os": "linux", "cpu": "arm" }, "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.50.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.50.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.50.2", "", { "os": "linux", "cpu": "none" }, "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.50.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.50.2", "", { "os": "linux", "cpu": "none" }, "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.50.2", "", { "os": "linux", "cpu": "none" }, "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.50.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.50.2", "", { "os": "linux", "cpu": "x64" }, "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.50.2", "", { "os": "linux", "cpu": "x64" }, "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.50.2", "", { "os": "none", "cpu": "arm64" }, "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.50.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.50.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.50.2", "", { "os": "win32", "cpu": "x64" }, "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "rollup": ["rollup@4.50.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.50.2", "@rollup/rollup-android-arm64": "4.50.2", "@rollup/rollup-darwin-arm64": "4.50.2", "@rollup/rollup-darwin-x64": "4.50.2", "@rollup/rollup-freebsd-arm64": "4.50.2", "@rollup/rollup-freebsd-x64": "4.50.2", "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", "@rollup/rollup-linux-arm-musleabihf": "4.50.2", "@rollup/rollup-linux-arm64-gnu": "4.50.2", "@rollup/rollup-linux-arm64-musl": "4.50.2", "@rollup/rollup-linux-loong64-gnu": "4.50.2", "@rollup/rollup-linux-ppc64-gnu": "4.50.2", "@rollup/rollup-linux-riscv64-gnu": "4.50.2", "@rollup/rollup-linux-riscv64-musl": "4.50.2", "@rollup/rollup-linux-s390x-gnu": "4.50.2", "@rollup/rollup-linux-x64-gnu": "4.50.2", "@rollup/rollup-linux-x64-musl": "4.50.2", "@rollup/rollup-openharmony-arm64": "4.50.2", "@rollup/rollup-win32-arm64-msvc": "4.50.2", "@rollup/rollup-win32-ia32-msvc": "4.50.2", "@rollup/rollup-win32-x64-msvc": "4.50.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "vite": ["vite@7.1.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ=="], + } +} diff --git a/Web/bfcache/frontend/index.html b/Web/bfcache/frontend/index.html new file mode 100644 index 0000000..7a8e629 --- /dev/null +++ b/Web/bfcache/frontend/index.html @@ -0,0 +1,48 @@ + + + + + + + bfcache + + +
+
+

bfcache

+
+
+
+
+
+

WebSocket status: Offline

+
+
+
+
+

Messages

+
    +
    +
    +
    +
    +

    Send Message

    + + +
    +
    +
    + +
    +
    + +
    + +
    + +
    +
    +
    + + + diff --git a/Web/WebSocket/websocket/frontend/package.json b/Web/bfcache/frontend/package.json similarity index 60% rename from Web/WebSocket/websocket/frontend/package.json rename to Web/bfcache/frontend/package.json index 4c2d420..85fcc08 100644 --- a/Web/WebSocket/websocket/frontend/package.json +++ b/Web/bfcache/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "websocket", + "name": "bfcache", "private": true, "version": "0.0.0", "type": "module", @@ -10,10 +10,6 @@ }, "devDependencies": { "typescript": "~5.8.3", - "vite": "^7.1.7" - }, - "dependencies": { - "@techniker-me/logger": "0.0.15", - "@techniker-me/tools": "2025.0.16" + "vite": "^7.1.2" } } diff --git a/Web/bfcache/frontend/src/main.ts b/Web/bfcache/frontend/src/main.ts new file mode 100644 index 0000000..e1bfe8e --- /dev/null +++ b/Web/bfcache/frontend/src/main.ts @@ -0,0 +1,84 @@ + + +const websocketStatus = document.getElementById('websocket-status') as HTMLSpanElement; +const messages = document.getElementById('messages') as HTMLUListElement; +const messageInput = document.getElementById('message-input') as HTMLInputElement; +const sendButton = document.getElementById('send-button') as HTMLButtonElement; +const loginDialog = document.getElementById('login-dialog') as HTMLDialogElement; +const usernameInput = document.getElementById('username-input') as HTMLInputElement; +const loginForm = document.getElementById('login-form') as HTMLFormElement; + +let username: string = ''; +let websocket: WebSocket; + +window.addEventListener('beforeunload', () => { + disconnect(websocket); +}); + +function connect(): Promise { + return new Promise((resolve, reject) => { + websocket = new WebSocket(`ws://${window.location.hostname}:4444`) + websocket.onopen = () => { + websocketStatus.textContent = 'Connected'; + + websocket.send(JSON.stringify({ + type: 'login', + username + })); + + resolve(websocket); + }; + + websocket.onerror = (event) => { + reject(event); + }; + + websocket.onclose = () => { + websocketStatus.textContent = 'Offline'; + }; + + websocket.onclose = () => { + websocketStatus.textContent = 'Offline'; + }; + + websocket.onerror = (event: Event) => { + websocketStatus.textContent = `Error [${event.toString()}]`; + }; + + websocket.onmessage = (event) => { + console.log('WebSocket message received:', event.data); + const message = document.createElement('li'); + message.textContent = event.data; + messages.appendChild(message); + }; + + sendButton.onclick = () => sendMessage(websocket, messageInput.value); + }); +} + +function disconnect(websocket: WebSocket) { + websocket.close(); +} + +function login() { + connect(); + loginDialog.close(); +} + +function sendMessage(websocket: WebSocket, message: string) { + if (!message) { + return; + } + + console.log('Sending message:', message); + websocket.send(message); + messageInput.value = ''; +} + +loginForm.onsubmit = (event) => { + event.preventDefault(); + loginDialog.close(); + login(usernameInput.value); +}; + +loginDialog.showModal(); \ No newline at end of file diff --git a/Web/bfcache/frontend/src/styles.css b/Web/bfcache/frontend/src/styles.css new file mode 100644 index 0000000..77b90ee --- /dev/null +++ b/Web/bfcache/frontend/src/styles.css @@ -0,0 +1,49 @@ +body { + background-color: #f0f0f0; + font-family: Arial, sans-serif; + margin: 0; + padding: 0; +} + +h3 { + text-align: center; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +.flex-column-container { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; +} + +.flex-row-container { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; +} + +.flex-item { + flex: 1; +} + +#login-button { + margin: 10px auto; + width: 100%; + padding: 10px 20px; + background-color: #007bff; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +} + +#login-button:hover { + background-color: #0056b3; +} \ No newline at end of file diff --git a/Web/bfcache/frontend/src/vite-env.d.ts b/Web/bfcache/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/Web/bfcache/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/Web/WebSocket/websocket/frontend/tsconfig.json b/Web/bfcache/frontend/tsconfig.json similarity index 90% rename from Web/WebSocket/websocket/frontend/tsconfig.json rename to Web/bfcache/frontend/tsconfig.json index 1a3882d..4f5edc2 100644 --- a/Web/WebSocket/websocket/frontend/tsconfig.json +++ b/Web/bfcache/frontend/tsconfig.json @@ -4,7 +4,6 @@ "useDefineForClassFields": true, "module": "ESNext", "lib": ["ES2022", "DOM", "DOM.Iterable"], - "types": ["vite/client"], "skipLibCheck": true, /* Bundler mode */ @@ -18,7 +17,7 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "erasableSyntaxOnly": false, + "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, diff --git a/Web/bfcache/server/.vscode/settings.json b/Web/bfcache/server/.vscode/settings.json new file mode 100644 index 0000000..7852c0d --- /dev/null +++ b/Web/bfcache/server/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "idf.pythonInstallPath": "/opt/homebrew/bin/python3" +} \ No newline at end of file diff --git a/Web/bfcache/server/src/index.ts b/Web/bfcache/server/src/index.ts index 68e55dd..3cf7487 100644 --- a/Web/bfcache/server/src/index.ts +++ b/Web/bfcache/server/src/index.ts @@ -1,5 +1,6 @@ import {LoggerFactory} from '@techniker-me/logger'; import WebSocketServerFactory from './net/websockets/WebSocketServerFactory'; +import type { WebSocketHandler } from 'bun'; LoggerFactory.setLoggingLevel('All'); @@ -15,7 +16,7 @@ const server = Bun.serve({ return new Response('hi'); }, - websocket: webSocketRelayServer + websocket: webSocketRelayServer as WebSocketHandler }); process.on('SIGINT', () => { diff --git a/Web/bfcache/server/src/net/websockets/WebSocketServer.ts b/Web/bfcache/server/src/net/websockets/WebSocketServer.ts index 2467591..6fb29cc 100644 --- a/Web/bfcache/server/src/net/websockets/WebSocketServer.ts +++ b/Web/bfcache/server/src/net/websockets/WebSocketServer.ts @@ -95,4 +95,102 @@ export default class WebSocketServer { get perMessageDeflate(): PerMessageDeflate { return this._perMessageDeflate; } + + public start(connectDelegate: (req, websocket, headers) => void, requestDelegate: (req, websocket, headers) => void, disconnectDelegate: (req, websocket, headers) => void, pongDelegate: (req, websocket, headers) => void) { + if (this._server) { + throw new Error('Can only start server once'); + } + + assert.assertFunction('connectDelegate', connectDelegate); + assert.assertFunction('requestDelegate', requestDelegate); + assert.assertFunction('disconnectDelegate', disconnectDelegate); + assert.assertFunction('pongDelegate', pongDelegate); + + const serverOptions = _.cloneDeep(this._parameters); + + log.info('Websocket server listening on port [%s] bound to [%s]', this._httpServer.address().port, _.get(serverOptions, ['path'])); + + serverOptions.noServer = true; + + this._server = new ws.Server(serverOptions); + this._extensions = new WebsocketExtensions(); + this._extensions.add(deflate); + + // TODO Prevent upgrade request from being handled by this WS server if it's not within the configured path + this._server._server = this._httpServer; + this._httpServer.on('listening', this._server.emit.bind(this._server, 'listening')); + this._httpServer.on('error', this._server.emit.bind(this._server, 'error')); + this._httpServer.on('upgrade', (req, socket, head) => { + if (!_.startsWith(req.url, serverOptions.path)) { + return; + } + + this._server.handleUpgrade(req, socket, head, ws => { + this._server.emit('connection', ws, req); + }); + }); + + this._server.on('error', e => { + log.error('An error occurred on websocket', e); + }); + + this._server.on('connection', (connection, req) => { + let closed = false; + + try { + connection.id = randomstring.generate(connectionIdLength); + connection.remoteAddress = getRemoteAddress.call(this, connection, req); + + log.debug('[%s] connected from [%s] with headers [%j]', connection.id, connection.remoteAddress, req.headers); + + connection.isOpen = () => connection.readyState === ws.OPEN; + + connection.isClosed = () => connection.readyState === ws.CLOSED; + + connection.on('error', e => { + log.error('An error occurred on websocket', e); + }); + + connection.on('message', message => { + try { + requestDelegate(connection, message); + } catch (e) { + log.error('Request handler failed for message [%s]', message, e); + } + }); + + connection.on('close', (reasonCode, description) => { + if (closed) { + log.warn('[%s] Multiple close events [%s] [%s] [%s]', connection.id, connection.remoteAddress, reasonCode, description); + + return; + } + + closed = true; + + try { + disconnectDelegate(connection, reasonCode, description); + } catch (e) { + log.error('Disconnect handler failed', e); + } + }); + + connection.on('pong', message => { + try { + pongDelegate(connection, message); + } catch (e) { + log.error('Pong handler failed', e); + } + }); + + return connectDelegate(connection, _.get(req, ['headers'])); + } catch (e) { + log.error('Accept/connect handler failed', e); + } + } + ); + + return this; + } +} } diff --git a/Web/bfcache/server/src/net/websockets/WebSocketServerFactory.ts b/Web/bfcache/server/src/net/websockets/WebSocketServerFactory.ts index 3b45d06..7fb902c 100644 --- a/Web/bfcache/server/src/net/websockets/WebSocketServerFactory.ts +++ b/Web/bfcache/server/src/net/websockets/WebSocketServerFactory.ts @@ -9,11 +9,13 @@ export default class WebSocketServerFactory { const webSocketRelayServerOptions: WebSocketServerOptions = { onSocketError: (client, error) => logger.error(`Error: [%o] [${error.message}]`, client), onSocketOpen: client => { + console.log('New WebSocketClient [%o]', client); logger.debug('New WebSocketClient [%o]', client); clients.add(client); }, onSocketMessage: (fromClient, message) => { + console.log('Relaying message [%o]', message); logger.debug(`Relaying message [%o]`, message); for (const client of clients) { @@ -24,7 +26,10 @@ export default class WebSocketServerFactory { client.send(message); } }, - onSocketClose: client => clients.delete(client), + onSocketClose: client => { + console.log('Client closed [%o]', client); + clients.delete(client); + }, onSocketDrain: client => logger.debug('Client drain [%o]', client), publishToSelf: false };