Skip to content

Commit 7b618a4

Browse files
initial commit
0 parents  commit 7b618a4

File tree

21 files changed

+2795
-0
lines changed

21 files changed

+2795
-0
lines changed

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- '**'
7+
pull_request:
8+
schedule:
9+
- cron: '0 0 * * 0'
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
test-bun:
16+
runs-on: ubuntu-latest
17+
timeout-minutes: 10
18+
19+
strategy:
20+
fail-fast: false
21+
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v4
25+
26+
- name: Install Bun
27+
uses: oven-sh/setup-bun@v2
28+
29+
- name: Install dependencies
30+
run: bun install
31+
32+
- name: Run tests
33+
run: bun test

.github/workflows/publish.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# reference: https://docs.npmjs.com/generating-provenance-statements
2+
3+
name: Publish
4+
5+
on:
6+
push:
7+
tags:
8+
- '*'
9+
10+
jobs:
11+
publish:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
id-token: write
16+
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
21+
- name: Install Bun
22+
uses: oven-sh/setup-bun@v2
23+
24+
- name: Install dependencies
25+
run: bun install
26+
27+
- name: Compile project
28+
run: bun run compile
29+
30+
- name: Use Node.js 20
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: 20
34+
registry-url: 'https://registry.npmjs.org'
35+
36+
- name: Publish package
37+
run: npm publish --provenance --access public
38+
env:
39+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# dependencies (bun install)
2+
node_modules
3+
4+
# output
5+
out
6+
dist
7+
*.tgz
8+
9+
# code coverage
10+
coverage
11+
*.lcov
12+
13+
# logs
14+
logs
15+
_.log
16+
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17+
18+
# dotenv environment variable files
19+
.env
20+
.env.development.local
21+
.env.test.local
22+
.env.production.local
23+
.env.local
24+
25+
# caches
26+
.eslintcache
27+
.cache
28+
*.tsbuildinfo
29+
30+
# IntelliJ based IDEs
31+
.idea
32+
33+
# Finder (MacOS) folder config
34+
.DS_Store

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2025-present Guillermo Rauch and Socket.IO contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# @socket.io/bun-engine
2+
3+
## How to use
4+
5+
```js
6+
import { Server as Engine } from "@socket.io/bun-engine";
7+
import { Server } from "socket.io";
8+
9+
const io = new Server();
10+
11+
const engine = new Engine({
12+
path: "/socket.io/",
13+
});
14+
15+
io.bind(engine);
16+
17+
Bun.serve({
18+
...engine.handler(),
19+
port: 3000,
20+
});
21+
22+
io.on("connection", (socket) => {
23+
// ...
24+
});
25+
```
26+
27+
## License
28+
29+
[MIT](/LICENSE)

