harsh-dev commited on
Commit
a7476ec
·
unverified ·
1 Parent(s): 42d3e61

Add application file

Browse files
.dockerignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ node_modules
2
+ dist
3
+ .git
4
+ .gitignore
5
+ Dockerfile
6
+ .dockerignore
7
+ CODE_REVIEW_REPORT.md
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ node_modules
2
+ .env
.prettierignore ADDED
@@ -0,0 +1 @@
 
 
1
+ node_modules
.prettierrc ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "singleQuote": true,
3
+ "semi": false,
4
+ "tabWidth": 4,
5
+ "trailingComma": "es5"
6
+ }
CODE_REVIEW_REPORT.md ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Code Review & Refactoring Report
2
+
3
+ ## Overview
4
+
5
+ This report analyzes the current codebase (`sockets` project) and provides actionable feedback to improve maintainability, performance, and structure. The project uses **Express** and **ws** for a simple WebSocket server with room management.
6
+
7
+ ---
8
+
9
+ ## 1. Architectural Improvements
10
+
11
+ ### Issue: specialized "Service" instantiated per message
12
+
13
+ Currently, a new `MessageService` instance is created for every single incoming message.
14
+
15
+ ```typescript
16
+ // src/index.ts
17
+ new MessageService({ msg: msg.toString(), ... })
18
+ ```
19
+
20
+ This is inefficient and semantically incorrect for a "Service". A Service should ideally be a singleton or a static utility that handles logic, rather than an ephemeral object representing a single request.
21
+
22
+ **Recommendation:**
23
+ Refactor `MessageService` to contain static methods or be a singleton instance that accepts the message and socket as arguments to a `handleMessage` method.
24
+
25
+ ### Issue: Tight Coupling and Logic in Constructor
26
+
27
+ Critical business logic (parsing, routing, executing side effects) happens inside the `constructor` of `MessageService`.
28
+
29
+ ```typescript
30
+ // src/services/MessageService.ts
31
+ constructor(...) {
32
+ // ... logic ...
33
+ if(action === 'join') ...
34
+ }
35
+ ```
36
+
37
+ Constructors should primarily initialize state. Side effects (like broadcasting messages) should happen in explicit methods.
38
+
39
+ **Recommendation:**
40
+ Move logic out of the constructor. Create a `handleIncomingMessage(data: string, socket: WebSocket)` method.
41
+
42
+ ---
43
+
44
+ ## 2. Robustness and Error Handling
45
+
46
+ ### Issue: Unsafe `JSON.parse`
47
+
48
+ If a client sends an invalid JSON string, `JSON.parse(msg)` inside `MessageService` will throw an error. Since this is not wrapped in a `try-catch` block, it could crash the application or unexpectedly terminate the socket connection.
49
+
50
+ **Recommendation:**
51
+ Wrap the parsing logic in a `try-catch` block. If parsing fails, send an error response to the sender and abort processing.
52
+
53
+ ### Issue: Missing Input Validation
54
+
55
+ The code assumes `room_id`, `username`, and `action` always exist in the parsed payload.
56
+
57
+ ```typescript
58
+ this.room_id = layers['room_id'] // Could be undefined
59
+ ```
60
+
61
+ Accessing properties on undefined or unexpected data types can lead to runtime errors.
62
+
63
+ **Recommendation:**
64
+ Use a schema validation library (like **Zod**) or manual checks to ensure incoming data matches the expected structure (Interface) before processing.
65
+
66
+ ---
67
+
68
+ ## 3. Type Safety and TypeScript Best Practices
69
+
70
+ ### Issue: `any` types implicitly
71
+
72
+ `JSON.parse` returns `any`. You should define an interface for your message structure.
73
+
74
+ ```typescript
75
+ interface WebSocketMessage {
76
+ room_id: string
77
+ username: string
78
+ message: string
79
+ action: 'join' | 'leave' | 'message'
80
+ }
81
+ ```
82
+
83
+ ### Issue: Encapsulation
84
+
85
+ In `RoomServices`, the `map` property is public.
86
+
87
+ ```typescript
88
+ map: Map<string, Map<string, WebSocket>>
89
+ ```
90
+
91
+ This allows external code to modify the state directly, bypassing helper methods like `leaveRoom`.
92
+
93
+ **Recommendation:**
94
+ Make `map` private: `private map: Map<...>`.
95
+
96
+ ---
97
+
98
+ ## 4. Code Cleanliness & Manageability
99
+
100
+ ### Magic Strings
101
+
102
+ Strings like `'join'` and `'leave'` are hardcoded in multiple places.
103
+ **Recommendation:** use an `enum` or constants for Action types.
104
+
105
+ ```typescript
106
+ enum SocketAction {
107
+ JOIN = 'join',
108
+ LEAVE = 'leave',
109
+ MESSAGE = 'message',
110
+ }
111
+ ```
112
+
113
+ ### Duplicate Logic
114
+
115
+ In `MessageService.broadcast`, the check for room existence is repeated redundant calls.
116
+
117
+ ---
118
+
119
+ ## Proposed Refactored Structure
120
+
121
+ ### `src/types.ts`
122
+
123
+ Define shared interfaces and enums here.
124
+
125
+ ### `src/services/RoomManager.ts` (Renamed from RoomServices)
126
+
127
+ Keep it focused on state management (add, remove, get users). Ensure data is private.
128
+
129
+ ### `src/handlers/MessageHandler.ts` (Renamed from MessageService)
130
+
131
+ A stateless function or class that:
132
+
133
+ 1. Parses the message (safely).
134
+ 2. Validates the schema.
135
+ 3. Calls the appropriate action on `RoomServices`.
136
+
137
+ ### `src/index.ts`
138
+
139
+ Initialize the `App` and pass the singletons/handlers to the WebSocket event listener.
140
+
141
+ ---
142
+
143
+ ## Summary of Next Steps
144
+
145
+ 1. **Safety**: Add `try-catch` around JSON parsing.
146
+ 2. **Organization**: Separate "Data Types" from "Logic".
147
+ 3. **Pattern**: Stop `new MessageService(...)` pattern. Use `MessageController.handle(...)`.
148
+ 4. **Resilience**: Validate all inputs before using them.
DO_NOT_TOUCH_THIS/index.ts ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import http from 'http'
2
+ import express from 'express'
3
+ import { WebSocketServer } from 'ws'
4
+ import RoomServices from './services/RoomServices.js'
5
+ import MessageService from './services/MessageService.js'
6
+
7
+ const rooms = new RoomServices()
8
+ type wsData = Buffer | ArrayBuffer | Buffer[] | string
9
+
10
+ const app = express()
11
+ const PORT = 3000
12
+ app.use(express.json())
13
+
14
+ app.get('/', (req, res) => {
15
+ res.send('Hello from Express + WebSocket server!')
16
+ })
17
+
18
+ const server = http.createServer(app)
19
+ const wss = new WebSocketServer({ server })
20
+
21
+ wss.on('connection', (ws) => {
22
+ console.log('connected to user')
23
+
24
+ ws.on('message', (msg: wsData) => {
25
+ // console.log('client send this: ', msg.toString())
26
+ // ws.send(`you sent this? : ${msg.toString()}`)
27
+ new MessageService({
28
+ msg: msg.toString(),
29
+ rooms: rooms,
30
+ con: ws,
31
+ })
32
+ })
33
+
34
+ ws.on('close', () => {
35
+ console.log('connection closed!')
36
+ })
37
+ })
38
+
39
+ server.listen(PORT, () => {
40
+ console.log(`Server running on http://localhost:${PORT}`)
41
+ })
DO_NOT_TOUCH_THIS/services/MessageService.ts ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import RoomServices from './RoomServices.js'
2
+ import WebSocket from 'ws'
3
+
4
+ class MessageService {
5
+ username: string
6
+ message: string
7
+ room_id: string
8
+ action: string
9
+ rooms: RoomServices
10
+ con: WebSocket
11
+
12
+ constructor({
13
+ msg,
14
+ rooms,
15
+ con,
16
+ }: {
17
+ msg: string
18
+ rooms: RoomServices
19
+ con: WebSocket
20
+ }) {
21
+ const layers = JSON.parse(msg)
22
+ this.room_id = layers['room_id']
23
+ this.username = layers['username']
24
+ this.message = layers['message']
25
+ this.action = layers['action']
26
+ this.rooms = rooms
27
+ this.con = con
28
+
29
+ if (this.action.toLowerCase() === 'join') {
30
+ this.join()
31
+ } else if (this.action.toLowerCase() === 'leave') {
32
+ this.leave()
33
+ } else {
34
+ this.broadcast()
35
+ }
36
+ }
37
+
38
+ join() {
39
+ this.rooms.putRoom(this.room_id, this.username, this.con)
40
+ this.broadcast()
41
+ }
42
+
43
+ leave() {
44
+ this.rooms.leaveRoom(this.room_id, this.username)
45
+ this.broadcast()
46
+ }
47
+
48
+ broadcast() {
49
+ if (!this.rooms.hasRoom(this.room_id)) return
50
+ if (!this.rooms.getRoom(this.room_id)) return
51
+ let arr_rooms = this.rooms.getRoom(this.room_id)
52
+ if (!arr_rooms) return
53
+ for (const [key, value] of arr_rooms) {
54
+ if (this.action.toLowerCase() === 'join') {
55
+ value.send(
56
+ `${this.username} has joined the ${this.room_id} chat!`
57
+ )
58
+ } else if (this.action.toLowerCase() === 'leave') {
59
+ value.send(
60
+ `${this.username} has left the ${this.room_id} chat!`
61
+ )
62
+ } else {
63
+ value.send(`${this.username}: ${this.message}`)
64
+ }
65
+ }
66
+ }
67
+
68
+ broadcast_message(msg: string) {
69
+ if (!this.rooms.hasRoom(this.room_id)) return
70
+ if (!this.rooms.getRoom(this.room_id)) return
71
+ let arr_rooms = this.rooms.getRoom(this.room_id)
72
+ if (!arr_rooms) return
73
+ for (const [key, value] of arr_rooms) {
74
+ value.send(`${this.username}: ${msg}`)
75
+ }
76
+ }
77
+ }
78
+
79
+ export default MessageService
DO_NOT_TOUCH_THIS/services/RoomServices.ts ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WebSocket from 'ws'
2
+
3
+ class RoomServices {
4
+ map: Map<string, Map<string, WebSocket>>
5
+ //instead of WebSocket[] use Map<string, WebSocket> mapped to username to its websocket, so you cna make him leave whenever needed!
6
+ constructor() {
7
+ this.map = new Map()
8
+ }
9
+
10
+ hasRoom(roomId: string): boolean {
11
+ return this.map.has(roomId)
12
+ }
13
+
14
+ getRoom(roomId: string) {
15
+ return this.map.get(roomId)
16
+ }
17
+
18
+ leaveRoom(roomId: string, username: string): boolean {
19
+ return this.getRoom(roomId)?.delete(username) === true
20
+ }
21
+
22
+ putRoom(roomId: string, username: string, ws: WebSocket) {
23
+ if (!this.hasRoom(roomId))
24
+ this.map.set(roomId, new Map<string, WebSocket>())
25
+ this.map.get(roomId)?.set(username, ws)
26
+ }
27
+
28
+ getConnection(room_id: string, username: string) {
29
+ return this.map.get(room_id)?.get(username)
30
+ }
31
+ }
32
+
33
+ export default RoomServices
Dockerfile ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage 1: Build
2
+ FROM node:20-alpine AS builder
3
+
4
+ WORKDIR /app
5
+
6
+ COPY package*.json ./
7
+ # Install all dependencies including devDependencies for building
8
+ RUN npm install
9
+
10
+ COPY . .
11
+
12
+ # Build the TypeScript code
13
+ RUN npm run build
14
+
15
+ # Stage 2: Production
16
+ FROM node:20-alpine
17
+
18
+ WORKDIR /app
19
+
20
+ COPY package*.json ./
21
+
22
+ # Install only production dependencies
23
+ RUN npm ci --only=production
24
+
25
+ # Copy built assets from builder stage
26
+ COPY --from=builder /app/dist ./dist
27
+
28
+ # Expose the application port
29
+ EXPOSE 7860
30
+
31
+ # Start the server
32
+ CMD ["npm", "start"]
dist/handlers/MessageHandler.d.ts ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import WebSocket from 'ws';
2
+ import { RoomManager } from '../services/RoomManager.js';
3
+ export declare class MessageHandler {
4
+ static handle(rawMessage: string, socket: WebSocket, roomManager: RoomManager): void;
5
+ }
6
+ //# sourceMappingURL=MessageHandler.d.ts.map
dist/handlers/MessageHandler.d.ts.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/handlers/MessageHandler.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAGxD,qBAAa,cAAc;IACvB,MAAM,CAAC,MAAM,CACT,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,WAAW,GACzB,IAAI;CAgFV"}
dist/handlers/MessageHandler.js ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WebSocket from 'ws';
2
+ import { RoomManager } from '../services/RoomManager.js';
3
+ import { SocketAction } from '../types.js';
4
+ export class MessageHandler {
5
+ static handle(rawMessage, socket, roomManager) {
6
+ let parsedData;
7
+ try {
8
+ parsedData = JSON.parse(rawMessage);
9
+ }
10
+ catch (error) {
11
+ console.error('Error parsing JSON:', error);
12
+ socket.send(JSON.stringify({ error: 'Invalid JSON', status: 0 }));
13
+ return;
14
+ }
15
+ // Basic validation to ensure required fields exist
16
+ if (!parsedData.room_id || !parsedData.username || !parsedData.action) {
17
+ socket.send(JSON.stringify({
18
+ error: 'Missing fields: room_id, username, or action',
19
+ status: 0,
20
+ }));
21
+ return;
22
+ }
23
+ const { room_id, username, message } = parsedData;
24
+ // Normalize action to ensure case-insensitivity matches logic
25
+ const action = parsedData.action.toLowerCase();
26
+ switch (action) {
27
+ case SocketAction.JOIN:
28
+ roomManager.joinRoom(room_id, username, socket);
29
+ roomManager.broadcast(room_id, JSON.stringify({
30
+ message: `${username} has joined the ${room_id} chat!`,
31
+ username: `${username}`,
32
+ room_id: `${room_id}`,
33
+ action: `join`,
34
+ status: 1,
35
+ }));
36
+ break;
37
+ case SocketAction.LEAVE:
38
+ const clientLeft = roomManager.leaveRoom(room_id, username);
39
+ if (clientLeft) {
40
+ roomManager.broadcast(room_id, JSON.stringify({
41
+ message: `${username} has left the ${room_id} chat!`,
42
+ username: `${username}`,
43
+ room_id: `${room_id}`,
44
+ action: `leave`,
45
+ status: 1,
46
+ }));
47
+ }
48
+ break;
49
+ case SocketAction.MESSAGE:
50
+ roomManager.broadcast(room_id, JSON.stringify({
51
+ message: `${message}`,
52
+ username: `${username}`,
53
+ room_id: `${room_id}`,
54
+ action: `message`,
55
+ status: 1,
56
+ }));
57
+ break;
58
+ default:
59
+ socket.send(JSON.stringify({
60
+ error: `Unknown action: ${action}`,
61
+ status: 0,
62
+ }));
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ //# sourceMappingURL=MessageHandler.js.map
dist/handlers/MessageHandler.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"MessageHandler.js","sourceRoot":"","sources":["../../src/handlers/MessageHandler.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,MAAM,OAAO,cAAc;IACvB,MAAM,CAAC,MAAM,CACT,UAAkB,EAClB,MAAiB,EACjB,WAAwB;QAExB,IAAI,UAAe,CAAA;QAEnB,IAAI,CAAC;YACD,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAA;YAC3C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;YACjE,OAAM;QACV,CAAC;QAED,mDAAmD;QACnD,IAAI,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YACpE,MAAM,CAAC,IAAI,CACP,IAAI,CAAC,SAAS,CAAC;gBACX,KAAK,EAAE,8CAA8C;gBACrD,MAAM,EAAE,CAAC;aACZ,CAAC,CACL,CAAA;YACD,OAAM;QACV,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,UAAU,CAAA;QACjD,8DAA8D;QAC9D,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;QAE9C,QAAQ,MAAM,EAAE,CAAC;YACb,KAAK,YAAY,CAAC,IAAI;gBAClB,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;gBAC/C,WAAW,CAAC,SAAS,CACjB,OAAO,EACP,IAAI,CAAC,SAAS,CAAC;oBACX,OAAO,EAAE,GAAG,QAAQ,mBAAmB,OAAO,QAAQ;oBACtD,QAAQ,EAAE,GAAG,QAAQ,EAAE;oBACvB,OAAO,EAAE,GAAG,OAAO,EAAE;oBACrB,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,CAAC;iBACZ,CAAC,CACL,CAAA;gBACD,MAAK;YAET,KAAK,YAAY,CAAC,KAAK;gBACnB,MAAM,UAAU,GAAG,WAAW,CAAC,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;gBAC3D,IAAI,UAAU,EAAE,CAAC;oBACb,WAAW,CAAC,SAAS,CACjB,OAAO,EACP,IAAI,CAAC,SAAS,CAAC;wBACX,OAAO,EAAE,GAAG,QAAQ,iBAAiB,OAAO,QAAQ;wBACpD,QAAQ,EAAE,GAAG,QAAQ,EAAE;wBACvB,OAAO,EAAE,GAAG,OAAO,EAAE;wBACrB,MAAM,EAAE,OAAO;wBACf,MAAM,EAAE,CAAC;qBACZ,CAAC,CACL,CAAA;gBACL,CAAC;gBACD,MAAK;YAET,KAAK,YAAY,CAAC,OAAO;gBACrB,WAAW,CAAC,SAAS,CACjB,OAAO,EACP,IAAI,CAAC,SAAS,CAAC;oBACX,OAAO,EAAE,GAAG,OAAO,EAAE;oBACrB,QAAQ,EAAE,GAAG,QAAQ,EAAE;oBACvB,OAAO,EAAE,GAAG,OAAO,EAAE;oBACrB,MAAM,EAAE,SAAS;oBACjB,MAAM,EAAE,CAAC;iBACZ,CAAC,CACL,CAAA;gBACD,MAAK;YAET;gBACI,MAAM,CAAC,IAAI,CACP,IAAI,CAAC,SAAS,CAAC;oBACX,KAAK,EAAE,mBAAmB,MAAM,EAAE;oBAClC,MAAM,EAAE,CAAC;iBACZ,CAAC,CACL,CAAA;gBACD,MAAK;QACb,CAAC;IACL,CAAC;CACJ"}
dist/index.d.ts ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
dist/index.d.ts.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
dist/index.js ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import http from 'http';
2
+ import { WebSocketServer } from 'ws';
3
+ import { RoomManager } from './services/RoomManager.js';
4
+ import { MessageHandler } from './handlers/MessageHandler.js';
5
+ const roomManager = new RoomManager();
6
+ const PORT = 3001;
7
+ const server = http.createServer((req, res) => {
8
+ // Handle simple GET /
9
+ if (req.method === 'GET' && req.url === '/') {
10
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
11
+ res.end('Hello from WebSocket server without Express!');
12
+ }
13
+ else {
14
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
15
+ res.end('Not Found');
16
+ }
17
+ });
18
+ const wss = new WebSocketServer({ server });
19
+ wss.on('connection', (ws) => {
20
+ console.log('connected to user');
21
+ ws.on('message', (msg) => {
22
+ MessageHandler.handle(msg.toString(), ws, roomManager);
23
+ });
24
+ ws.on('close', () => {
25
+ console.log('connection closed!');
26
+ });
27
+ });
28
+ server.listen(PORT, () => {
29
+ console.log(`Server running on http://localhost:${PORT}`);
30
+ });
31
+ //# sourceMappingURL=index.js.map
dist/index.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAA;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAE7D,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAA;AAGrC,MAAM,IAAI,GAAG,IAAI,CAAA;AAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAC1C,sBAAsB;IACtB,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,GAAG,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;QAC1C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAA;QACpD,GAAG,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAA;IAC3D,CAAC;SAAM,CAAC;QACJ,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAA;QACpD,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACxB,CAAC;AACL,CAAC,CAAC,CAAA;AAEF,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;AAE3C,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE;IACxB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;IAEhC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAW,EAAE,EAAE;QAC7B,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,WAAW,CAAC,CAAA;IAC1D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAChB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;IACrC,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACrB,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAA;AAC7D,CAAC,CAAC,CAAA"}
dist/services/MessageService.d.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import RoomServices from './RoomServices.js';
2
+ import WebSocket from 'ws';
3
+ declare class MessageService {
4
+ username: string;
5
+ message: string;
6
+ room_id: string;
7
+ action: string;
8
+ rooms: RoomServices;
9
+ con: WebSocket;
10
+ constructor({ msg, rooms, con, }: {
11
+ msg: string;
12
+ rooms: RoomServices;
13
+ con: WebSocket;
14
+ });
15
+ join(): void;
16
+ leave(): void;
17
+ broadcast(): void;
18
+ broadcast_message(msg: string): void;
19
+ }
20
+ export default MessageService;
21
+ //# sourceMappingURL=MessageService.d.ts.map
dist/services/MessageService.d.ts.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"MessageService.d.ts","sourceRoot":"","sources":["../../src/services/MessageService.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,mBAAmB,CAAA;AAC5C,OAAO,SAAS,MAAM,IAAI,CAAA;AAE1B,cAAM,cAAc;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,YAAY,CAAA;IACnB,GAAG,EAAE,SAAS,CAAA;gBAEF,EACR,GAAG,EACH,KAAK,EACL,GAAG,GACN,EAAE;QACC,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,YAAY,CAAA;QACnB,GAAG,EAAE,SAAS,CAAA;KACjB;IAoBD,IAAI;IAKJ,KAAK;IAKL,SAAS;IAoBT,iBAAiB,CAAC,GAAG,EAAE,MAAM;CAShC;AAED,eAAe,cAAc,CAAA"}
dist/services/MessageService.js ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import RoomServices from './RoomServices.js';
2
+ import WebSocket from 'ws';
3
+ class MessageService {
4
+ username;
5
+ message;
6
+ room_id;
7
+ action;
8
+ rooms;
9
+ con;
10
+ constructor({ msg, rooms, con, }) {
11
+ const layers = JSON.parse(msg);
12
+ this.room_id = layers['room_id'];
13
+ this.username = layers['username'];
14
+ this.message = layers['message'];
15
+ this.action = layers['action'];
16
+ this.rooms = rooms;
17
+ this.con = con;
18
+ if (this.action.toLowerCase() === 'join') {
19
+ this.join();
20
+ }
21
+ else if (this.action.toLowerCase() === 'leave') {
22
+ this.leave();
23
+ // } else if (this.action.toLocaleLowerCase() === 'switch') {
24
+ // this.switchRooms()
25
+ }
26
+ else {
27
+ this.broadcast();
28
+ }
29
+ }
30
+ join() {
31
+ this.rooms.putRoom(this.room_id, this.username, this.con);
32
+ this.broadcast();
33
+ }
34
+ leave() {
35
+ this.rooms.leaveRoom(this.room_id, this.username);
36
+ this.broadcast();
37
+ }
38
+ broadcast() {
39
+ if (!this.rooms.hasRoom(this.room_id))
40
+ return;
41
+ if (!this.rooms.getRoom(this.room_id))
42
+ return;
43
+ let arr_rooms = this.rooms.getRoom(this.room_id);
44
+ if (!arr_rooms)
45
+ return;
46
+ for (const [key, value] of arr_rooms) {
47
+ if (this.action.toLowerCase() === 'join') {
48
+ value.send(`${this.username} has joined the ${this.room_id} chat!`);
49
+ }
50
+ else if (this.action.toLowerCase() === 'leave') {
51
+ value.send(`${this.username} has left the ${this.room_id} chat!`);
52
+ }
53
+ else {
54
+ value.send(`${this.username}: ${this.message}`);
55
+ }
56
+ }
57
+ }
58
+ broadcast_message(msg) {
59
+ if (!this.rooms.hasRoom(this.room_id))
60
+ return;
61
+ if (!this.rooms.getRoom(this.room_id))
62
+ return;
63
+ let arr_rooms = this.rooms.getRoom(this.room_id);
64
+ if (!arr_rooms)
65
+ return;
66
+ for (const [key, value] of arr_rooms) {
67
+ value.send(`${this.username}: ${msg}`);
68
+ }
69
+ }
70
+ }
71
+ export default MessageService;
72
+ //# sourceMappingURL=MessageService.js.map
dist/services/MessageService.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"MessageService.js","sourceRoot":"","sources":["../../src/services/MessageService.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,mBAAmB,CAAA;AAC5C,OAAO,SAAS,MAAM,IAAI,CAAA;AAE1B,MAAM,cAAc;IAChB,QAAQ,CAAQ;IAChB,OAAO,CAAQ;IACf,OAAO,CAAQ;IACf,MAAM,CAAQ;IACd,KAAK,CAAc;IACnB,GAAG,CAAW;IAEd,YAAY,EACR,GAAG,EACH,KAAK,EACL,GAAG,GAKN;QACG,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;QAChC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,CAAA;QAClC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;QAC9B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QAEd,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;YACvC,IAAI,CAAC,IAAI,EAAE,CAAA;QACf,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE,CAAC;YAC/C,IAAI,CAAC,KAAK,EAAE,CAAA;YACZ,6DAA6D;YAC7D,yBAAyB;QAC7B,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,SAAS,EAAE,CAAA;QACpB,CAAC;IACL,CAAC;IAED,IAAI;QACA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;QACzD,IAAI,CAAC,SAAS,EAAE,CAAA;IACpB,CAAC;IAED,KAAK;QACD,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QACjD,IAAI,CAAC,SAAS,EAAE,CAAA;IACpB,CAAC;IAED,SAAS;QACL,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAM;QAC7C,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAM;QAC7C,IAAI,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAChD,IAAI,CAAC,SAAS;YAAE,OAAM;QACtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,SAAS,EAAE,CAAC;YACnC,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;gBACvC,KAAK,CAAC,IAAI,CACN,GAAG,IAAI,CAAC,QAAQ,mBAAmB,IAAI,CAAC,OAAO,QAAQ,CAC1D,CAAA;YACL,CAAC;iBAAM,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE,CAAC;gBAC/C,KAAK,CAAC,IAAI,CACN,GAAG,IAAI,CAAC,QAAQ,iBAAiB,IAAI,CAAC,OAAO,QAAQ,CACxD,CAAA;YACL,CAAC;iBAAM,CAAC;gBACJ,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;YACnD,CAAC;QACL,CAAC;IACL,CAAC;IAED,iBAAiB,CAAC,GAAW;QACzB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAM;QAC7C,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAM;QAC7C,IAAI,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAChD,IAAI,CAAC,SAAS;YAAE,OAAM;QACtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,SAAS,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC,CAAA;QAC1C,CAAC;IACL,CAAC;CACJ;AAED,eAAe,cAAc,CAAA"}
dist/services/RoomManager.d.ts ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WebSocket from 'ws';
2
+ export declare class RoomManager {
3
+ private rooms;
4
+ constructor();
5
+ /**
6
+ * Adds a user to a specific room.
7
+ * Creates the room if it doesn't exist.
8
+ */
9
+ joinRoom(roomId: string, username: string, socket: WebSocket): void;
10
+ /**
11
+ * Removes a user from a room.
12
+ * Cleans up the room if it becomes empty.
13
+ */
14
+ leaveRoom(roomId: string, username: string): boolean;
15
+ /**
16
+ * Checks if a room exists.
17
+ */
18
+ hasRoom(roomId: string): boolean;
19
+ /**
20
+ * Sends a message to all users in a specific room.
21
+ */
22
+ broadcast(roomId: string, message: string): void;
23
+ }
24
+ //# sourceMappingURL=RoomManager.d.ts.map
dist/services/RoomManager.d.ts.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"RoomManager.d.ts","sourceRoot":"","sources":["../../src/services/RoomManager.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAA;AAE1B,qBAAa,WAAW;IAEpB,OAAO,CAAC,KAAK,CAAqC;;IAMlD;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI;IAOnE;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAYpD;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;CAUnD"}
dist/services/RoomManager.js ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WebSocket from 'ws';
2
+ export class RoomManager {
3
+ // Map<RoomId, Map<Username, WebSocket>>
4
+ rooms;
5
+ constructor() {
6
+ this.rooms = new Map();
7
+ }
8
+ /**
9
+ * Adds a user to a specific room.
10
+ * Creates the room if it doesn't exist.
11
+ */
12
+ joinRoom(roomId, username, socket) {
13
+ if (!this.rooms.has(roomId)) {
14
+ this.rooms.set(roomId, new Map());
15
+ }
16
+ this.rooms.get(roomId)?.set(username, socket);
17
+ }
18
+ /**
19
+ * Removes a user from a room.
20
+ * Cleans up the room if it becomes empty.
21
+ */
22
+ leaveRoom(roomId, username) {
23
+ const room = this.rooms.get(roomId);
24
+ if (room) {
25
+ const deleted = room.delete(username);
26
+ if (room.size === 0) {
27
+ this.rooms.delete(roomId);
28
+ }
29
+ return deleted;
30
+ }
31
+ return false;
32
+ }
33
+ /**
34
+ * Checks if a room exists.
35
+ */
36
+ hasRoom(roomId) {
37
+ return this.rooms.has(roomId);
38
+ }
39
+ /**
40
+ * Sends a message to all users in a specific room.
41
+ */
42
+ broadcast(roomId, message) {
43
+ const room = this.rooms.get(roomId);
44
+ if (!room)
45
+ return;
46
+ for (const client of room.values()) {
47
+ if (client.readyState === WebSocket.OPEN) {
48
+ client.send(message);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ //# sourceMappingURL=RoomManager.js.map
dist/services/RoomManager.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"RoomManager.js","sourceRoot":"","sources":["../../src/services/RoomManager.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAA;AAE1B,MAAM,OAAO,WAAW;IACpB,wCAAwC;IAChC,KAAK,CAAqC;IAElD;QACI,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAA;IAC1B,CAAC;IAED;;;OAGG;IACH,QAAQ,CAAC,MAAc,EAAE,QAAgB,EAAE,MAAiB;QACxD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC,CAAA;QACrC,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IACjD,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,MAAc,EAAE,QAAgB;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACnC,IAAI,IAAI,EAAE,CAAC;YACP,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;YACrC,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBAClB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC7B,CAAC;YACD,OAAO,OAAO,CAAA;QAClB,CAAC;QACD,OAAO,KAAK,CAAA;IAChB,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,MAAc;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACjC,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,MAAc,EAAE,OAAe;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI;YAAE,OAAM;QAEjB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACjC,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;gBACvC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACxB,CAAC;QACL,CAAC;IACL,CAAC;CACJ"}
dist/services/RoomServices.d.ts ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WebSocket from 'ws';
2
+ declare class RoomServices {
3
+ map: Map<string, Map<string, WebSocket>>;
4
+ constructor();
5
+ hasRoom(roomId: string): boolean;
6
+ getRoom(roomId: string): Map<string, WebSocket> | undefined;
7
+ leaveRoom(roomId: string, username: string): boolean;
8
+ putRoom(roomId: string, username: string, ws: WebSocket): void;
9
+ getConnection(room_id: string, username: string): WebSocket | undefined;
10
+ }
11
+ export default RoomServices;
12
+ //# sourceMappingURL=RoomServices.d.ts.map
dist/services/RoomServices.d.ts.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"RoomServices.d.ts","sourceRoot":"","sources":["../../src/services/RoomServices.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAA;AAE1B,cAAM,YAAY;IACd,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAA;;IAMxC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAIhC,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIpD,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS;IAMvD,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAGlD;AAED,eAAe,YAAY,CAAA"}
dist/services/RoomServices.js ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WebSocket from 'ws';
2
+ class RoomServices {
3
+ map;
4
+ //instead of WebSocket[] use Map<string, WebSocket> mapped to username to its websocket, so you cna make him leave whenever needed!
5
+ constructor() {
6
+ this.map = new Map();
7
+ }
8
+ hasRoom(roomId) {
9
+ return this.map.has(roomId);
10
+ }
11
+ getRoom(roomId) {
12
+ return this.map.get(roomId);
13
+ }
14
+ leaveRoom(roomId, username) {
15
+ return this.getRoom(roomId)?.delete(username) === true;
16
+ }
17
+ putRoom(roomId, username, ws) {
18
+ if (!this.hasRoom(roomId))
19
+ this.map.set(roomId, new Map());
20
+ this.map.get(roomId)?.set(username, ws);
21
+ }
22
+ getConnection(room_id, username) {
23
+ return this.map.get(room_id)?.get(username);
24
+ }
25
+ }
26
+ export default RoomServices;
27
+ //# sourceMappingURL=RoomServices.js.map
dist/services/RoomServices.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"RoomServices.js","sourceRoot":"","sources":["../../src/services/RoomServices.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAA;AAE1B,MAAM,YAAY;IACd,GAAG,CAAqC;IACxC,mIAAmI;IACnI;QACI,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,CAAA;IACxB,CAAC;IAED,OAAO,CAAC,MAAc;QAClB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC/B,CAAC;IAED,OAAO,CAAC,MAAc;QAClB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC/B,CAAC;IAED,SAAS,CAAC,MAAc,EAAE,QAAgB;QACtC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAA;IAC1D,CAAC;IAED,OAAO,CAAC,MAAc,EAAE,QAAgB,EAAE,EAAa;QACnD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YACrB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,GAAG,EAAqB,CAAC,CAAA;QACtD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;IAC3C,CAAC;IAED,aAAa,CAAC,OAAe,EAAE,QAAgB;QAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC/C,CAAC;CACJ;AAED,eAAe,YAAY,CAAA"}
dist/types.d.ts ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export declare enum SocketAction {
2
+ JOIN = "join",
3
+ LEAVE = "leave",
4
+ MESSAGE = "message"
5
+ }
6
+ export interface WebSocketMessage {
7
+ room_id: string;
8
+ username: string;
9
+ message: string;
10
+ action: SocketAction;
11
+ }
12
+ //# sourceMappingURL=types.d.ts.map
dist/types.d.ts.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oBAAY,YAAY;IACpB,IAAI,SAAS;IACb,KAAK,UAAU;IACf,OAAO,YAAY;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,YAAY,CAAA;CACvB"}
dist/types.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ export var SocketAction;
2
+ (function (SocketAction) {
3
+ SocketAction["JOIN"] = "join";
4
+ SocketAction["LEAVE"] = "leave";
5
+ SocketAction["MESSAGE"] = "message";
6
+ })(SocketAction || (SocketAction = {}));
7
+ //# sourceMappingURL=types.js.map
dist/types.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,YAIX;AAJD,WAAY,YAAY;IACpB,6BAAa,CAAA;IACb,+BAAe,CAAA;IACf,mCAAmB,CAAA;AACvB,CAAC,EAJW,YAAY,KAAZ,YAAY,QAIvB"}
package-lock.json ADDED
@@ -0,0 +1,1679 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "sockets",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "sockets",
9
+ "version": "1.0.0",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "ws": "^8.19.0"
13
+ },
14
+ "devDependencies": {
15
+ "@types/express": "^5.0.6",
16
+ "@types/node": "^25.1.0",
17
+ "@types/ws": "^8.18.1",
18
+ "prettier": "^3.8.1",
19
+ "typescript": "^5.9.3",
20
+ "wrangler": "^4.61.1"
21
+ }
22
+ },
23
+ "node_modules/@cloudflare/kv-asset-handler": {
24
+ "version": "0.4.2",
25
+ "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz",
26
+ "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==",
27
+ "dev": true,
28
+ "license": "MIT OR Apache-2.0",
29
+ "engines": {
30
+ "node": ">=18.0.0"
31
+ }
32
+ },
33
+ "node_modules/@cloudflare/unenv-preset": {
34
+ "version": "2.12.0",
35
+ "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.12.0.tgz",
36
+ "integrity": "sha512-NK4vN+2Z/GbfGS4BamtbbVk1rcu5RmqaYGiyHJQrA09AoxdZPHDF3W/EhgI0YSK8p3vRo/VNCtbSJFPON7FWMQ==",
37
+ "dev": true,
38
+ "license": "MIT OR Apache-2.0",
39
+ "peerDependencies": {
40
+ "unenv": "2.0.0-rc.24",
41
+ "workerd": "^1.20260115.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "workerd": {
45
+ "optional": true
46
+ }
47
+ }
48
+ },
49
+ "node_modules/@cloudflare/workerd-darwin-64": {
50
+ "version": "1.20260128.0",
51
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260128.0.tgz",
52
+ "integrity": "sha512-XJN8zWWNG3JwAUqqwMLNKJ9fZfdlQkx/zTTHW/BB8wHat9LjKD6AzxqCu432YmfjR+NxEKCzUOxMu1YOxlVxmg==",
53
+ "cpu": [
54
+ "x64"
55
+ ],
56
+ "dev": true,
57
+ "license": "Apache-2.0",
58
+ "optional": true,
59
+ "os": [
60
+ "darwin"
61
+ ],
62
+ "engines": {
63
+ "node": ">=16"
64
+ }
65
+ },
66
+ "node_modules/@cloudflare/workerd-darwin-arm64": {
67
+ "version": "1.20260128.0",
68
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260128.0.tgz",
69
+ "integrity": "sha512-vKnRcmnm402GQ5DOdfT5H34qeR2m07nhnTtky8mTkNWP+7xmkz32AMdclwMmfO/iX9ncyKwSqmml2wPG32eq/w==",
70
+ "cpu": [
71
+ "arm64"
72
+ ],
73
+ "dev": true,
74
+ "license": "Apache-2.0",
75
+ "optional": true,
76
+ "os": [
77
+ "darwin"
78
+ ],
79
+ "engines": {
80
+ "node": ">=16"
81
+ }
82
+ },
83
+ "node_modules/@cloudflare/workerd-linux-64": {
84
+ "version": "1.20260128.0",
85
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260128.0.tgz",
86
+ "integrity": "sha512-RiaR+Qugof/c6oI5SagD2J5wJmIfI8wQWaV2Y9905Raj6sAYOFaEKfzkKnoLLLNYb4NlXicBrffJi1j7R/ypUA==",
87
+ "cpu": [
88
+ "x64"
89
+ ],
90
+ "dev": true,
91
+ "license": "Apache-2.0",
92
+ "optional": true,
93
+ "os": [
94
+ "linux"
95
+ ],
96
+ "engines": {
97
+ "node": ">=16"
98
+ }
99
+ },
100
+ "node_modules/@cloudflare/workerd-linux-arm64": {
101
+ "version": "1.20260128.0",
102
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260128.0.tgz",
103
+ "integrity": "sha512-U39U9vcXLXYDbrJ112Q7D0LDUUnM54oXfAxPgrL2goBwio7Z6RnsM25TRvm+Q06F4+FeDOC4D51JXlFHb9t1OA==",
104
+ "cpu": [
105
+ "arm64"
106
+ ],
107
+ "dev": true,
108
+ "license": "Apache-2.0",
109
+ "optional": true,
110
+ "os": [
111
+ "linux"
112
+ ],
113
+ "engines": {
114
+ "node": ">=16"
115
+ }
116
+ },
117
+ "node_modules/@cloudflare/workerd-windows-64": {
118
+ "version": "1.20260128.0",
119
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260128.0.tgz",
120
+ "integrity": "sha512-fdJwSqRkJsAJFJ7+jy0th2uMO6fwaDA8Ny6+iFCssfzlNkc4dP/twXo+3F66FMLMe/6NIqjzVts0cpiv7ERYbQ==",
121
+ "cpu": [
122
+ "x64"
123
+ ],
124
+ "dev": true,
125
+ "license": "Apache-2.0",
126
+ "optional": true,
127
+ "os": [
128
+ "win32"
129
+ ],
130
+ "engines": {
131
+ "node": ">=16"
132
+ }
133
+ },
134
+ "node_modules/@cspotcode/source-map-support": {
135
+ "version": "0.8.1",
136
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
137
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
138
+ "dev": true,
139
+ "license": "MIT",
140
+ "dependencies": {
141
+ "@jridgewell/trace-mapping": "0.3.9"
142
+ },
143
+ "engines": {
144
+ "node": ">=12"
145
+ }
146
+ },
147
+ "node_modules/@emnapi/runtime": {
148
+ "version": "1.8.1",
149
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
150
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
151
+ "dev": true,
152
+ "license": "MIT",
153
+ "optional": true,
154
+ "dependencies": {
155
+ "tslib": "^2.4.0"
156
+ }
157
+ },
158
+ "node_modules/@esbuild/aix-ppc64": {
159
+ "version": "0.27.0",
160
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz",
161
+ "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==",
162
+ "cpu": [
163
+ "ppc64"
164
+ ],
165
+ "dev": true,
166
+ "license": "MIT",
167
+ "optional": true,
168
+ "os": [
169
+ "aix"
170
+ ],
171
+ "engines": {
172
+ "node": ">=18"
173
+ }
174
+ },
175
+ "node_modules/@esbuild/android-arm": {
176
+ "version": "0.27.0",
177
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz",
178
+ "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==",
179
+ "cpu": [
180
+ "arm"
181
+ ],
182
+ "dev": true,
183
+ "license": "MIT",
184
+ "optional": true,
185
+ "os": [
186
+ "android"
187
+ ],
188
+ "engines": {
189
+ "node": ">=18"
190
+ }
191
+ },
192
+ "node_modules/@esbuild/android-arm64": {
193
+ "version": "0.27.0",
194
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz",
195
+ "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==",
196
+ "cpu": [
197
+ "arm64"
198
+ ],
199
+ "dev": true,
200
+ "license": "MIT",
201
+ "optional": true,
202
+ "os": [
203
+ "android"
204
+ ],
205
+ "engines": {
206
+ "node": ">=18"
207
+ }
208
+ },
209
+ "node_modules/@esbuild/android-x64": {
210
+ "version": "0.27.0",
211
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz",
212
+ "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==",
213
+ "cpu": [
214
+ "x64"
215
+ ],
216
+ "dev": true,
217
+ "license": "MIT",
218
+ "optional": true,
219
+ "os": [
220
+ "android"
221
+ ],
222
+ "engines": {
223
+ "node": ">=18"
224
+ }
225
+ },
226
+ "node_modules/@esbuild/darwin-arm64": {
227
+ "version": "0.27.0",
228
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz",
229
+ "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==",
230
+ "cpu": [
231
+ "arm64"
232
+ ],
233
+ "dev": true,
234
+ "license": "MIT",
235
+ "optional": true,
236
+ "os": [
237
+ "darwin"
238
+ ],
239
+ "engines": {
240
+ "node": ">=18"
241
+ }
242
+ },
243
+ "node_modules/@esbuild/darwin-x64": {
244
+ "version": "0.27.0",
245
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz",
246
+ "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==",
247
+ "cpu": [
248
+ "x64"
249
+ ],
250
+ "dev": true,
251
+ "license": "MIT",
252
+ "optional": true,
253
+ "os": [
254
+ "darwin"
255
+ ],
256
+ "engines": {
257
+ "node": ">=18"
258
+ }
259
+ },
260
+ "node_modules/@esbuild/freebsd-arm64": {
261
+ "version": "0.27.0",
262
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz",
263
+ "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==",
264
+ "cpu": [
265
+ "arm64"
266
+ ],
267
+ "dev": true,
268
+ "license": "MIT",
269
+ "optional": true,
270
+ "os": [
271
+ "freebsd"
272
+ ],
273
+ "engines": {
274
+ "node": ">=18"
275
+ }
276
+ },
277
+ "node_modules/@esbuild/freebsd-x64": {
278
+ "version": "0.27.0",
279
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz",
280
+ "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==",
281
+ "cpu": [
282
+ "x64"
283
+ ],
284
+ "dev": true,
285
+ "license": "MIT",
286
+ "optional": true,
287
+ "os": [
288
+ "freebsd"
289
+ ],
290
+ "engines": {
291
+ "node": ">=18"
292
+ }
293
+ },
294
+ "node_modules/@esbuild/linux-arm": {
295
+ "version": "0.27.0",
296
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz",
297
+ "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==",
298
+ "cpu": [
299
+ "arm"
300
+ ],
301
+ "dev": true,
302
+ "license": "MIT",
303
+ "optional": true,
304
+ "os": [
305
+ "linux"
306
+ ],
307
+ "engines": {
308
+ "node": ">=18"
309
+ }
310
+ },
311
+ "node_modules/@esbuild/linux-arm64": {
312
+ "version": "0.27.0",
313
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz",
314
+ "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==",
315
+ "cpu": [
316
+ "arm64"
317
+ ],
318
+ "dev": true,
319
+ "license": "MIT",
320
+ "optional": true,
321
+ "os": [
322
+ "linux"
323
+ ],
324
+ "engines": {
325
+ "node": ">=18"
326
+ }
327
+ },
328
+ "node_modules/@esbuild/linux-ia32": {
329
+ "version": "0.27.0",
330
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz",
331
+ "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==",
332
+ "cpu": [
333
+ "ia32"
334
+ ],
335
+ "dev": true,
336
+ "license": "MIT",
337
+ "optional": true,
338
+ "os": [
339
+ "linux"
340
+ ],
341
+ "engines": {
342
+ "node": ">=18"
343
+ }
344
+ },
345
+ "node_modules/@esbuild/linux-loong64": {
346
+ "version": "0.27.0",
347
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz",
348
+ "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==",
349
+ "cpu": [
350
+ "loong64"
351
+ ],
352
+ "dev": true,
353
+ "license": "MIT",
354
+ "optional": true,
355
+ "os": [
356
+ "linux"
357
+ ],
358
+ "engines": {
359
+ "node": ">=18"
360
+ }
361
+ },
362
+ "node_modules/@esbuild/linux-mips64el": {
363
+ "version": "0.27.0",
364
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz",
365
+ "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==",
366
+ "cpu": [
367
+ "mips64el"
368
+ ],
369
+ "dev": true,
370
+ "license": "MIT",
371
+ "optional": true,
372
+ "os": [
373
+ "linux"
374
+ ],
375
+ "engines": {
376
+ "node": ">=18"
377
+ }
378
+ },
379
+ "node_modules/@esbuild/linux-ppc64": {
380
+ "version": "0.27.0",
381
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz",
382
+ "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==",
383
+ "cpu": [
384
+ "ppc64"
385
+ ],
386
+ "dev": true,
387
+ "license": "MIT",
388
+ "optional": true,
389
+ "os": [
390
+ "linux"
391
+ ],
392
+ "engines": {
393
+ "node": ">=18"
394
+ }
395
+ },
396
+ "node_modules/@esbuild/linux-riscv64": {
397
+ "version": "0.27.0",
398
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz",
399
+ "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==",
400
+ "cpu": [
401
+ "riscv64"
402
+ ],
403
+ "dev": true,
404
+ "license": "MIT",
405
+ "optional": true,
406
+ "os": [
407
+ "linux"
408
+ ],
409
+ "engines": {
410
+ "node": ">=18"
411
+ }
412
+ },
413
+ "node_modules/@esbuild/linux-s390x": {
414
+ "version": "0.27.0",
415
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz",
416
+ "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==",
417
+ "cpu": [
418
+ "s390x"
419
+ ],
420
+ "dev": true,
421
+ "license": "MIT",
422
+ "optional": true,
423
+ "os": [
424
+ "linux"
425
+ ],
426
+ "engines": {
427
+ "node": ">=18"
428
+ }
429
+ },
430
+ "node_modules/@esbuild/linux-x64": {
431
+ "version": "0.27.0",
432
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz",
433
+ "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==",
434
+ "cpu": [
435
+ "x64"
436
+ ],
437
+ "dev": true,
438
+ "license": "MIT",
439
+ "optional": true,
440
+ "os": [
441
+ "linux"
442
+ ],
443
+ "engines": {
444
+ "node": ">=18"
445
+ }
446
+ },
447
+ "node_modules/@esbuild/netbsd-arm64": {
448
+ "version": "0.27.0",
449
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz",
450
+ "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==",
451
+ "cpu": [
452
+ "arm64"
453
+ ],
454
+ "dev": true,
455
+ "license": "MIT",
456
+ "optional": true,
457
+ "os": [
458
+ "netbsd"
459
+ ],
460
+ "engines": {
461
+ "node": ">=18"
462
+ }
463
+ },
464
+ "node_modules/@esbuild/netbsd-x64": {
465
+ "version": "0.27.0",
466
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz",
467
+ "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==",
468
+ "cpu": [
469
+ "x64"
470
+ ],
471
+ "dev": true,
472
+ "license": "MIT",
473
+ "optional": true,
474
+ "os": [
475
+ "netbsd"
476
+ ],
477
+ "engines": {
478
+ "node": ">=18"
479
+ }
480
+ },
481
+ "node_modules/@esbuild/openbsd-arm64": {
482
+ "version": "0.27.0",
483
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz",
484
+ "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==",
485
+ "cpu": [
486
+ "arm64"
487
+ ],
488
+ "dev": true,
489
+ "license": "MIT",
490
+ "optional": true,
491
+ "os": [
492
+ "openbsd"
493
+ ],
494
+ "engines": {
495
+ "node": ">=18"
496
+ }
497
+ },
498
+ "node_modules/@esbuild/openbsd-x64": {
499
+ "version": "0.27.0",
500
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz",
501
+ "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==",
502
+ "cpu": [
503
+ "x64"
504
+ ],
505
+ "dev": true,
506
+ "license": "MIT",
507
+ "optional": true,
508
+ "os": [
509
+ "openbsd"
510
+ ],
511
+ "engines": {
512
+ "node": ">=18"
513
+ }
514
+ },
515
+ "node_modules/@esbuild/openharmony-arm64": {
516
+ "version": "0.27.0",
517
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz",
518
+ "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==",
519
+ "cpu": [
520
+ "arm64"
521
+ ],
522
+ "dev": true,
523
+ "license": "MIT",
524
+ "optional": true,
525
+ "os": [
526
+ "openharmony"
527
+ ],
528
+ "engines": {
529
+ "node": ">=18"
530
+ }
531
+ },
532
+ "node_modules/@esbuild/sunos-x64": {
533
+ "version": "0.27.0",
534
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz",
535
+ "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==",
536
+ "cpu": [
537
+ "x64"
538
+ ],
539
+ "dev": true,
540
+ "license": "MIT",
541
+ "optional": true,
542
+ "os": [
543
+ "sunos"
544
+ ],
545
+ "engines": {
546
+ "node": ">=18"
547
+ }
548
+ },
549
+ "node_modules/@esbuild/win32-arm64": {
550
+ "version": "0.27.0",
551
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz",
552
+ "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==",
553
+ "cpu": [
554
+ "arm64"
555
+ ],
556
+ "dev": true,
557
+ "license": "MIT",
558
+ "optional": true,
559
+ "os": [
560
+ "win32"
561
+ ],
562
+ "engines": {
563
+ "node": ">=18"
564
+ }
565
+ },
566
+ "node_modules/@esbuild/win32-ia32": {
567
+ "version": "0.27.0",
568
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz",
569
+ "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==",
570
+ "cpu": [
571
+ "ia32"
572
+ ],
573
+ "dev": true,
574
+ "license": "MIT",
575
+ "optional": true,
576
+ "os": [
577
+ "win32"
578
+ ],
579
+ "engines": {
580
+ "node": ">=18"
581
+ }
582
+ },
583
+ "node_modules/@esbuild/win32-x64": {
584
+ "version": "0.27.0",
585
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz",
586
+ "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==",
587
+ "cpu": [
588
+ "x64"
589
+ ],
590
+ "dev": true,
591
+ "license": "MIT",
592
+ "optional": true,
593
+ "os": [
594
+ "win32"
595
+ ],
596
+ "engines": {
597
+ "node": ">=18"
598
+ }
599
+ },
600
+ "node_modules/@img/colour": {
601
+ "version": "1.0.0",
602
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
603
+ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
604
+ "dev": true,
605
+ "license": "MIT",
606
+ "engines": {
607
+ "node": ">=18"
608
+ }
609
+ },
610
+ "node_modules/@img/sharp-darwin-arm64": {
611
+ "version": "0.34.5",
612
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
613
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
614
+ "cpu": [
615
+ "arm64"
616
+ ],
617
+ "dev": true,
618
+ "license": "Apache-2.0",
619
+ "optional": true,
620
+ "os": [
621
+ "darwin"
622
+ ],
623
+ "engines": {
624
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
625
+ },
626
+ "funding": {
627
+ "url": "https://opencollective.com/libvips"
628
+ },
629
+ "optionalDependencies": {
630
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
631
+ }
632
+ },
633
+ "node_modules/@img/sharp-darwin-x64": {
634
+ "version": "0.34.5",
635
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
636
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
637
+ "cpu": [
638
+ "x64"
639
+ ],
640
+ "dev": true,
641
+ "license": "Apache-2.0",
642
+ "optional": true,
643
+ "os": [
644
+ "darwin"
645
+ ],
646
+ "engines": {
647
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
648
+ },
649
+ "funding": {
650
+ "url": "https://opencollective.com/libvips"
651
+ },
652
+ "optionalDependencies": {
653
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
654
+ }
655
+ },
656
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
657
+ "version": "1.2.4",
658
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
659
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
660
+ "cpu": [
661
+ "arm64"
662
+ ],
663
+ "dev": true,
664
+ "license": "LGPL-3.0-or-later",
665
+ "optional": true,
666
+ "os": [
667
+ "darwin"
668
+ ],
669
+ "funding": {
670
+ "url": "https://opencollective.com/libvips"
671
+ }
672
+ },
673
+ "node_modules/@img/sharp-libvips-darwin-x64": {
674
+ "version": "1.2.4",
675
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
676
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
677
+ "cpu": [
678
+ "x64"
679
+ ],
680
+ "dev": true,
681
+ "license": "LGPL-3.0-or-later",
682
+ "optional": true,
683
+ "os": [
684
+ "darwin"
685
+ ],
686
+ "funding": {
687
+ "url": "https://opencollective.com/libvips"
688
+ }
689
+ },
690
+ "node_modules/@img/sharp-libvips-linux-arm": {
691
+ "version": "1.2.4",
692
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
693
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
694
+ "cpu": [
695
+ "arm"
696
+ ],
697
+ "dev": true,
698
+ "license": "LGPL-3.0-or-later",
699
+ "optional": true,
700
+ "os": [
701
+ "linux"
702
+ ],
703
+ "funding": {
704
+ "url": "https://opencollective.com/libvips"
705
+ }
706
+ },
707
+ "node_modules/@img/sharp-libvips-linux-arm64": {
708
+ "version": "1.2.4",
709
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
710
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
711
+ "cpu": [
712
+ "arm64"
713
+ ],
714
+ "dev": true,
715
+ "license": "LGPL-3.0-or-later",
716
+ "optional": true,
717
+ "os": [
718
+ "linux"
719
+ ],
720
+ "funding": {
721
+ "url": "https://opencollective.com/libvips"
722
+ }
723
+ },
724
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
725
+ "version": "1.2.4",
726
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
727
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
728
+ "cpu": [
729
+ "ppc64"
730
+ ],
731
+ "dev": true,
732
+ "license": "LGPL-3.0-or-later",
733
+ "optional": true,
734
+ "os": [
735
+ "linux"
736
+ ],
737
+ "funding": {
738
+ "url": "https://opencollective.com/libvips"
739
+ }
740
+ },
741
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
742
+ "version": "1.2.4",
743
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
744
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
745
+ "cpu": [
746
+ "riscv64"
747
+ ],
748
+ "dev": true,
749
+ "license": "LGPL-3.0-or-later",
750
+ "optional": true,
751
+ "os": [
752
+ "linux"
753
+ ],
754
+ "funding": {
755
+ "url": "https://opencollective.com/libvips"
756
+ }
757
+ },
758
+ "node_modules/@img/sharp-libvips-linux-s390x": {
759
+ "version": "1.2.4",
760
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
761
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
762
+ "cpu": [
763
+ "s390x"
764
+ ],
765
+ "dev": true,
766
+ "license": "LGPL-3.0-or-later",
767
+ "optional": true,
768
+ "os": [
769
+ "linux"
770
+ ],
771
+ "funding": {
772
+ "url": "https://opencollective.com/libvips"
773
+ }
774
+ },
775
+ "node_modules/@img/sharp-libvips-linux-x64": {
776
+ "version": "1.2.4",
777
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
778
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
779
+ "cpu": [
780
+ "x64"
781
+ ],
782
+ "dev": true,
783
+ "license": "LGPL-3.0-or-later",
784
+ "optional": true,
785
+ "os": [
786
+ "linux"
787
+ ],
788
+ "funding": {
789
+ "url": "https://opencollective.com/libvips"
790
+ }
791
+ },
792
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
793
+ "version": "1.2.4",
794
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
795
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
796
+ "cpu": [
797
+ "arm64"
798
+ ],
799
+ "dev": true,
800
+ "license": "LGPL-3.0-or-later",
801
+ "optional": true,
802
+ "os": [
803
+ "linux"
804
+ ],
805
+ "funding": {
806
+ "url": "https://opencollective.com/libvips"
807
+ }
808
+ },
809
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
810
+ "version": "1.2.4",
811
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
812
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
813
+ "cpu": [
814
+ "x64"
815
+ ],
816
+ "dev": true,
817
+ "license": "LGPL-3.0-or-later",
818
+ "optional": true,
819
+ "os": [
820
+ "linux"
821
+ ],
822
+ "funding": {
823
+ "url": "https://opencollective.com/libvips"
824
+ }
825
+ },
826
+ "node_modules/@img/sharp-linux-arm": {
827
+ "version": "0.34.5",
828
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
829
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
830
+ "cpu": [
831
+ "arm"
832
+ ],
833
+ "dev": true,
834
+ "license": "Apache-2.0",
835
+ "optional": true,
836
+ "os": [
837
+ "linux"
838
+ ],
839
+ "engines": {
840
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
841
+ },
842
+ "funding": {
843
+ "url": "https://opencollective.com/libvips"
844
+ },
845
+ "optionalDependencies": {
846
+ "@img/sharp-libvips-linux-arm": "1.2.4"
847
+ }
848
+ },
849
+ "node_modules/@img/sharp-linux-arm64": {
850
+ "version": "0.34.5",
851
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
852
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
853
+ "cpu": [
854
+ "arm64"
855
+ ],
856
+ "dev": true,
857
+ "license": "Apache-2.0",
858
+ "optional": true,
859
+ "os": [
860
+ "linux"
861
+ ],
862
+ "engines": {
863
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
864
+ },
865
+ "funding": {
866
+ "url": "https://opencollective.com/libvips"
867
+ },
868
+ "optionalDependencies": {
869
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
870
+ }
871
+ },
872
+ "node_modules/@img/sharp-linux-ppc64": {
873
+ "version": "0.34.5",
874
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
875
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
876
+ "cpu": [
877
+ "ppc64"
878
+ ],
879
+ "dev": true,
880
+ "license": "Apache-2.0",
881
+ "optional": true,
882
+ "os": [
883
+ "linux"
884
+ ],
885
+ "engines": {
886
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
887
+ },
888
+ "funding": {
889
+ "url": "https://opencollective.com/libvips"
890
+ },
891
+ "optionalDependencies": {
892
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
893
+ }
894
+ },
895
+ "node_modules/@img/sharp-linux-riscv64": {
896
+ "version": "0.34.5",
897
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
898
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
899
+ "cpu": [
900
+ "riscv64"
901
+ ],
902
+ "dev": true,
903
+ "license": "Apache-2.0",
904
+ "optional": true,
905
+ "os": [
906
+ "linux"
907
+ ],
908
+ "engines": {
909
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
910
+ },
911
+ "funding": {
912
+ "url": "https://opencollective.com/libvips"
913
+ },
914
+ "optionalDependencies": {
915
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
916
+ }
917
+ },
918
+ "node_modules/@img/sharp-linux-s390x": {
919
+ "version": "0.34.5",
920
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
921
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
922
+ "cpu": [
923
+ "s390x"
924
+ ],
925
+ "dev": true,
926
+ "license": "Apache-2.0",
927
+ "optional": true,
928
+ "os": [
929
+ "linux"
930
+ ],
931
+ "engines": {
932
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
933
+ },
934
+ "funding": {
935
+ "url": "https://opencollective.com/libvips"
936
+ },
937
+ "optionalDependencies": {
938
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
939
+ }
940
+ },
941
+ "node_modules/@img/sharp-linux-x64": {
942
+ "version": "0.34.5",
943
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
944
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
945
+ "cpu": [
946
+ "x64"
947
+ ],
948
+ "dev": true,
949
+ "license": "Apache-2.0",
950
+ "optional": true,
951
+ "os": [
952
+ "linux"
953
+ ],
954
+ "engines": {
955
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
956
+ },
957
+ "funding": {
958
+ "url": "https://opencollective.com/libvips"
959
+ },
960
+ "optionalDependencies": {
961
+ "@img/sharp-libvips-linux-x64": "1.2.4"
962
+ }
963
+ },
964
+ "node_modules/@img/sharp-linuxmusl-arm64": {
965
+ "version": "0.34.5",
966
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
967
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
968
+ "cpu": [
969
+ "arm64"
970
+ ],
971
+ "dev": true,
972
+ "license": "Apache-2.0",
973
+ "optional": true,
974
+ "os": [
975
+ "linux"
976
+ ],
977
+ "engines": {
978
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
979
+ },
980
+ "funding": {
981
+ "url": "https://opencollective.com/libvips"
982
+ },
983
+ "optionalDependencies": {
984
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
985
+ }
986
+ },
987
+ "node_modules/@img/sharp-linuxmusl-x64": {
988
+ "version": "0.34.5",
989
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
990
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
991
+ "cpu": [
992
+ "x64"
993
+ ],
994
+ "dev": true,
995
+ "license": "Apache-2.0",
996
+ "optional": true,
997
+ "os": [
998
+ "linux"
999
+ ],
1000
+ "engines": {
1001
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
1002
+ },
1003
+ "funding": {
1004
+ "url": "https://opencollective.com/libvips"
1005
+ },
1006
+ "optionalDependencies": {
1007
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
1008
+ }
1009
+ },
1010
+ "node_modules/@img/sharp-wasm32": {
1011
+ "version": "0.34.5",
1012
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
1013
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
1014
+ "cpu": [
1015
+ "wasm32"
1016
+ ],
1017
+ "dev": true,
1018
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
1019
+ "optional": true,
1020
+ "dependencies": {
1021
+ "@emnapi/runtime": "^1.7.0"
1022
+ },
1023
+ "engines": {
1024
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
1025
+ },
1026
+ "funding": {
1027
+ "url": "https://opencollective.com/libvips"
1028
+ }
1029
+ },
1030
+ "node_modules/@img/sharp-win32-arm64": {
1031
+ "version": "0.34.5",
1032
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
1033
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
1034
+ "cpu": [
1035
+ "arm64"
1036
+ ],
1037
+ "dev": true,
1038
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
1039
+ "optional": true,
1040
+ "os": [
1041
+ "win32"
1042
+ ],
1043
+ "engines": {
1044
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
1045
+ },
1046
+ "funding": {
1047
+ "url": "https://opencollective.com/libvips"
1048
+ }
1049
+ },
1050
+ "node_modules/@img/sharp-win32-ia32": {
1051
+ "version": "0.34.5",
1052
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
1053
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
1054
+ "cpu": [
1055
+ "ia32"
1056
+ ],
1057
+ "dev": true,
1058
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
1059
+ "optional": true,
1060
+ "os": [
1061
+ "win32"
1062
+ ],
1063
+ "engines": {
1064
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
1065
+ },
1066
+ "funding": {
1067
+ "url": "https://opencollective.com/libvips"
1068
+ }
1069
+ },
1070
+ "node_modules/@img/sharp-win32-x64": {
1071
+ "version": "0.34.5",
1072
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
1073
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
1074
+ "cpu": [
1075
+ "x64"
1076
+ ],
1077
+ "dev": true,
1078
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
1079
+ "optional": true,
1080
+ "os": [
1081
+ "win32"
1082
+ ],
1083
+ "engines": {
1084
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
1085
+ },
1086
+ "funding": {
1087
+ "url": "https://opencollective.com/libvips"
1088
+ }
1089
+ },
1090
+ "node_modules/@jridgewell/resolve-uri": {
1091
+ "version": "3.1.2",
1092
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
1093
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
1094
+ "dev": true,
1095
+ "license": "MIT",
1096
+ "engines": {
1097
+ "node": ">=6.0.0"
1098
+ }
1099
+ },
1100
+ "node_modules/@jridgewell/sourcemap-codec": {
1101
+ "version": "1.5.5",
1102
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
1103
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
1104
+ "dev": true,
1105
+ "license": "MIT"
1106
+ },
1107
+ "node_modules/@jridgewell/trace-mapping": {
1108
+ "version": "0.3.9",
1109
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
1110
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
1111
+ "dev": true,
1112
+ "license": "MIT",
1113
+ "dependencies": {
1114
+ "@jridgewell/resolve-uri": "^3.0.3",
1115
+ "@jridgewell/sourcemap-codec": "^1.4.10"
1116
+ }
1117
+ },
1118
+ "node_modules/@poppinss/colors": {
1119
+ "version": "4.1.6",
1120
+ "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz",
1121
+ "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==",
1122
+ "dev": true,
1123
+ "license": "MIT",
1124
+ "dependencies": {
1125
+ "kleur": "^4.1.5"
1126
+ }
1127
+ },
1128
+ "node_modules/@poppinss/dumper": {
1129
+ "version": "0.6.5",
1130
+ "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz",
1131
+ "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==",
1132
+ "dev": true,
1133
+ "license": "MIT",
1134
+ "dependencies": {
1135
+ "@poppinss/colors": "^4.1.5",
1136
+ "@sindresorhus/is": "^7.0.2",
1137
+ "supports-color": "^10.0.0"
1138
+ }
1139
+ },
1140
+ "node_modules/@poppinss/exception": {
1141
+ "version": "1.2.3",
1142
+ "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz",
1143
+ "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==",
1144
+ "dev": true,
1145
+ "license": "MIT"
1146
+ },
1147
+ "node_modules/@sindresorhus/is": {
1148
+ "version": "7.2.0",
1149
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz",
1150
+ "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==",
1151
+ "dev": true,
1152
+ "license": "MIT",
1153
+ "engines": {
1154
+ "node": ">=18"
1155
+ },
1156
+ "funding": {
1157
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
1158
+ }
1159
+ },
1160
+ "node_modules/@speed-highlight/core": {
1161
+ "version": "1.2.14",
1162
+ "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.14.tgz",
1163
+ "integrity": "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==",
1164
+ "dev": true,
1165
+ "license": "CC0-1.0"
1166
+ },
1167
+ "node_modules/@types/body-parser": {
1168
+ "version": "1.19.6",
1169
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
1170
+ "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
1171
+ "dev": true,
1172
+ "license": "MIT",
1173
+ "dependencies": {
1174
+ "@types/connect": "*",
1175
+ "@types/node": "*"
1176
+ }
1177
+ },
1178
+ "node_modules/@types/connect": {
1179
+ "version": "3.4.38",
1180
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
1181
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
1182
+ "dev": true,
1183
+ "license": "MIT",
1184
+ "dependencies": {
1185
+ "@types/node": "*"
1186
+ }
1187
+ },
1188
+ "node_modules/@types/express": {
1189
+ "version": "5.0.6",
1190
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
1191
+ "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
1192
+ "dev": true,
1193
+ "license": "MIT",
1194
+ "dependencies": {
1195
+ "@types/body-parser": "*",
1196
+ "@types/express-serve-static-core": "^5.0.0",
1197
+ "@types/serve-static": "^2"
1198
+ }
1199
+ },
1200
+ "node_modules/@types/express-serve-static-core": {
1201
+ "version": "5.1.1",
1202
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
1203
+ "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
1204
+ "dev": true,
1205
+ "license": "MIT",
1206
+ "dependencies": {
1207
+ "@types/node": "*",
1208
+ "@types/qs": "*",
1209
+ "@types/range-parser": "*",
1210
+ "@types/send": "*"
1211
+ }
1212
+ },
1213
+ "node_modules/@types/http-errors": {
1214
+ "version": "2.0.5",
1215
+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
1216
+ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
1217
+ "dev": true,
1218
+ "license": "MIT"
1219
+ },
1220
+ "node_modules/@types/node": {
1221
+ "version": "25.1.0",
1222
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz",
1223
+ "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==",
1224
+ "dev": true,
1225
+ "license": "MIT",
1226
+ "dependencies": {
1227
+ "undici-types": "~7.16.0"
1228
+ }
1229
+ },
1230
+ "node_modules/@types/qs": {
1231
+ "version": "6.14.0",
1232
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
1233
+ "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
1234
+ "dev": true,
1235
+ "license": "MIT"
1236
+ },
1237
+ "node_modules/@types/range-parser": {
1238
+ "version": "1.2.7",
1239
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
1240
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
1241
+ "dev": true,
1242
+ "license": "MIT"
1243
+ },
1244
+ "node_modules/@types/send": {
1245
+ "version": "1.2.1",
1246
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
1247
+ "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
1248
+ "dev": true,
1249
+ "license": "MIT",
1250
+ "dependencies": {
1251
+ "@types/node": "*"
1252
+ }
1253
+ },
1254
+ "node_modules/@types/serve-static": {
1255
+ "version": "2.2.0",
1256
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
1257
+ "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
1258
+ "dev": true,
1259
+ "license": "MIT",
1260
+ "dependencies": {
1261
+ "@types/http-errors": "*",
1262
+ "@types/node": "*"
1263
+ }
1264
+ },
1265
+ "node_modules/@types/ws": {
1266
+ "version": "8.18.1",
1267
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
1268
+ "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
1269
+ "dev": true,
1270
+ "license": "MIT",
1271
+ "dependencies": {
1272
+ "@types/node": "*"
1273
+ }
1274
+ },
1275
+ "node_modules/blake3-wasm": {
1276
+ "version": "2.1.5",
1277
+ "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
1278
+ "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==",
1279
+ "dev": true,
1280
+ "license": "MIT"
1281
+ },
1282
+ "node_modules/cookie": {
1283
+ "version": "1.1.1",
1284
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
1285
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
1286
+ "dev": true,
1287
+ "license": "MIT",
1288
+ "engines": {
1289
+ "node": ">=18"
1290
+ },
1291
+ "funding": {
1292
+ "type": "opencollective",
1293
+ "url": "https://opencollective.com/express"
1294
+ }
1295
+ },
1296
+ "node_modules/detect-libc": {
1297
+ "version": "2.1.2",
1298
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
1299
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
1300
+ "dev": true,
1301
+ "license": "Apache-2.0",
1302
+ "engines": {
1303
+ "node": ">=8"
1304
+ }
1305
+ },
1306
+ "node_modules/error-stack-parser-es": {
1307
+ "version": "1.0.5",
1308
+ "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz",
1309
+ "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==",
1310
+ "dev": true,
1311
+ "license": "MIT",
1312
+ "funding": {
1313
+ "url": "https://github.com/sponsors/antfu"
1314
+ }
1315
+ },
1316
+ "node_modules/esbuild": {
1317
+ "version": "0.27.0",
1318
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz",
1319
+ "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
1320
+ "dev": true,
1321
+ "hasInstallScript": true,
1322
+ "license": "MIT",
1323
+ "bin": {
1324
+ "esbuild": "bin/esbuild"
1325
+ },
1326
+ "engines": {
1327
+ "node": ">=18"
1328
+ },
1329
+ "optionalDependencies": {
1330
+ "@esbuild/aix-ppc64": "0.27.0",
1331
+ "@esbuild/android-arm": "0.27.0",
1332
+ "@esbuild/android-arm64": "0.27.0",
1333
+ "@esbuild/android-x64": "0.27.0",
1334
+ "@esbuild/darwin-arm64": "0.27.0",
1335
+ "@esbuild/darwin-x64": "0.27.0",
1336
+ "@esbuild/freebsd-arm64": "0.27.0",
1337
+ "@esbuild/freebsd-x64": "0.27.0",
1338
+ "@esbuild/linux-arm": "0.27.0",
1339
+ "@esbuild/linux-arm64": "0.27.0",
1340
+ "@esbuild/linux-ia32": "0.27.0",
1341
+ "@esbuild/linux-loong64": "0.27.0",
1342
+ "@esbuild/linux-mips64el": "0.27.0",
1343
+ "@esbuild/linux-ppc64": "0.27.0",
1344
+ "@esbuild/linux-riscv64": "0.27.0",
1345
+ "@esbuild/linux-s390x": "0.27.0",
1346
+ "@esbuild/linux-x64": "0.27.0",
1347
+ "@esbuild/netbsd-arm64": "0.27.0",
1348
+ "@esbuild/netbsd-x64": "0.27.0",
1349
+ "@esbuild/openbsd-arm64": "0.27.0",
1350
+ "@esbuild/openbsd-x64": "0.27.0",
1351
+ "@esbuild/openharmony-arm64": "0.27.0",
1352
+ "@esbuild/sunos-x64": "0.27.0",
1353
+ "@esbuild/win32-arm64": "0.27.0",
1354
+ "@esbuild/win32-ia32": "0.27.0",
1355
+ "@esbuild/win32-x64": "0.27.0"
1356
+ }
1357
+ },
1358
+ "node_modules/fsevents": {
1359
+ "version": "2.3.3",
1360
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1361
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1362
+ "dev": true,
1363
+ "hasInstallScript": true,
1364
+ "license": "MIT",
1365
+ "optional": true,
1366
+ "os": [
1367
+ "darwin"
1368
+ ],
1369
+ "engines": {
1370
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1371
+ }
1372
+ },
1373
+ "node_modules/kleur": {
1374
+ "version": "4.1.5",
1375
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
1376
+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
1377
+ "dev": true,
1378
+ "license": "MIT",
1379
+ "engines": {
1380
+ "node": ">=6"
1381
+ }
1382
+ },
1383
+ "node_modules/miniflare": {
1384
+ "version": "4.20260128.0",
1385
+ "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260128.0.tgz",
1386
+ "integrity": "sha512-AVCn3vDRY+YXu1sP4mRn81ssno6VUqxo29uY2QVfgxXU2TMLvhRIoGwm7RglJ3Gzfuidit5R86CMQ6AvdFTGAw==",
1387
+ "dev": true,
1388
+ "license": "MIT",
1389
+ "dependencies": {
1390
+ "@cspotcode/source-map-support": "0.8.1",
1391
+ "sharp": "^0.34.5",
1392
+ "undici": "7.18.2",
1393
+ "workerd": "1.20260128.0",
1394
+ "ws": "8.18.0",
1395
+ "youch": "4.1.0-beta.10"
1396
+ },
1397
+ "bin": {
1398
+ "miniflare": "bootstrap.js"
1399
+ },
1400
+ "engines": {
1401
+ "node": ">=18.0.0"
1402
+ }
1403
+ },
1404
+ "node_modules/miniflare/node_modules/ws": {
1405
+ "version": "8.18.0",
1406
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
1407
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
1408
+ "dev": true,
1409
+ "license": "MIT",
1410
+ "engines": {
1411
+ "node": ">=10.0.0"
1412
+ },
1413
+ "peerDependencies": {
1414
+ "bufferutil": "^4.0.1",
1415
+ "utf-8-validate": ">=5.0.2"
1416
+ },
1417
+ "peerDependenciesMeta": {
1418
+ "bufferutil": {
1419
+ "optional": true
1420
+ },
1421
+ "utf-8-validate": {
1422
+ "optional": true
1423
+ }
1424
+ }
1425
+ },
1426
+ "node_modules/path-to-regexp": {
1427
+ "version": "6.3.0",
1428
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
1429
+ "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
1430
+ "dev": true,
1431
+ "license": "MIT"
1432
+ },
1433
+ "node_modules/pathe": {
1434
+ "version": "2.0.3",
1435
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
1436
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
1437
+ "dev": true,
1438
+ "license": "MIT"
1439
+ },
1440
+ "node_modules/prettier": {
1441
+ "version": "3.8.1",
1442
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
1443
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
1444
+ "dev": true,
1445
+ "license": "MIT",
1446
+ "bin": {
1447
+ "prettier": "bin/prettier.cjs"
1448
+ },
1449
+ "engines": {
1450
+ "node": ">=14"
1451
+ },
1452
+ "funding": {
1453
+ "url": "https://github.com/prettier/prettier?sponsor=1"
1454
+ }
1455
+ },
1456
+ "node_modules/semver": {
1457
+ "version": "7.7.3",
1458
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
1459
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
1460
+ "dev": true,
1461
+ "license": "ISC",
1462
+ "bin": {
1463
+ "semver": "bin/semver.js"
1464
+ },
1465
+ "engines": {
1466
+ "node": ">=10"
1467
+ }
1468
+ },
1469
+ "node_modules/sharp": {
1470
+ "version": "0.34.5",
1471
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
1472
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
1473
+ "dev": true,
1474
+ "hasInstallScript": true,
1475
+ "license": "Apache-2.0",
1476
+ "dependencies": {
1477
+ "@img/colour": "^1.0.0",
1478
+ "detect-libc": "^2.1.2",
1479
+ "semver": "^7.7.3"
1480
+ },
1481
+ "engines": {
1482
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
1483
+ },
1484
+ "funding": {
1485
+ "url": "https://opencollective.com/libvips"
1486
+ },
1487
+ "optionalDependencies": {
1488
+ "@img/sharp-darwin-arm64": "0.34.5",
1489
+ "@img/sharp-darwin-x64": "0.34.5",
1490
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
1491
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
1492
+ "@img/sharp-libvips-linux-arm": "1.2.4",
1493
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
1494
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
1495
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
1496
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
1497
+ "@img/sharp-libvips-linux-x64": "1.2.4",
1498
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
1499
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
1500
+ "@img/sharp-linux-arm": "0.34.5",
1501
+ "@img/sharp-linux-arm64": "0.34.5",
1502
+ "@img/sharp-linux-ppc64": "0.34.5",
1503
+ "@img/sharp-linux-riscv64": "0.34.5",
1504
+ "@img/sharp-linux-s390x": "0.34.5",
1505
+ "@img/sharp-linux-x64": "0.34.5",
1506
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
1507
+ "@img/sharp-linuxmusl-x64": "0.34.5",
1508
+ "@img/sharp-wasm32": "0.34.5",
1509
+ "@img/sharp-win32-arm64": "0.34.5",
1510
+ "@img/sharp-win32-ia32": "0.34.5",
1511
+ "@img/sharp-win32-x64": "0.34.5"
1512
+ }
1513
+ },
1514
+ "node_modules/supports-color": {
1515
+ "version": "10.2.2",
1516
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
1517
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
1518
+ "dev": true,
1519
+ "license": "MIT",
1520
+ "engines": {
1521
+ "node": ">=18"
1522
+ },
1523
+ "funding": {
1524
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
1525
+ }
1526
+ },
1527
+ "node_modules/tslib": {
1528
+ "version": "2.8.1",
1529
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
1530
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
1531
+ "dev": true,
1532
+ "license": "0BSD",
1533
+ "optional": true
1534
+ },
1535
+ "node_modules/typescript": {
1536
+ "version": "5.9.3",
1537
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
1538
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
1539
+ "dev": true,
1540
+ "license": "Apache-2.0",
1541
+ "bin": {
1542
+ "tsc": "bin/tsc",
1543
+ "tsserver": "bin/tsserver"
1544
+ },
1545
+ "engines": {
1546
+ "node": ">=14.17"
1547
+ }
1548
+ },
1549
+ "node_modules/undici": {
1550
+ "version": "7.18.2",
1551
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.18.2.tgz",
1552
+ "integrity": "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==",
1553
+ "dev": true,
1554
+ "license": "MIT",
1555
+ "engines": {
1556
+ "node": ">=20.18.1"
1557
+ }
1558
+ },
1559
+ "node_modules/undici-types": {
1560
+ "version": "7.16.0",
1561
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
1562
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
1563
+ "dev": true,
1564
+ "license": "MIT"
1565
+ },
1566
+ "node_modules/unenv": {
1567
+ "version": "2.0.0-rc.24",
1568
+ "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz",
1569
+ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==",
1570
+ "dev": true,
1571
+ "license": "MIT",
1572
+ "dependencies": {
1573
+ "pathe": "^2.0.3"
1574
+ }
1575
+ },
1576
+ "node_modules/workerd": {
1577
+ "version": "1.20260128.0",
1578
+ "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260128.0.tgz",
1579
+ "integrity": "sha512-EhLJGptSGFi8AEErLiamO3PoGpbRqL+v4Ve36H2B38VxmDgFOSmDhfepBnA14sCQzGf1AEaoZX2DCwZsmO74yQ==",
1580
+ "dev": true,
1581
+ "hasInstallScript": true,
1582
+ "license": "Apache-2.0",
1583
+ "bin": {
1584
+ "workerd": "bin/workerd"
1585
+ },
1586
+ "engines": {
1587
+ "node": ">=16"
1588
+ },
1589
+ "optionalDependencies": {
1590
+ "@cloudflare/workerd-darwin-64": "1.20260128.0",
1591
+ "@cloudflare/workerd-darwin-arm64": "1.20260128.0",
1592
+ "@cloudflare/workerd-linux-64": "1.20260128.0",
1593
+ "@cloudflare/workerd-linux-arm64": "1.20260128.0",
1594
+ "@cloudflare/workerd-windows-64": "1.20260128.0"
1595
+ }
1596
+ },
1597
+ "node_modules/wrangler": {
1598
+ "version": "4.61.1",
1599
+ "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.61.1.tgz",
1600
+ "integrity": "sha512-hfYQ16VLPkNi8xE1/V3052S2stM5e+vq3Idpt83sXoDC3R7R1CLgMkK6M6+Qp3G+9GVDNyHCkvohMPdfFTaD4Q==",
1601
+ "dev": true,
1602
+ "license": "MIT OR Apache-2.0",
1603
+ "dependencies": {
1604
+ "@cloudflare/kv-asset-handler": "0.4.2",
1605
+ "@cloudflare/unenv-preset": "2.12.0",
1606
+ "blake3-wasm": "2.1.5",
1607
+ "esbuild": "0.27.0",
1608
+ "miniflare": "4.20260128.0",
1609
+ "path-to-regexp": "6.3.0",
1610
+ "unenv": "2.0.0-rc.24",
1611
+ "workerd": "1.20260128.0"
1612
+ },
1613
+ "bin": {
1614
+ "wrangler": "bin/wrangler.js",
1615
+ "wrangler2": "bin/wrangler.js"
1616
+ },
1617
+ "engines": {
1618
+ "node": ">=20.0.0"
1619
+ },
1620
+ "optionalDependencies": {
1621
+ "fsevents": "~2.3.2"
1622
+ },
1623
+ "peerDependencies": {
1624
+ "@cloudflare/workers-types": "^4.20260128.0"
1625
+ },
1626
+ "peerDependenciesMeta": {
1627
+ "@cloudflare/workers-types": {
1628
+ "optional": true
1629
+ }
1630
+ }
1631
+ },
1632
+ "node_modules/ws": {
1633
+ "version": "8.19.0",
1634
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
1635
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
1636
+ "license": "MIT",
1637
+ "engines": {
1638
+ "node": ">=10.0.0"
1639
+ },
1640
+ "peerDependencies": {
1641
+ "bufferutil": "^4.0.1",
1642
+ "utf-8-validate": ">=5.0.2"
1643
+ },
1644
+ "peerDependenciesMeta": {
1645
+ "bufferutil": {
1646
+ "optional": true
1647
+ },
1648
+ "utf-8-validate": {
1649
+ "optional": true
1650
+ }
1651
+ }
1652
+ },
1653
+ "node_modules/youch": {
1654
+ "version": "4.1.0-beta.10",
1655
+ "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz",
1656
+ "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==",
1657
+ "dev": true,
1658
+ "license": "MIT",
1659
+ "dependencies": {
1660
+ "@poppinss/colors": "^4.1.5",
1661
+ "@poppinss/dumper": "^0.6.4",
1662
+ "@speed-highlight/core": "^1.2.7",
1663
+ "cookie": "^1.0.2",
1664
+ "youch-core": "^0.3.3"
1665
+ }
1666
+ },
1667
+ "node_modules/youch-core": {
1668
+ "version": "0.3.3",
1669
+ "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz",
1670
+ "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==",
1671
+ "dev": true,
1672
+ "license": "MIT",
1673
+ "dependencies": {
1674
+ "@poppinss/exception": "^1.2.2",
1675
+ "error-stack-parser-es": "^1.0.5"
1676
+ }
1677
+ }
1678
+ }
1679
+ }
package.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "sockets",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "dev": "npm run build && npm run start",
7
+ "start": "node dist/index.js",
8
+ "build": "tsc",
9
+ "test": "echo \"Error: no test specified\" && exit 1"
10
+ },
11
+ "keywords": [],
12
+ "type": "module",
13
+ "author": "",
14
+ "license": "ISC",
15
+ "description": "",
16
+ "dependencies": {
17
+ "ws": "^8.19.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/express": "^5.0.6",
21
+ "@types/node": "^25.1.0",
22
+ "@types/ws": "^8.18.1",
23
+ "prettier": "^3.8.1",
24
+ "typescript": "^5.9.3",
25
+ "wrangler": "^4.61.1"
26
+ }
27
+ }
src/global.d.ts ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ declare module 'cloudflare:node' {
2
+ import type { IncomingMessage, ServerResponse } from 'node:http'
3
+
4
+ export function httpServerHandler(
5
+ request: Request,
6
+ handler: (req: IncomingMessage, res: ServerResponse) => void
7
+ ): Promise<Response>
8
+ }
src/handlers/MessageHandler.ts ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WebSocket from 'ws'
2
+ import { RoomManager } from '../services/RoomManager.js'
3
+ import { SocketAction } from '../types.js'
4
+
5
+ export class MessageHandler {
6
+ static handle(
7
+ rawMessage: string,
8
+ socket: WebSocket,
9
+ roomManager: RoomManager
10
+ ): void {
11
+ let parsedData: any
12
+
13
+ try {
14
+ parsedData = JSON.parse(rawMessage)
15
+ } catch (error) {
16
+ console.error('Error parsing JSON:', error)
17
+ socket.send(JSON.stringify({ error: 'Invalid JSON', status: 0 }))
18
+ return
19
+ }
20
+
21
+ // Basic validation to ensure required fields exist
22
+ if (!parsedData.room_id || !parsedData.username || !parsedData.action) {
23
+ socket.send(
24
+ JSON.stringify({
25
+ error: 'Missing fields: room_id, username, or action',
26
+ status: 0,
27
+ })
28
+ )
29
+ return
30
+ }
31
+
32
+ const { room_id, username, message } = parsedData
33
+ // Normalize action to ensure case-insensitivity matches logic
34
+ const action = parsedData.action.toLowerCase()
35
+
36
+ switch (action) {
37
+ case SocketAction.JOIN:
38
+ roomManager.joinRoom(room_id, username, socket)
39
+ roomManager.broadcast(
40
+ room_id,
41
+ JSON.stringify({
42
+ message: `${username} has joined the ${room_id} chat!`,
43
+ username: `${username}`,
44
+ room_id: `${room_id}`,
45
+ action: `join`,
46
+ status: 1,
47
+ })
48
+ )
49
+ break
50
+
51
+ case SocketAction.LEAVE:
52
+ const clientLeft = roomManager.leaveRoom(room_id, username)
53
+ if (clientLeft) {
54
+ roomManager.broadcast(
55
+ room_id,
56
+ JSON.stringify({
57
+ message: `${username} has left the ${room_id} chat!`,
58
+ username: `${username}`,
59
+ room_id: `${room_id}`,
60
+ action: `leave`,
61
+ status: 1,
62
+ })
63
+ )
64
+ }
65
+ break
66
+
67
+ case SocketAction.MESSAGE:
68
+ roomManager.broadcast(
69
+ room_id,
70
+ JSON.stringify({
71
+ message: `${message}`,
72
+ username: `${username}`,
73
+ room_id: `${room_id}`,
74
+ action: `message`,
75
+ status: 1,
76
+ })
77
+ )
78
+ break
79
+
80
+ default:
81
+ socket.send(
82
+ JSON.stringify({
83
+ error: `Unknown action: ${action}`,
84
+ status: 0,
85
+ })
86
+ )
87
+ break
88
+ }
89
+ }
90
+ }
src/index.ts ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import http from 'http'
2
+ import { WebSocketServer } from 'ws'
3
+ import { RoomManager } from './services/RoomManager.js'
4
+ import { MessageHandler } from './handlers/MessageHandler.js'
5
+
6
+ const roomManager = new RoomManager()
7
+ type wsData = Buffer | ArrayBuffer | Buffer[] | string
8
+
9
+ const PORT = process.env.PORT || 7860
10
+
11
+ const server = http.createServer((req, res) => {
12
+ // Handle simple GET /
13
+ if (req.method === 'GET' && req.url === '/') {
14
+ res.writeHead(200, { 'Content-Type': 'text/plain' })
15
+ res.end('Hello from WebSocket server without Express!')
16
+ } else {
17
+ res.writeHead(404, { 'Content-Type': 'text/plain' })
18
+ res.end('Not Found')
19
+ }
20
+ })
21
+
22
+ const wss = new WebSocketServer({ server })
23
+
24
+ wss.on('connection', (ws) => {
25
+ console.log('connected to user')
26
+
27
+ ws.on('message', (msg: wsData) => {
28
+ MessageHandler.handle(msg.toString(), ws, roomManager)
29
+ })
30
+
31
+ ws.on('close', () => {
32
+ console.log('connection closed!')
33
+ })
34
+ })
35
+
36
+ server.listen(PORT, () => {
37
+ console.log(`Server running on http://localhost:${PORT}`)
38
+ })
src/services/RoomManager.ts ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WebSocket from 'ws'
2
+
3
+ export class RoomManager {
4
+ // Map<RoomId, Map<Username, WebSocket>>
5
+ private rooms: Map<string, Map<string, WebSocket>>
6
+
7
+ constructor() {
8
+ this.rooms = new Map()
9
+ }
10
+
11
+ /**
12
+ * Adds a user to a specific room.
13
+ * Creates the room if it doesn't exist.
14
+ */
15
+ joinRoom(roomId: string, username: string, socket: WebSocket): void {
16
+ if (!this.rooms.has(roomId)) {
17
+ this.rooms.set(roomId, new Map())
18
+ }
19
+ this.rooms.get(roomId)?.set(username, socket)
20
+ }
21
+
22
+ /**
23
+ * Removes a user from a room.
24
+ * Cleans up the room if it becomes empty.
25
+ */
26
+ leaveRoom(roomId: string, username: string): boolean {
27
+ const room = this.rooms.get(roomId)
28
+ if (room) {
29
+ const deleted = room.delete(username)
30
+ if (room.size === 0) {
31
+ this.rooms.delete(roomId)
32
+ }
33
+ return deleted
34
+ }
35
+ return false
36
+ }
37
+
38
+ /**
39
+ * Checks if a room exists.
40
+ */
41
+ hasRoom(roomId: string): boolean {
42
+ return this.rooms.has(roomId)
43
+ }
44
+
45
+ /**
46
+ * Sends a message to all users in a specific room.
47
+ */
48
+ broadcast(roomId: string, message: string): void {
49
+ const room = this.rooms.get(roomId)
50
+ if (!room) return
51
+
52
+ for (const client of room.values()) {
53
+ if (client.readyState === WebSocket.OPEN) {
54
+ client.send(message)
55
+ }
56
+ }
57
+ }
58
+ }
src/types.ts ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export enum SocketAction {
2
+ JOIN = 'join',
3
+ LEAVE = 'leave',
4
+ MESSAGE = 'message',
5
+ }
6
+
7
+ export interface WebSocketMessage {
8
+ room_id: string
9
+ username: string
10
+ message: string
11
+ action: SocketAction
12
+ }
tsconfig.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ // Visit https://aka.ms/tsconfig to read more about this file
3
+ "compilerOptions": {
4
+ // File Layout
5
+ "rootDir": "./src",
6
+ "outDir": "./dist",
7
+
8
+ // Environment Settings
9
+ // See also https://aka.ms/tsconfig/module
10
+ "module": "nodenext",
11
+ "target": "esnext",
12
+ // For nodejs:
13
+ "lib": ["esnext"],
14
+ "types": ["node"],
15
+ // and npm install -D @types/node
16
+
17
+ // Other Outputs
18
+ "sourceMap": true,
19
+ "declaration": true,
20
+ "declarationMap": true,
21
+
22
+ // Stricter Typechecking Options
23
+ "noUncheckedIndexedAccess": true,
24
+ "exactOptionalPropertyTypes": true,
25
+
26
+ // Style Options
27
+ // "noImplicitReturns": true,
28
+ // "noImplicitOverride": true,
29
+ // "noUnusedLocals": true,
30
+ // "noUnusedParameters": true,
31
+ // "noFallthroughCasesInSwitch": true,
32
+ // "noPropertyAccessFromIndexSignature": true,
33
+
34
+ // Recommended Options
35
+ "strict": true,
36
+ "jsx": "react-jsx",
37
+ "verbatimModuleSyntax": true,
38
+ "isolatedModules": true,
39
+ "noUncheckedSideEffectImports": true,
40
+ "moduleDetection": "force",
41
+ "skipLibCheck": true
42
+ },
43
+ "include": ["src/**/*.ts"],
44
+ "exclude": ["node_modules"]
45
+ }