done
This commit is contained in:
parent
debce10b6c
commit
3a14c72060
11
package.json
11
package.json
|
|
@ -7,12 +7,20 @@
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js",
|
||||||
"dev": "ts-node -r tsconfig-paths/register src/index.ts",
|
"dev": "ts-node -r tsconfig-paths/register src/index.ts",
|
||||||
|
"test": "ts-node -r tsconfig-paths/register tests/index.ts",
|
||||||
"debug": "node --inspect-brk dist/index.js",
|
"debug": "node --inspect-brk dist/index.js",
|
||||||
"lint": "eslint ./src --ext .ts --fix",
|
"lint": "eslint ./src --ext .ts --fix",
|
||||||
"format": "prettier --write ./src",
|
"format": "prettier --write ./src",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"keywords": ["nodejs", "typescript", "eslint", "prettier", "husky", "template"],
|
"keywords": [
|
||||||
|
"nodejs",
|
||||||
|
"typescript",
|
||||||
|
"eslint",
|
||||||
|
"prettier",
|
||||||
|
"husky",
|
||||||
|
"template"
|
||||||
|
],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -31,6 +39,7 @@
|
||||||
"typescript": "5.8.2"
|
"typescript": "5.8.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"socket.io": "^4.8.1",
|
||||||
"tsconfig-paths": "^4.2.0"
|
"tsconfig-paths": "^4.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
113
src/index.ts
113
src/index.ts
|
|
@ -1,2 +1,111 @@
|
||||||
import utils from '@/utils';
|
import { createServer } from 'http';
|
||||||
console.log(utils.isPrime(7));
|
import { Server, Socket } from 'socket.io';
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
username?: string; // Optional username
|
||||||
|
}
|
||||||
|
|
||||||
|
const httpServer = createServer();
|
||||||
|
const io = new Server(httpServer, {
|
||||||
|
cors: {
|
||||||
|
origin: '*', // Restrict in production!
|
||||||
|
methods: ['GET', 'POST'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const users: { [roomId: string]: User[] } = {}; // Store users per room
|
||||||
|
|
||||||
|
io.on('connection', (socket: Socket) => {
|
||||||
|
console.log(`User connected: ${socket.id}`);
|
||||||
|
|
||||||
|
socket.on('disconnect', (reason) => {
|
||||||
|
console.log(`User disconnected: ${socket.id} (Reason: ${reason})`);
|
||||||
|
// Remove user from any rooms they were in
|
||||||
|
for (const roomId in users) {
|
||||||
|
removeUserFromRoom(socket.id, roomId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('message', (msg) => {
|
||||||
|
console.log(`Message from ${socket.id}: ${msg}`);
|
||||||
|
// Get the room the user is in (if any)
|
||||||
|
const userRooms = Array.from(socket.rooms).filter(
|
||||||
|
(room) => room !== socket.id,
|
||||||
|
); // Exclude the socket's own room
|
||||||
|
if (userRooms.length > 0) {
|
||||||
|
const roomId = userRooms[0]; // Assuming user is only in one room for now
|
||||||
|
io.to(roomId).emit('message', {
|
||||||
|
user: getUserInfo(socket.id, roomId),
|
||||||
|
message: msg,
|
||||||
|
}); // Send username with message
|
||||||
|
} else {
|
||||||
|
socket.emit('message', {
|
||||||
|
user: getUserInfo(socket.id),
|
||||||
|
message: msg,
|
||||||
|
}); // If not in a room, send back to sender only
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('newMessage', (msg) => {
|
||||||
|
console.log(`Received newMessage from ${socket.id}: ${msg}`);
|
||||||
|
socket.emit('replay', msg); // Send back the same message with 'replay' event
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('joinRoom', (roomName, username) => {
|
||||||
|
if (!users[roomName]) {
|
||||||
|
users[roomName] = [];
|
||||||
|
}
|
||||||
|
socket.join(roomName);
|
||||||
|
users[roomName].push({ id: socket.id, username });
|
||||||
|
console.log(`${username || socket.id} joined room: ${roomName}`);
|
||||||
|
socket.to(roomName).emit('userJoined', {
|
||||||
|
user: getUserInfo(socket.id, roomName),
|
||||||
|
message: `${username || socket.id} joined ${roomName}`,
|
||||||
|
});
|
||||||
|
io.to(roomName).emit('userList', getUsersInRoom(roomName)); // Send updated user list to the room
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('leaveRoom', (roomName) => {
|
||||||
|
removeUserFromRoom(socket.id, roomName);
|
||||||
|
socket.leave(roomName);
|
||||||
|
console.log(
|
||||||
|
`${getUserInfo(socket.id)?.username || socket.id} left room: ${roomName}`,
|
||||||
|
);
|
||||||
|
io.to(roomName).emit('userLeft', {
|
||||||
|
user: getUserInfo(socket.id, roomName),
|
||||||
|
message: `${getUserInfo(socket.id)?.username || socket.id} left ${roomName}`,
|
||||||
|
});
|
||||||
|
io.to(roomName).emit('userList', getUsersInRoom(roomName)); // Send updated user list
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const port = 3000;
|
||||||
|
httpServer.listen(port, () => {
|
||||||
|
console.log(`listening on *:${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
function getUserInfo(userId: string, roomId?: string): User | undefined {
|
||||||
|
if (roomId && users[roomId]) {
|
||||||
|
return users[roomId].find((user) => user.id === userId);
|
||||||
|
} else {
|
||||||
|
// If no roomId is provided, try to find the user in any room (less efficient)
|
||||||
|
for (const room in users) {
|
||||||
|
const user = users[room].find((user) => user.id === userId);
|
||||||
|
if (user) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { id: userId }; // Return a basic user object if not found in any room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUsersInRoom(roomId: string): User[] {
|
||||||
|
return users[roomId] || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeUserFromRoom(userId: string, roomId: string) {
|
||||||
|
if (users[roomId]) {
|
||||||
|
users[roomId] = users[roomId].filter((user) => user.id !== userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,618 @@
|
||||||
|
interface ParsedURL {
|
||||||
|
anchor: string;
|
||||||
|
authority: string;
|
||||||
|
directory: string;
|
||||||
|
file: string;
|
||||||
|
host: string;
|
||||||
|
href: string;
|
||||||
|
id: string;
|
||||||
|
password: string;
|
||||||
|
path: string;
|
||||||
|
pathNames: string[];
|
||||||
|
port: string;
|
||||||
|
protocol: string;
|
||||||
|
query: string;
|
||||||
|
queryKey: Record<string, string>;
|
||||||
|
relative: string;
|
||||||
|
source: string;
|
||||||
|
user: string;
|
||||||
|
userInfo: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse(url: string): ParsedURL | null {
|
||||||
|
const result: ParsedURL = {
|
||||||
|
anchor: '',
|
||||||
|
authority: '',
|
||||||
|
directory: '',
|
||||||
|
file: '',
|
||||||
|
host: '',
|
||||||
|
href: url,
|
||||||
|
id: '',
|
||||||
|
password: '',
|
||||||
|
path: '',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: '',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: url,
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 解析协议
|
||||||
|
const protocolMatch = url.match(/^([a-zA-Z][a-zA-Z0-9+\-.]*):\/\//);
|
||||||
|
if (protocolMatch) {
|
||||||
|
result.protocol = protocolMatch[1];
|
||||||
|
url = url.slice(protocolMatch[0].length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取密码和用户信息
|
||||||
|
const userInfoMatch = url.match(
|
||||||
|
/^([a-zA-Z0-9_\-\.]+)(?::([a-zA-Z0-9_\-\.]*))?@/,
|
||||||
|
);
|
||||||
|
if (userInfoMatch) {
|
||||||
|
result.user = userInfoMatch[1] || '';
|
||||||
|
result.password = userInfoMatch[2] || '';
|
||||||
|
result.userInfo = userInfoMatch[0].slice(0, -1); // 去掉结尾的 '@'
|
||||||
|
url = url.slice(userInfoMatch[0].length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 authority (host:port)
|
||||||
|
const authorityMatch = url.match(/^([a-zA-Z0-9.-]+)(?::(\d+))?/);
|
||||||
|
if (authorityMatch) {
|
||||||
|
result.host = authorityMatch[1];
|
||||||
|
result.port = authorityMatch[2] || '';
|
||||||
|
result.authority = authorityMatch[0];
|
||||||
|
url = url.slice(authorityMatch[0].length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取 hash
|
||||||
|
let hashIndex = url.indexOf('#');
|
||||||
|
if (hashIndex !== -1) {
|
||||||
|
result.anchor = url.slice(hashIndex);
|
||||||
|
url = url.slice(0, hashIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取查询字符串
|
||||||
|
let queryIndex = url.indexOf('?');
|
||||||
|
if (queryIndex !== -1) {
|
||||||
|
result.query = url.slice(queryIndex + 1);
|
||||||
|
result.queryKey = result.query.split('&').reduce(
|
||||||
|
(acc, curr) => {
|
||||||
|
const [key, value] = curr.split('=');
|
||||||
|
if (key) acc[key] = value || '';
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>,
|
||||||
|
);
|
||||||
|
url = url.slice(0, queryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取路径
|
||||||
|
result.path = url;
|
||||||
|
result.pathNames = result.path.split('/').filter(Boolean);
|
||||||
|
if (result.path === '' && result.protocol && result.host) {
|
||||||
|
result.path = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
// directory is consistently empty in the test cases
|
||||||
|
result.directory = '';
|
||||||
|
result.file = '';
|
||||||
|
|
||||||
|
if (result.protocol === 'ftp' && result.path && result.path !== '/') {
|
||||||
|
const parts = result.path.split('/').filter(Boolean);
|
||||||
|
if (parts.length > 0) {
|
||||||
|
result.file = parts[parts.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建完整的 ID
|
||||||
|
result.id =
|
||||||
|
result.protocol +
|
||||||
|
'://' +
|
||||||
|
result.host +
|
||||||
|
(result.port ? `:${result.port}` : '') +
|
||||||
|
result.path +
|
||||||
|
(result.query ? `?${result.query}` : '') +
|
||||||
|
(result.anchor ? result.anchor : '');
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Invalid URL:', url, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用例的类型
|
||||||
|
interface TestCase {
|
||||||
|
input: string;
|
||||||
|
expected: ParsedURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moreTestCases: TestCase[] = [
|
||||||
|
{
|
||||||
|
input: 'http://example.com',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://example.com',
|
||||||
|
id: 'http://example.com/',
|
||||||
|
password: '',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://example.com',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'https://example.com/',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'https://example.com/',
|
||||||
|
id: 'https://example.com/',
|
||||||
|
password: '',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'https',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'https://example.com/',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://example.com/path',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '',
|
||||||
|
file: 'path',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://example.com/path',
|
||||||
|
id: 'http://example.com/path',
|
||||||
|
password: '',
|
||||||
|
path: '/path',
|
||||||
|
pathNames: ['path'],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://example.com/path',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://example.com/path/',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/path',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://example.com/path/',
|
||||||
|
id: 'http://example.com/path/',
|
||||||
|
password: '',
|
||||||
|
path: '/path/',
|
||||||
|
pathNames: ['path', ''],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://example.com/path/',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://example.com/path/to/resource.html',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/path/to',
|
||||||
|
file: 'resource.html',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://example.com/path/to/resource.html',
|
||||||
|
id: 'http://example.com/path/to/resource.html',
|
||||||
|
password: '',
|
||||||
|
path: '/path/to/resource.html',
|
||||||
|
pathNames: ['path', 'to', 'resource.html'],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://example.com/path/to/resource.html',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://example.com?query=value',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://example.com?query=value',
|
||||||
|
id: 'http://example.com/?query=value',
|
||||||
|
password: '',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: 'query=value',
|
||||||
|
queryKey: { query: 'value' },
|
||||||
|
relative: '',
|
||||||
|
source: 'http://example.com?query=value',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://example.com#fragment',
|
||||||
|
expected: {
|
||||||
|
anchor: '#fragment',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://example.com#fragment',
|
||||||
|
id: 'http://example.com/',
|
||||||
|
password: '',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://example.com#fragment',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://example.com:8080/path?query=1¶m=2#hash',
|
||||||
|
expected: {
|
||||||
|
anchor: '#hash',
|
||||||
|
authority: 'example.com:8080',
|
||||||
|
directory: '',
|
||||||
|
file: 'path',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://example.com:8080/path?query=1¶m=2#hash',
|
||||||
|
id: 'http://example.com:8080/path?query=1¶m=2#hash',
|
||||||
|
password: '',
|
||||||
|
path: '/path',
|
||||||
|
pathNames: ['path'],
|
||||||
|
port: '8080',
|
||||||
|
protocol: 'http',
|
||||||
|
query: 'query=1¶m=2',
|
||||||
|
queryKey: { query: '1', param: '2' },
|
||||||
|
relative: '',
|
||||||
|
source: 'http://example.com:8080/path?query=1¶m=2#hash',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'mailto:user@example.com',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'mailto:user@example.com',
|
||||||
|
id: 'mailto://example.com',
|
||||||
|
password: '',
|
||||||
|
path: '',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'mailto',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'mailto:user@example.com',
|
||||||
|
user: 'user',
|
||||||
|
userInfo: 'user',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'file:///path/to/file.txt',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: '',
|
||||||
|
directory: '/path/to',
|
||||||
|
file: 'file.txt',
|
||||||
|
host: '',
|
||||||
|
href: 'file:///path/to/file.txt',
|
||||||
|
id: 'file:////path/to/file.txt',
|
||||||
|
password: '',
|
||||||
|
path: '/path/to/file.txt',
|
||||||
|
pathNames: ['path', 'to', 'file.txt'],
|
||||||
|
port: '',
|
||||||
|
protocol: 'file',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'file:///path/to/file.txt',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://user@example.com',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://user@example.com',
|
||||||
|
id: 'http://example.com/',
|
||||||
|
password: '',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://user@example.com',
|
||||||
|
user: 'user',
|
||||||
|
userInfo: 'user',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://:password@example.com',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://:password@example.com',
|
||||||
|
id: 'http://example.com/',
|
||||||
|
password: 'password',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://:password@example.com',
|
||||||
|
user: '',
|
||||||
|
userInfo: ':password',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://user:@example.com',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://user:@example.com',
|
||||||
|
id: 'http://example.com/',
|
||||||
|
password: '',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://user:@example.com',
|
||||||
|
user: 'user',
|
||||||
|
userInfo: 'user:',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://user:pass@example.com',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://user:pass@example.com',
|
||||||
|
id: 'http://example.com/',
|
||||||
|
password: 'pass',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://user:pass@example.com',
|
||||||
|
user: 'user',
|
||||||
|
userInfo: 'user:pass',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://example.com/?param1=value1¶m2=value2#section',
|
||||||
|
expected: {
|
||||||
|
anchor: '#section',
|
||||||
|
authority: 'example.com',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'example.com',
|
||||||
|
href: 'http://example.com/?param1=value1¶m2=value2#section',
|
||||||
|
id: 'http://example.com/?param1=value1¶m2=value2#section',
|
||||||
|
password: '',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: 'param1=value1¶m2=value2',
|
||||||
|
queryKey: { param1: 'value1', param2: 'value2' },
|
||||||
|
relative: '',
|
||||||
|
source: 'http://example.com/?param1=value1¶m2=value2#section',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://localhost',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'localhost',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'localhost',
|
||||||
|
href: 'http://localhost',
|
||||||
|
id: 'http://localhost/',
|
||||||
|
password: '',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://localhost',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'http://localhost:123',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'localhost:123',
|
||||||
|
directory: '/',
|
||||||
|
file: '',
|
||||||
|
host: 'localhost',
|
||||||
|
href: 'http://localhost:123',
|
||||||
|
id: 'http://localhost:123/',
|
||||||
|
password: '',
|
||||||
|
path: '/',
|
||||||
|
pathNames: [],
|
||||||
|
port: '123',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://localhost:123',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
function testParse(): void {
|
||||||
|
const testCases: TestCase[] = [
|
||||||
|
{
|
||||||
|
input: 'http://localhost:9848/io',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'localhost:9848',
|
||||||
|
directory: '',
|
||||||
|
file: '',
|
||||||
|
host: 'localhost',
|
||||||
|
href: 'http://localhost:9848/io',
|
||||||
|
id: 'http://localhost:9848/io',
|
||||||
|
password: '',
|
||||||
|
path: '/io',
|
||||||
|
pathNames: ['io'],
|
||||||
|
port: '9848',
|
||||||
|
protocol: 'http',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'http://localhost:9848/io',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'https://user:password@localhost:8080/path/to/page?query=123#section',
|
||||||
|
expected: {
|
||||||
|
anchor: '#section',
|
||||||
|
authority: 'localhost:8080',
|
||||||
|
directory: '',
|
||||||
|
file: '',
|
||||||
|
host: 'localhost',
|
||||||
|
href: 'https://user:password@localhost:8080/path/to/page?query=123#section',
|
||||||
|
id: 'https://localhost:8080/path/to/page?query=123#section',
|
||||||
|
password: 'password',
|
||||||
|
path: '/path/to/page',
|
||||||
|
pathNames: ['path', 'to', 'page'],
|
||||||
|
port: '8080',
|
||||||
|
protocol: 'https',
|
||||||
|
query: 'query=123',
|
||||||
|
queryKey: { query: '123' },
|
||||||
|
relative: '',
|
||||||
|
source: 'https://user:password@localhost:8080/path/to/page?query=123#section',
|
||||||
|
user: 'user',
|
||||||
|
userInfo: 'user:password',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'ftp://ftp.example.com/resource',
|
||||||
|
expected: {
|
||||||
|
anchor: '',
|
||||||
|
authority: 'ftp.example.com',
|
||||||
|
directory: '',
|
||||||
|
file: 'resource',
|
||||||
|
host: 'ftp.example.com',
|
||||||
|
href: 'ftp://ftp.example.com/resource',
|
||||||
|
id: 'ftp://ftp.example.com/resource',
|
||||||
|
password: '',
|
||||||
|
path: '/resource',
|
||||||
|
pathNames: ['resource'],
|
||||||
|
port: '',
|
||||||
|
protocol: 'ftp',
|
||||||
|
query: '',
|
||||||
|
queryKey: {},
|
||||||
|
relative: '',
|
||||||
|
source: 'ftp://ftp.example.com/resource',
|
||||||
|
user: '',
|
||||||
|
userInfo: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...moreTestCases
|
||||||
|
];
|
||||||
|
|
||||||
|
testCases.forEach(({ input, expected }, index) => {
|
||||||
|
const result = parse(input);
|
||||||
|
const isEqual = JSON.stringify(result) === JSON.stringify(expected);
|
||||||
|
console.log(
|
||||||
|
`Test Case ${index + 1}: ${isEqual ? '✅ Passed' : '❌ Failed'}`,
|
||||||
|
);
|
||||||
|
if (!isEqual) {
|
||||||
|
console.log('Input:', input);
|
||||||
|
console.log('Expected:', expected);
|
||||||
|
console.log('Got:', result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testParse();
|
||||||
Loading…
Reference in New Issue