@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/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 "util";
2
- import browser from "open";
3
- import type { Payload } from "./types";
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
- port,
7
- domain,
8
- subdomain,
9
- open,
6
+ port,
7
+ domain,
8
+ subdomain,
9
+ open
10
10
  }: {
11
- port?: string;
12
- domain?: string;
13
- subdomain?: string;
14
- open?: boolean;
11
+ port?: string
12
+ domain?: string
13
+ subdomain?: string
14
+ open?: boolean
15
15
  }) {
16
- const params = new URLSearchParams({
17
- new: "",
18
- ...(subdomain ? { subdomain } : {}),
19
- }).toString();
20
- const serverUrl = `ws://${domain}?${params}`;
21
- const socket = new WebSocket(serverUrl);
22
-
23
- const url = `http://localhost:${port}`;
24
-
25
- socket.addEventListener("message", async (event) => {
26
- const data = JSON.parse(event.data as string);
27
-
28
- if (data.url) {
29
- console.log(`\n↪ Your URL: \x1b[32m${data.url}\x1b[0m\n`);
30
- if (open) browser(data.url);
31
- }
32
-
33
- if (data.method) {
34
- console.log(`\x1b[32m${data.method}\x1b[0m ${data.pathname}`);
35
-
36
- const res = await fetch(`${url}${data.pathname || ""}`, {
37
- method: data.method,
38
- headers: data.headers,
39
- body: data.body,
40
- });
41
-
42
- const { status, statusText, headers } = res;
43
- const body = await res.text();
44
-
45
- const payload: Payload = {
46
- method: data.method,
47
- pathname: data.pathname,
48
- status,
49
- statusText,
50
- headers: Object.fromEntries(headers),
51
- body,
52
- };
53
-
54
- socket.send(JSON.stringify(payload));
55
- }
56
- });
57
-
58
- socket.addEventListener("open", (event) => {
59
- if (!event.target.readyState) throw "not ready";
60
- });
61
-
62
- socket.addEventListener("close", () => {
63
- console.warn("server closed connection");
64
- process.exit();
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
- args: process.argv,
75
- options: {
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" },
81
- },
82
- allowPositionals: true,
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
- console.log(process.env.npm_package_version);
87
- process.exit();
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(values);
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
- import { serve } from "bun";
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
- const port = 3000;
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
- port,
7
- fetch: async (req) => {
8
- const url = new URL(req.url);
9
- console.log(req.method, url.pathname, url.search);
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
- const bod = await req.text();
12
- bod && console.log("Body: ", bod);
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
- return new Response(`hello from localhost:${port}`);
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(`Serving app at http://localhost:${port}`);
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"