bun.lock

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"lockfileVersion": 1,
3+
"workspaces": {
4+
"": {
5+
"name": "@socket.io/bun-engine",
6+
"devDependencies": {
7+
"@types/bun": "^1.2.20",
8+
"prettier": "^3.6.2",
9+
"socket.io": "^4.8.1",
10+
},
11+
"peerDependencies": {
12+
"typescript": "^5",
13+
},
14+
},
15+
},
16+
"packages": {
17+
"@socket.io/component-emitter": ["@socket.io/[email protected]", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
18+
19+
"@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="],
20+
21+
"@types/cors": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="],
22+
23+
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ=="],
24+
25+
"@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="],
26+
27+
"accepts": ["[email protected]", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
28+
29+
"base64id": ["[email protected]", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
30+
31+
"bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="],
32+
33+
"cookie": ["[email protected]", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
34+
35+
"cors": ["[email protected]", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
36+
37+
"csstype": ["[email protected]", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
38+
39+
"debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
40+
41+
"engine.io": ["[email protected]", "", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1" } }, "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g=="],
42+
43+
"engine.io-parser": ["[email protected]", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
44+
45+
"mime-db": ["[email protected]", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
46+
47+
"mime-types": ["[email protected]", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
48+
49+
"ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
50+
51+
"negotiator": ["[email protected]", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
52+
53+
"object-assign": ["[email protected]", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
54+
55+
"prettier": ["[email protected]", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
56+
57+
"socket.io": ["[email protected]", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg=="],
58+
59+
"socket.io-adapter": ["[email protected]", "", { "dependencies": { "debug": "~4.3.4", "ws": "~8.17.1" } }, "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg=="],
60+
61+
"socket.io-parser": ["[email protected]", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } }, "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew=="],
62+
63+
"typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
64+
65+
"undici-types": ["[email protected]", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
66+
67+
"vary": ["[email protected]", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
68+
69+
"ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
70+
}
71+
}

lib/cors.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
type OriginOption = boolean | string | RegExp | (string | RegExp)[];
2+
3+
export interface CorsOptions {
4+
origin?: OriginOption;
5+
methods?: string | string[];
6+
allowedHeaders?: string | string[];
7+
exposedHeaders?: string | string[];
8+
credentials?: boolean;
9+
maxAge?: number;
10+
}
11+
12+
export function addCorsHeaders(
13+
headers: Headers,
14+
opts: CorsOptions,
15+
req: Request,
16+
) {
17+
addOrigin(opts, headers, req);
18+
addCredentials(opts, headers);
19+
addExposedHeaders(opts, headers);
20+
21+
if (req.method === "OPTIONS") {
22+
addMethods(opts, headers);
23+
addAllowedHeaders(opts, headers, req);
24+
addMaxAge(opts, headers);
25+
}
26+
}
27+
28+
function join(arg: string | string[]) {
29+
return Array.isArray(arg) ? arg.join(",") : arg;
30+
}
31+
32+
function isOriginAllowed(allowedOrigin: OriginOption, origin: string): boolean {
33+
if (Array.isArray(allowedOrigin)) {
34+
for (let i = 0; i < allowedOrigin.length; i++) {
35+
if (isOriginAllowed(allowedOrigin[i]!, origin)) {
36+
return true;
37+
}
38+
}
39+
return false;
40+
} else if (typeof allowedOrigin === "string") {
41+
return allowedOrigin === origin;
42+
} else if (allowedOrigin instanceof RegExp) {
43+
return allowedOrigin.test(origin);
44+
} else {
45+
return !!allowedOrigin;
46+
}
47+
}
48+
49+
function addOrigin(opts: CorsOptions, headers: Headers, req: Request) {
50+
const origin = req.headers.get("origin")!;
51+
const allowedOrigin = opts.origin;
52+
53+
if (!allowedOrigin || allowedOrigin === "*") {
54+
headers.set("Access-Control-Allow-Origin", "*");
55+
} else if (typeof allowedOrigin === "string") {
56+
headers.set("Access-Control-Allow-Origin", allowedOrigin);
57+
headers.append("Vary", "Origin");
58+
} else {
59+
const isAllowed = isOriginAllowed(allowedOrigin, origin);
60+
headers.set("Access-Control-Allow-Origin", isAllowed ? origin : "false");
61+
headers.append("Vary", "Origin");
62+
}
63+
}
64+
65+
function addMethods(opts: CorsOptions, headers: Headers) {
66+
if (opts.methods) {
67+
headers.set("Access-Control-Allow-Methods", join(opts.methods));
68+
}
69+
}
70+
71+
function addAllowedHeaders(opts: CorsOptions, headers: Headers, req: Request) {
72+
if (opts.allowedHeaders) {
73+
headers.set("Access-Control-Allow-Headers", join(opts.allowedHeaders));
74+
return;
75+
}
76+
const requestedHeaders = req.headers.get("access-control-request-headers");
77+
if (requestedHeaders) {
78+
headers.append("Vary", "Access-Control-Request-Headers");
79+
headers.set("Access-Control-Allow-Headers", requestedHeaders);
80+
}
81+
}
82+
83+
function addExposedHeaders(opts: CorsOptions, headers: Headers) {
84+
if (opts.exposedHeaders) {
85+
headers.set("Access-Control-Expose-Headers", join(opts.exposedHeaders));
86+
}
87+
}
88+
89+
function addCredentials(opts: CorsOptions, headers: Headers) {
90+
if (opts.credentials) {
91+
headers.set("Access-Control-Allow-Credentials", "true");
92+
}
93+
}
94+
95+
function addMaxAge(opts: CorsOptions, headers: Headers) {
96+
if (opts.maxAge) {
97+
headers.set("Access-Control-Max-Age", opts.maxAge.toString());
98+
}
99+
}

0 commit comments

Comments
 (0)