commit ffbbb3b3f98bdcf54f5fe285c89a5d18dc6b549f Author: Feror Date: Thu May 22 17:29:22 2025 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/.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/README.md b/README.md new file mode 100644 index 0000000..6fd7631 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# test-react-location + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.2.14. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..30c0450 --- /dev/null +++ b/bun.lock @@ -0,0 +1,25 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "test-react-location", + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.2.14", "", { "dependencies": { "bun-types": "1.2.14" } }, "sha512-VsFZKs8oKHzI7zwvECiAJ5oSorWndIWEVhfbYqZd4HI/45kzW7PN2Rr5biAzvGvRuNmYLSANY+H59ubHq8xw7Q=="], + + "@types/node": ["@types/node@22.15.21", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ=="], + + "bun-types": ["bun-types@1.2.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-Kuh4Ub28ucMRWeiUUWMHsT9Wcbr4H3kLIO72RZZElSDxSu7vpetRvxIUDUaW6QtaIeixIpm7OXtNnZPf82EzwA=="], + + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + } +} diff --git a/front/index.html b/front/index.html new file mode 100644 index 0000000..e408d1b --- /dev/null +++ b/front/index.html @@ -0,0 +1,16 @@ + + + + + + + Document + + + + +

Current position

+

x:
y:

+ + + \ No newline at end of file diff --git a/front/script.ts b/front/script.ts new file mode 100644 index 0000000..ad59168 --- /dev/null +++ b/front/script.ts @@ -0,0 +1,21 @@ +const positionP = document.getElementById("position")!; + +const ws = new WebSocket(`ws://${location.host}/ws/broadcast`); + +setInterval(() => { + navigator.geolocation.getCurrentPosition( + (position) => { + ws.send( + JSON.stringify({ + x: position.coords.latitude, + y: position.coords.longitude, + z: position.coords.altitude, + }) + ); + positionP.innerHTML = `x: ${position.coords.latitude}
y: ${position.coords.longitude}`; + }, + (error) => { + positionP.innerText = JSON.stringify(error); + } + ); +}, 1000); diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..1d7125d --- /dev/null +++ b/index.ts @@ -0,0 +1,72 @@ +import { serve, type ServerWebSocket, type WebSocketHandler } from "bun"; +import front from "./front/index.html"; + +const subscribers = [] as ServerWebSocket<{ uuid?: string; role: "broadcast" | "subscribe" }>[]; + +const server = serve({ + routes: { + "/": front, + "/ws/broadcast": (req, server) => { + const uuid = crypto.randomUUID(); + if ( + server.upgrade(req, { + data: { uuid, role: "broadcast" }, + }) + ) { + return; + } + + return new Response("Upgrade required", { status: 426 }); + }, + "/ws/subscribe": (req, server) => { + const uuid = crypto.randomUUID(); + if ( + server.upgrade(req, { + data: { uuid, role: "subscribe" }, + }) + ) { + return; + } + + return new Response("Upgrade required", { status: 426 }); + }, + }, + development: { + console: true, + }, + websocket: { + open(ws) { + if (ws.data.role === "subscribe") { + subscribers.push(ws); + } else if (ws.data.role === "broadcast") { + ws.send("Connected to broadcast"); + } + }, + message(ws, message) { + console.log("Received message", message.toString()); + if (ws.data.role === "broadcast") { + const uuid = ws.data.uuid; + const coords = JSON.parse(message.toString()); + const messageToSend = JSON.stringify({ + geometry: { + ...coords, + spatialReference: { + wkid: 4326, + }, + }, + attributes: { + uuid, + }, + }); + subscribers.forEach((subscriber) => { + subscriber.send(messageToSend); + }); + } else if (ws.data.role === "subscribe") { + ws.send(`Received message: ${message}`); + } + }, + close(ws) {}, + } as WebSocketHandler<{ uuid?: string; role: "broadcast" | "subscribe" }>, +}); + +console.log(`Server running at ${server.url}`); diff --git a/package.json b/package.json new file mode 100644 index 0000000..220d97e --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "test-react-location", + "module": "index.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..be3d138 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "Preserve", + "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 + } +}