@rubriclab/bunl 0.0.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client.js +16 -38
- package/client.ts +19 -39
- package/package.json +4 -4
- package/server.ts +30 -39
- package/types.ts +10 -0
package/build/client.js
CHANGED
|
@@ -437,7 +437,7 @@ var open_default = open;
|
|
|
437
437
|
|
|
438
438
|
// client.ts
|
|
439
439
|
async function main({
|
|
440
|
-
|
|
440
|
+
port,
|
|
441
441
|
domain,
|
|
442
442
|
subdomain,
|
|
443
443
|
open: open2
|
|
@@ -448,6 +448,7 @@ async function main({
|
|
|
448
448
|
}).toString();
|
|
449
449
|
const serverUrl = `ws://${domain}?${params}`;
|
|
450
450
|
const socket = new WebSocket(serverUrl);
|
|
451
|
+
const url = `http://localhost:${port}`;
|
|
451
452
|
socket.addEventListener("message", async (event) => {
|
|
452
453
|
const data = JSON.parse(event.data);
|
|
453
454
|
if (data.url) {
|
|
@@ -458,6 +459,7 @@ async function main({
|
|
|
458
459
|
open_default(data.url);
|
|
459
460
|
}
|
|
460
461
|
if (data.method) {
|
|
462
|
+
console.log(`[32m${data.method}[0m ${data.pathname}`);
|
|
461
463
|
const res = await fetch(`${url}${data.pathname || ""}`, {
|
|
462
464
|
method: data.method,
|
|
463
465
|
headers: data.headers,
|
|
@@ -465,63 +467,39 @@ async function main({
|
|
|
465
467
|
});
|
|
466
468
|
const { status, statusText, headers } = res;
|
|
467
469
|
const body = await res.text();
|
|
468
|
-
const
|
|
470
|
+
const payload = {
|
|
471
|
+
method: data.method,
|
|
469
472
|
pathname: data.pathname,
|
|
470
473
|
status,
|
|
471
474
|
statusText,
|
|
472
475
|
headers: Object.fromEntries(headers),
|
|
473
476
|
body
|
|
474
|
-
}
|
|
475
|
-
socket.send(
|
|
477
|
+
};
|
|
478
|
+
socket.send(JSON.stringify(payload));
|
|
476
479
|
}
|
|
477
480
|
});
|
|
478
481
|
socket.addEventListener("open", (event) => {
|
|
479
482
|
if (!event.target.readyState)
|
|
480
|
-
throw "
|
|
483
|
+
throw "not ready";
|
|
481
484
|
});
|
|
482
485
|
socket.addEventListener("close", () => {
|
|
483
|
-
console.
|
|
486
|
+
console.warn("server closed connection");
|
|
484
487
|
process.exit();
|
|
485
488
|
});
|
|
486
489
|
}
|
|
487
490
|
var { values } = parseArgs({
|
|
488
491
|
args: process.argv,
|
|
489
492
|
options: {
|
|
490
|
-
port: {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
495
|
-
domain: {
|
|
496
|
-
type: "string",
|
|
497
|
-
default: "localhost:1234",
|
|
498
|
-
short: "d"
|
|
499
|
-
},
|
|
500
|
-
subdomain: {
|
|
501
|
-
type: "string",
|
|
502
|
-
short: "s"
|
|
503
|
-
},
|
|
504
|
-
open: {
|
|
505
|
-
type: "boolean",
|
|
506
|
-
short: "o"
|
|
507
|
-
},
|
|
508
|
-
version: {
|
|
509
|
-
type: "boolean",
|
|
510
|
-
short: "v"
|
|
511
|
-
}
|
|
493
|
+
port: { type: "string", short: "p", default: "3000" },
|
|
494
|
+
domain: { type: "string", short: "d", default: "localhost:1234" },
|
|
495
|
+
subdomain: { type: "string", short: "s" },
|
|
496
|
+
open: { type: "boolean", short: "o" },
|
|
497
|
+
version: { type: "boolean", short: "v" }
|
|
512
498
|
},
|
|
513
499
|
allowPositionals: true
|
|
514
500
|
});
|
|
515
501
|
if (values.version) {
|
|
516
|
-
console.log("0.
|
|
502
|
+
console.log("0.1.24");
|
|
517
503
|
process.exit();
|
|
518
504
|
}
|
|
519
|
-
|
|
520
|
-
throw "pass -p 3000";
|
|
521
|
-
var { port, domain, subdomain, open: open2 } = values;
|
|
522
|
-
main({
|
|
523
|
-
url: `localhost:${port}`,
|
|
524
|
-
domain,
|
|
525
|
-
subdomain,
|
|
526
|
-
open: open2
|
|
527
|
-
});
|
|
505
|
+
main(values);
|
package/client.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { parseArgs } from "util";
|
|
2
2
|
import browser from "open";
|
|
3
|
+
import type { Payload } from "./types";
|
|
3
4
|
|
|
4
5
|
async function main({
|
|
5
|
-
|
|
6
|
+
port,
|
|
6
7
|
domain,
|
|
7
8
|
subdomain,
|
|
8
9
|
open,
|
|
9
10
|
}: {
|
|
10
|
-
|
|
11
|
+
port?: string;
|
|
11
12
|
domain?: string;
|
|
12
13
|
subdomain?: string;
|
|
13
14
|
open?: boolean;
|
|
@@ -19,6 +20,8 @@ async function main({
|
|
|
19
20
|
const serverUrl = `ws://${domain}?${params}`;
|
|
20
21
|
const socket = new WebSocket(serverUrl);
|
|
21
22
|
|
|
23
|
+
const url = `http://localhost:${port}`;
|
|
24
|
+
|
|
22
25
|
socket.addEventListener("message", async (event) => {
|
|
23
26
|
const data = JSON.parse(event.data as string);
|
|
24
27
|
|
|
@@ -28,6 +31,8 @@ async function main({
|
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
if (data.method) {
|
|
34
|
+
console.log(`\x1b[32m${data.method}\x1b[0m ${data.pathname}`);
|
|
35
|
+
|
|
31
36
|
const res = await fetch(`${url}${data.pathname || ""}`, {
|
|
32
37
|
method: data.method,
|
|
33
38
|
headers: data.headers,
|
|
@@ -37,24 +42,25 @@ async function main({
|
|
|
37
42
|
const { status, statusText, headers } = res;
|
|
38
43
|
const body = await res.text();
|
|
39
44
|
|
|
40
|
-
const
|
|
45
|
+
const payload: Payload = {
|
|
46
|
+
method: data.method,
|
|
41
47
|
pathname: data.pathname,
|
|
42
48
|
status,
|
|
43
49
|
statusText,
|
|
44
50
|
headers: Object.fromEntries(headers),
|
|
45
51
|
body,
|
|
46
|
-
}
|
|
52
|
+
};
|
|
47
53
|
|
|
48
|
-
socket.send(
|
|
54
|
+
socket.send(JSON.stringify(payload));
|
|
49
55
|
}
|
|
50
56
|
});
|
|
51
57
|
|
|
52
58
|
socket.addEventListener("open", (event) => {
|
|
53
|
-
if (!event.target.readyState) throw "
|
|
59
|
+
if (!event.target.readyState) throw "not ready";
|
|
54
60
|
});
|
|
55
61
|
|
|
56
62
|
socket.addEventListener("close", () => {
|
|
57
|
-
console.
|
|
63
|
+
console.warn("server closed connection");
|
|
58
64
|
process.exit();
|
|
59
65
|
});
|
|
60
66
|
}
|
|
@@ -67,28 +73,11 @@ async function main({
|
|
|
67
73
|
const { values } = parseArgs({
|
|
68
74
|
args: process.argv,
|
|
69
75
|
options: {
|
|
70
|
-
port: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
domain: {
|
|
76
|
-
type: "string",
|
|
77
|
-
default: "localhost:1234",
|
|
78
|
-
short: "d",
|
|
79
|
-
},
|
|
80
|
-
subdomain: {
|
|
81
|
-
type: "string",
|
|
82
|
-
short: "s",
|
|
83
|
-
},
|
|
84
|
-
open: {
|
|
85
|
-
type: "boolean",
|
|
86
|
-
short: "o",
|
|
87
|
-
},
|
|
88
|
-
version: {
|
|
89
|
-
type: "boolean",
|
|
90
|
-
short: "v",
|
|
91
|
-
},
|
|
76
|
+
port: { type: "string", short: "p", default: "3000" },
|
|
77
|
+
domain: { type: "string", short: "d", default: "localhost:1234" },
|
|
78
|
+
subdomain: { type: "string", short: "s" },
|
|
79
|
+
open: { type: "boolean", short: "o" },
|
|
80
|
+
version: { type: "boolean", short: "v" },
|
|
92
81
|
},
|
|
93
82
|
allowPositionals: true,
|
|
94
83
|
});
|
|
@@ -98,13 +87,4 @@ if (values.version) {
|
|
|
98
87
|
process.exit();
|
|
99
88
|
}
|
|
100
89
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const { port, domain, subdomain, open } = values;
|
|
104
|
-
|
|
105
|
-
main({
|
|
106
|
-
url: `localhost:${port}`,
|
|
107
|
-
domain,
|
|
108
|
-
subdomain,
|
|
109
|
-
open,
|
|
110
|
-
});
|
|
90
|
+
main(values);
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
},
|
|
5
5
|
"name": "@rubriclab/bunl",
|
|
6
6
|
"description": "Expose localhost to the world",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.1.24",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"main": "build/client.js",
|
|
21
21
|
"scripts": {
|
|
22
22
|
"server": "bun server.ts",
|
|
23
|
-
"dev:server": "bun --
|
|
24
|
-
"client": "bun --
|
|
25
|
-
"demo": "bun --
|
|
23
|
+
"dev:server": "bun --watch server.ts",
|
|
24
|
+
"client": "bun --watch client.ts",
|
|
25
|
+
"demo": "bun --watch demo.ts",
|
|
26
26
|
"client:upgrade": "bun rm -g @rubriclab/bunl && bun i -g @rubriclab/bunl@latest",
|
|
27
27
|
"build": "BUILD=build/client.js && bun build client.ts --outdir build --target bun && echo -e \"#! /usr/bin/env bun\n$(cat $BUILD)\" > $BUILD",
|
|
28
28
|
"npm:publish": "bun run build && npm publish"
|
package/server.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { serve,
|
|
1
|
+
import { serve, type ServerWebSocket } from "bun";
|
|
2
2
|
import { uid } from "./utils";
|
|
3
|
-
|
|
4
|
-
type Client = { id: string };
|
|
3
|
+
import type { Client, Payload } from "./types";
|
|
5
4
|
|
|
6
5
|
const port = Bun.env.PORT || 1234;
|
|
7
6
|
const scheme = Bun.env.SCHEME || "http";
|
|
8
7
|
const domain = Bun.env.DOMAIN || `localhost:${port}`;
|
|
9
8
|
|
|
10
9
|
const clients = new Map<string, ServerWebSocket<Client>>();
|
|
11
|
-
const
|
|
10
|
+
const requesters = new Map<string, WritableStream>();
|
|
12
11
|
|
|
13
12
|
serve<Client>({
|
|
14
13
|
port,
|
|
@@ -28,65 +27,57 @@ serve<Client>({
|
|
|
28
27
|
const subdomain = reqUrl.hostname.split(".")[0];
|
|
29
28
|
|
|
30
29
|
if (!clients.has(subdomain)) {
|
|
31
|
-
return new Response(
|
|
30
|
+
return new Response(`${subdomain} not found`, { status: 404 });
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
// The magic: forward the req to the client
|
|
35
34
|
const client = clients.get(subdomain)!;
|
|
36
35
|
const { method, url, headers: reqHeaders } = req;
|
|
37
36
|
const reqBody = await req.text();
|
|
38
|
-
const pathname = new URL(url).pathname
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
const pathname = new URL(url).pathname;
|
|
38
|
+
const payload: Payload = {
|
|
39
|
+
method,
|
|
40
|
+
pathname,
|
|
41
|
+
body: reqBody,
|
|
42
|
+
headers: reqHeaders,
|
|
43
|
+
};
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
let res = clientData.get(`${subdomain}/${pathname}`);
|
|
45
|
+
const { writable, readable } = new TransformStream();
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
while (!res) {
|
|
52
|
-
await sleep(1000);
|
|
53
|
-
retries--;
|
|
47
|
+
requesters.set(`${method}:${subdomain}${pathname}`, writable);
|
|
48
|
+
client.send(JSON.stringify(payload));
|
|
54
49
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (retries < 1) {
|
|
58
|
-
return new Response("client not responding", { status: 500 });
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const { status, statusText, headers, body } = JSON.parse(res);
|
|
63
|
-
delete headers["content-encoding"];
|
|
50
|
+
const res = await readable.getReader().read();
|
|
51
|
+
const { status, statusText, headers, body } = JSON.parse(res.value);
|
|
64
52
|
|
|
65
53
|
return new Response(body, { status, statusText, headers });
|
|
66
54
|
},
|
|
67
55
|
websocket: {
|
|
68
56
|
open(ws) {
|
|
69
57
|
clients.set(ws.data.id, ws);
|
|
70
|
-
console.log(
|
|
71
|
-
`\x1b[32mconnected to ${ws.data.id} (${clients.size} total)\x1b[0m`
|
|
72
|
-
);
|
|
58
|
+
console.log(`\x1b[32m+ ${ws.data.id} (${clients.size} total)\x1b[0m`);
|
|
73
59
|
ws.send(
|
|
74
60
|
JSON.stringify({
|
|
75
61
|
url: `${scheme}://${ws.data.id}.${domain}`,
|
|
76
62
|
})
|
|
77
63
|
);
|
|
78
64
|
},
|
|
79
|
-
message(
|
|
80
|
-
console.log("message from",
|
|
65
|
+
message: async ({ data: { id } }, message: string) => {
|
|
66
|
+
console.log("message from", id);
|
|
67
|
+
|
|
68
|
+
const { method, pathname } = JSON.parse(message) as Payload;
|
|
69
|
+
const writable = requesters.get(`${method}:${id}${pathname}`);
|
|
70
|
+
if (!writable) throw "connection not found";
|
|
81
71
|
|
|
82
|
-
const
|
|
83
|
-
|
|
72
|
+
const writer = writable.getWriter();
|
|
73
|
+
await writer.write(message);
|
|
74
|
+
await writer.close();
|
|
84
75
|
},
|
|
85
|
-
close(
|
|
86
|
-
console.log("closing",
|
|
87
|
-
clients.delete(
|
|
76
|
+
close({ data }) {
|
|
77
|
+
console.log("closing", data.id);
|
|
78
|
+
clients.delete(data.id);
|
|
88
79
|
},
|
|
89
80
|
},
|
|
90
81
|
});
|
|
91
82
|
|
|
92
|
-
console.log(`
|
|
83
|
+
console.log(`websocket server up at ws://${domain}`);
|