@rubriclab/bunl 0.1.24 → 0.2.0
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/.env.example +2 -1
- package/.github/workflows/publish.yml +16 -0
- package/.vscode/settings.json +16 -0
- package/Dockerfile +6 -8
- package/LICENSE +1 -1
- package/biome.json +3 -0
- package/build/client.js +94 -468
- package/bun.lock +153 -0
- package/client.ts +129 -79
- package/demo.ts +79 -12
- package/docker-compose.yml +36 -0
- package/e2e.test.ts +189 -0
- package/package.json +47 -43
- package/server.ts +146 -79
- package/tsconfig.json +3 -27
- package/types.ts +26 -9
- package/utils.ts +119 -6
- package/bun.lockb +0 -0
- package/fly.toml +0 -19
- package/index.ts +0 -67
package/bun.lock
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "@rubriclab/bunl",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@rubriclab/config": "^0.0.24",
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@rubriclab/package": "^0.0.124",
|
|
12
|
+
"@types/bun": "latest",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
"packages": {
|
|
17
|
+
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
|
18
|
+
|
|
19
|
+
"@biomejs/biome": ["@biomejs/biome@2.3.14", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.14", "@biomejs/cli-darwin-x64": "2.3.14", "@biomejs/cli-linux-arm64": "2.3.14", "@biomejs/cli-linux-arm64-musl": "2.3.14", "@biomejs/cli-linux-x64": "2.3.14", "@biomejs/cli-linux-x64-musl": "2.3.14", "@biomejs/cli-win32-arm64": "2.3.14", "@biomejs/cli-win32-x64": "2.3.14" }, "bin": { "biome": "bin/biome" } }, "sha512-QMT6QviX0WqXJCaiqVMiBUCr5WRQ1iFSjvOLoTk6auKukJMvnMzWucXpwZB0e8F00/1/BsS9DzcKgWH+CLqVuA=="],
|
|
20
|
+
|
|
21
|
+
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UJGPpvWJMkLxSRtpCAKfKh41Q4JJXisvxZL8ChN1eNW3m/WlPFJ6EFDCE7YfUb4XS8ZFi3C1dFpxUJ0Ety5n+A=="],
|
|
22
|
+
|
|
23
|
+
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-PNkLNQG6RLo8lG7QoWe/hhnMxJIt1tEimoXpGQjwS/dkdNiKBLPv4RpeQl8o3s1OKI3ZOR5XPiYtmbGGHAOnLA=="],
|
|
24
|
+
|
|
25
|
+
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-KT67FKfzIw6DNnUNdYlBg+eU24Go3n75GWK6NwU4+yJmDYFe9i/MjiI+U/iEzKvo0g7G7MZqoyrhIYuND2w8QQ=="],
|
|
26
|
+
|
|
27
|
+
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-LInRbXhYujtL3sH2TMCH/UBwJZsoGwfQjBrMfl84CD4hL/41C/EU5mldqf1yoFpsI0iPWuU83U+nB2TUUypWeg=="],
|
|
28
|
+
|
|
29
|
+
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-ZsZzQsl9U+wxFrGGS4f6UxREUlgHwmEfu1IrXlgNFrNnd5Th6lIJr8KmSzu/+meSa9f4rzFrbEW9LBBA6ScoMA=="],
|
|
30
|
+
|
|
31
|
+
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-KQU7EkbBBuHPW3/rAcoiVmhlPtDSGOGRPv9js7qJVpYTzjQmVR+C9Rfcz+ti8YCH+zT1J52tuBybtP4IodjxZQ=="],
|
|
32
|
+
|
|
33
|
+
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-+IKYkj/pUBbnRf1G1+RlyA3LWiDgra1xpS7H2g4BuOzzRbRB+hmlw0yFsLprHhbbt7jUzbzAbAjK/Pn0FDnh1A=="],
|
|
34
|
+
|
|
35
|
+
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.14", "", { "os": "win32", "cpu": "x64" }, "sha512-oizCjdyQ3WJEswpb3Chdngeat56rIdSYK12JI3iI11Mt5T5EXcZ7WLuowzEaFPNJ3zmOQFliMN8QY1Pi+qsfdQ=="],
|
|
36
|
+
|
|
37
|
+
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
|
38
|
+
|
|
39
|
+
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
|
40
|
+
|
|
41
|
+
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
|
42
|
+
|
|
43
|
+
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
|
44
|
+
|
|
45
|
+
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
|
46
|
+
|
|
47
|
+
"@rubriclab/cli": ["@rubriclab/cli@0.0.19", "", { "dependencies": { "@rubriclab/config": "*", "@rubriclab/package": "*" }, "peerDependencies": { "zod": "latest" } }, "sha512-Q8s9Gx33aQFQD154g1e1I1CrWv3u474l6hiobldk73IOB8kV42FkoYaXBI6j3QgxxjtfMmtlgz+yHZ6yVkJwAg=="],
|
|
48
|
+
|
|
49
|
+
"@rubriclab/config": ["@rubriclab/config@0.0.24", "", { "dependencies": { "@rubriclab/package": "*" }, "peerDependencies": { "@biomejs/biome": "^2.3.8", "@tailwindcss/postcss": "^4.1.17", "postcss": "^8.5.6", "tailwindcss": "^4.1.17", "typescript": "^5.9.3" } }, "sha512-9i+nj7ESsG/r95SiDtncQG9fZbNKNJrIJDNh09kAAvfKGpGputV6lSsIshr3dBNm8sYl+59u+w5QKilFKilXOA=="],
|
|
50
|
+
|
|
51
|
+
"@rubriclab/package": ["@rubriclab/package@0.0.124", "", { "dependencies": { "@rubriclab/cli": "*", "zod": "latest" }, "bin": { "rubriclab-package": "cli.ts" } }, "sha512-H0xMiWFdrTf/aoEd8hsJ9PfsBhhutROo4+Ryc8TKCjEPky1HFwEUPtD7l/wdaAHbGEGgTQP/8hTn6J0SDOFHIQ=="],
|
|
52
|
+
|
|
53
|
+
"@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="],
|
|
54
|
+
|
|
55
|
+
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="],
|
|
56
|
+
|
|
57
|
+
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="],
|
|
58
|
+
|
|
59
|
+
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="],
|
|
60
|
+
|
|
61
|
+
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="],
|
|
62
|
+
|
|
63
|
+
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="],
|
|
64
|
+
|
|
65
|
+
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="],
|
|
66
|
+
|
|
67
|
+
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="],
|
|
68
|
+
|
|
69
|
+
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="],
|
|
70
|
+
|
|
71
|
+
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="],
|
|
72
|
+
|
|
73
|
+
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="],
|
|
74
|
+
|
|
75
|
+
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="],
|
|
76
|
+
|
|
77
|
+
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="],
|
|
78
|
+
|
|
79
|
+
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="],
|
|
80
|
+
|
|
81
|
+
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="],
|
|
82
|
+
|
|
83
|
+
"@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
|
|
84
|
+
|
|
85
|
+
"@types/node": ["@types/node@25.2.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg=="],
|
|
86
|
+
|
|
87
|
+
"bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
|
|
88
|
+
|
|
89
|
+
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
90
|
+
|
|
91
|
+
"enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="],
|
|
92
|
+
|
|
93
|
+
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
|
94
|
+
|
|
95
|
+
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
|
96
|
+
|
|
97
|
+
"lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
|
|
98
|
+
|
|
99
|
+
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
|
|
100
|
+
|
|
101
|
+
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
|
|
102
|
+
|
|
103
|
+
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
|
|
104
|
+
|
|
105
|
+
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="],
|
|
106
|
+
|
|
107
|
+
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
|
|
108
|
+
|
|
109
|
+
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="],
|
|
110
|
+
|
|
111
|
+
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
|
|
112
|
+
|
|
113
|
+
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="],
|
|
114
|
+
|
|
115
|
+
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
|
|
116
|
+
|
|
117
|
+
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="],
|
|
118
|
+
|
|
119
|
+
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="],
|
|
120
|
+
|
|
121
|
+
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
|
122
|
+
|
|
123
|
+
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
|
124
|
+
|
|
125
|
+
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
|
126
|
+
|
|
127
|
+
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
|
128
|
+
|
|
129
|
+
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
|
130
|
+
|
|
131
|
+
"tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
|
|
132
|
+
|
|
133
|
+
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
|
134
|
+
|
|
135
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
136
|
+
|
|
137
|
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
138
|
+
|
|
139
|
+
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
|
140
|
+
|
|
141
|
+
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
|
142
|
+
|
|
143
|
+
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
|
|
144
|
+
|
|
145
|
+
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
|
|
146
|
+
|
|
147
|
+
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
|
|
148
|
+
|
|
149
|
+
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
|
150
|
+
|
|
151
|
+
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
152
|
+
}
|
|
153
|
+
}
|
package/client.ts
CHANGED
|
@@ -1,90 +1,140 @@
|
|
|
1
|
-
import { parseArgs } from
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import { parseArgs } from 'node:util'
|
|
2
|
+
import type { TunnelInit, TunnelRequest, TunnelResponse } from './types'
|
|
3
|
+
import { fromBase64, openBrowser, toBase64 } from './utils'
|
|
4
4
|
|
|
5
5
|
async function main({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
port,
|
|
7
|
+
domain,
|
|
8
|
+
subdomain,
|
|
9
|
+
open
|
|
10
10
|
}: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
port?: string
|
|
12
|
+
domain?: string
|
|
13
|
+
subdomain?: string
|
|
14
|
+
open?: boolean
|
|
15
15
|
}) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
16
|
+
const params = new URLSearchParams({
|
|
17
|
+
new: '',
|
|
18
|
+
...(subdomain ? { subdomain } : {})
|
|
19
|
+
}).toString()
|
|
20
|
+
|
|
21
|
+
// Auto-detect ws:// vs wss:// — use secure WebSocket for anything
|
|
22
|
+
// that isn't localhost or an explicit IP
|
|
23
|
+
const isLocal = /^(localhost|127\.|0\.0\.0\.0|\[::1\])/.test(domain || '')
|
|
24
|
+
const wsScheme = isLocal ? 'ws' : 'wss'
|
|
25
|
+
const serverUrl = `${wsScheme}://${domain}?${params}`
|
|
26
|
+
const socket = new WebSocket(serverUrl)
|
|
27
|
+
const localHost = Bun.env.HOST || 'localhost'
|
|
28
|
+
const localOrigin = `http://${localHost}:${port}`
|
|
29
|
+
|
|
30
|
+
socket.addEventListener('message', async event => {
|
|
31
|
+
const data = JSON.parse(event.data as string)
|
|
32
|
+
|
|
33
|
+
// Initial connection — server tells us our public URL
|
|
34
|
+
if (data.type === 'init') {
|
|
35
|
+
const init = data as TunnelInit
|
|
36
|
+
console.log(`\n↪ Your URL: \x1b[32m${init.url}\x1b[0m\n`)
|
|
37
|
+
if (open) openBrowser(init.url)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Incoming tunnel request — proxy to local server
|
|
42
|
+
if (data.type === 'request') {
|
|
43
|
+
const req = data as TunnelRequest
|
|
44
|
+
const now = performance.now()
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Decode base64 request body
|
|
48
|
+
const reqBody =
|
|
49
|
+
req.body && req.method !== 'GET' && req.method !== 'HEAD' ? fromBase64(req.body) : null
|
|
50
|
+
|
|
51
|
+
// Remove headers that would conflict with the local fetch
|
|
52
|
+
const fwdHeaders = { ...req.headers }
|
|
53
|
+
delete fwdHeaders.host
|
|
54
|
+
delete fwdHeaders.connection
|
|
55
|
+
delete fwdHeaders['transfer-encoding']
|
|
56
|
+
|
|
57
|
+
const res = await fetch(`${localOrigin}${req.pathname}`, {
|
|
58
|
+
body: reqBody,
|
|
59
|
+
headers: fwdHeaders,
|
|
60
|
+
method: req.method
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const elapsed = (performance.now() - now).toFixed(1)
|
|
64
|
+
console.log(`\x1b[32m${req.method}\x1b[0m ${req.pathname} → ${res.status} (${elapsed}ms)`)
|
|
65
|
+
|
|
66
|
+
// Read response as binary, encode to base64
|
|
67
|
+
const resBody = await res.arrayBuffer()
|
|
68
|
+
const headers: Record<string, string> = {}
|
|
69
|
+
res.headers.forEach((v, k) => {
|
|
70
|
+
headers[k] = v
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const response: TunnelResponse = {
|
|
74
|
+
body: toBase64(resBody),
|
|
75
|
+
headers,
|
|
76
|
+
id: req.id,
|
|
77
|
+
status: res.status,
|
|
78
|
+
statusText: res.statusText,
|
|
79
|
+
type: 'response'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
socket.send(JSON.stringify(response))
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error(`\x1b[31mERR\x1b[0m ${req.method} ${req.pathname}: ${err}`)
|
|
85
|
+
|
|
86
|
+
const response: TunnelResponse = {
|
|
87
|
+
body: toBase64(new TextEncoder().encode(`Failed to reach localhost:${port} — ${err}`).buffer),
|
|
88
|
+
headers: { 'content-type': 'text/plain' },
|
|
89
|
+
id: req.id,
|
|
90
|
+
status: 502,
|
|
91
|
+
statusText: 'Bad Gateway',
|
|
92
|
+
type: 'response'
|
|
93
|
+
}
|
|
94
|
+
socket.send(JSON.stringify(response))
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
socket.addEventListener('open', () => {
|
|
100
|
+
console.log(`Connected to ${serverUrl}`)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
socket.addEventListener('close', () => {
|
|
104
|
+
console.warn('Server closed connection')
|
|
105
|
+
process.exit(1)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
socket.addEventListener('error', err => {
|
|
109
|
+
console.error('WebSocket error:', err)
|
|
110
|
+
process.exit(1)
|
|
111
|
+
})
|
|
66
112
|
}
|
|
67
113
|
|
|
68
|
-
/**
|
|
69
|
-
* Eg. `bun client.ts -p 3000 -d example.so -s my-subdomain -o`
|
|
70
|
-
* > my-subdomain.example.so will be proxied to localhost:3000
|
|
71
|
-
* See README for full usage.
|
|
72
|
-
*/
|
|
73
114
|
const { values } = parseArgs({
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
})
|
|
115
|
+
allowPositionals: true,
|
|
116
|
+
args: process.argv,
|
|
117
|
+
options: {
|
|
118
|
+
domain: { default: 'localhost:1234', short: 'd', type: 'string' },
|
|
119
|
+
open: { short: 'o', type: 'boolean' },
|
|
120
|
+
port: { default: '3000', short: 'p', type: 'string' },
|
|
121
|
+
subdomain: { short: 's', type: 'string' },
|
|
122
|
+
version: { short: 'v', type: 'boolean' }
|
|
123
|
+
}
|
|
124
|
+
})
|
|
84
125
|
|
|
85
126
|
if (values.version) {
|
|
86
|
-
|
|
87
|
-
|
|
127
|
+
const pkg = await Bun.file(new URL('./package.json', import.meta.url)).json()
|
|
128
|
+
console.log(pkg.version)
|
|
129
|
+
process.exit()
|
|
88
130
|
}
|
|
89
131
|
|
|
90
|
-
main(
|
|
132
|
+
main({
|
|
133
|
+
domain: values.domain || 'localhost:1234',
|
|
134
|
+
open: values.open || false,
|
|
135
|
+
port: values.port || '3000',
|
|
136
|
+
subdomain: values.subdomain || ''
|
|
137
|
+
}).catch(err => {
|
|
138
|
+
console.error(err)
|
|
139
|
+
process.exit(1)
|
|
140
|
+
})
|
package/demo.ts
CHANGED
|
@@ -1,18 +1,85 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Demo webserver that serves multiple content types for testing the tunnel.
|
|
3
|
+
* Exercises: HTML, CSS, JSON API, binary images (PNG), and binary fonts (WOFF2-like).
|
|
4
|
+
*/
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
import { serve } from 'bun'
|
|
7
|
+
|
|
8
|
+
const port = Number(Bun.env.DEMO_PORT) || 3000
|
|
9
|
+
|
|
10
|
+
// 1x1 red PNG pixel (67 bytes) — smallest valid PNG
|
|
11
|
+
const PNG_1PX = Buffer.from(
|
|
12
|
+
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
|
|
13
|
+
'base64'
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// Generate a binary blob to simulate a font file
|
|
17
|
+
const FAKE_FONT = (() => {
|
|
18
|
+
const buf = Buffer.alloc(256)
|
|
19
|
+
for (let i = 0; i < 256; i++) buf[i] = i
|
|
20
|
+
return buf
|
|
21
|
+
})()
|
|
22
|
+
|
|
23
|
+
const CSS_CONTENT = `body { font-family: sans-serif; background: #111; color: #0f0; padding: 2rem; }
|
|
24
|
+
img { border: 2px solid #0f0; margin: 1rem 0; }`
|
|
25
|
+
|
|
26
|
+
const HTML = `<!DOCTYPE html>
|
|
27
|
+
<html>
|
|
28
|
+
<head>
|
|
29
|
+
<meta charset="utf-8">
|
|
30
|
+
<title>bunl demo</title>
|
|
31
|
+
<link rel="stylesheet" href="/style.css">
|
|
32
|
+
</head>
|
|
33
|
+
<body>
|
|
34
|
+
<h1>bunl tunnel works!</h1>
|
|
35
|
+
<p>This page was served through the tunnel.</p>
|
|
36
|
+
<img src="/image.png" alt="test pixel" width="100" height="100">
|
|
37
|
+
<p><a href="/api/health">JSON API</a> · <a href="/font.woff2">Font binary</a></p>
|
|
38
|
+
</body>
|
|
39
|
+
</html>`
|
|
4
40
|
|
|
5
41
|
serve({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
42
|
+
fetch(req) {
|
|
43
|
+
const url = new URL(req.url)
|
|
44
|
+
const { pathname } = url
|
|
45
|
+
|
|
46
|
+
console.log(`${req.method} ${pathname}`)
|
|
47
|
+
|
|
48
|
+
if (pathname === '/style.css') {
|
|
49
|
+
return new Response(CSS_CONTENT, {
|
|
50
|
+
headers: { 'content-type': 'text/css; charset=utf-8' }
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (pathname === '/image.png') {
|
|
55
|
+
return new Response(new Uint8Array(PNG_1PX), {
|
|
56
|
+
headers: { 'content-type': 'image/png' }
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (pathname === '/font.woff2') {
|
|
61
|
+
return new Response(new Uint8Array(FAKE_FONT), {
|
|
62
|
+
headers: { 'content-type': 'font/woff2' }
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (pathname === '/api/health') {
|
|
67
|
+
return Response.json({ status: 'ok', timestamp: Date.now() })
|
|
68
|
+
}
|
|
10
69
|
|
|
11
|
-
|
|
12
|
-
|
|
70
|
+
if (pathname === '/echo' && req.method === 'POST') {
|
|
71
|
+
return new Response(req.body, {
|
|
72
|
+
headers: {
|
|
73
|
+
'content-type': req.headers.get('content-type') || 'application/octet-stream'
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
13
77
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
})
|
|
78
|
+
return new Response(HTML, {
|
|
79
|
+
headers: { 'content-type': 'text/html; charset=utf-8' }
|
|
80
|
+
})
|
|
81
|
+
},
|
|
82
|
+
port
|
|
83
|
+
})
|
|
17
84
|
|
|
18
|
-
console.log(`
|
|
85
|
+
console.log(`Demo server at http://localhost:${port}`)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
services:
|
|
2
|
+
server:
|
|
3
|
+
build: .
|
|
4
|
+
ports:
|
|
5
|
+
- "1234:1234"
|
|
6
|
+
volumes:
|
|
7
|
+
- .:/app
|
|
8
|
+
- /app/node_modules
|
|
9
|
+
environment:
|
|
10
|
+
PORT: "1234"
|
|
11
|
+
SCHEME: http
|
|
12
|
+
DOMAIN: "localhost:1234"
|
|
13
|
+
entrypoint: ["bun", "--watch", "run", "server.ts"]
|
|
14
|
+
|
|
15
|
+
demo:
|
|
16
|
+
build: .
|
|
17
|
+
entrypoint: ["bun", "--watch", "run", "demo.ts"]
|
|
18
|
+
ports:
|
|
19
|
+
- "3000:3000"
|
|
20
|
+
volumes:
|
|
21
|
+
- .:/app
|
|
22
|
+
- /app/node_modules
|
|
23
|
+
environment:
|
|
24
|
+
DEMO_PORT: "3000"
|
|
25
|
+
|
|
26
|
+
client:
|
|
27
|
+
build: .
|
|
28
|
+
entrypoint: ["bun", "--watch", "run", "client.ts", "-p", "3000", "-d", "server:1234", "-s", "test"]
|
|
29
|
+
volumes:
|
|
30
|
+
- .:/app
|
|
31
|
+
- /app/node_modules
|
|
32
|
+
depends_on:
|
|
33
|
+
- server
|
|
34
|
+
- demo
|
|
35
|
+
environment:
|
|
36
|
+
HOST: "bunl-demo-1"
|