@seedprotocol/gateway-hyper 0.5.1 → 0.5.2
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/README.md +33 -4
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +253 -80
- package/dist/index.js.map +1 -1
- package/dist/tunnel/connect.d.ts.map +1 -1
- package/dist/tunnel/forward.d.ts +5 -0
- package/dist/tunnel/forward.d.ts.map +1 -0
- package/dist/tunnel/mountPath.d.ts +6 -0
- package/dist/tunnel/mountPath.d.ts.map +1 -0
- package/dist/tunnel/protocol.d.ts.map +1 -1
- package/dist/tunnel/proxyApp.d.ts +11 -0
- package/dist/tunnel/proxyApp.d.ts.map +1 -0
- package/dist/tunnel/serve.d.ts.map +1 -1
- package/dist/tunnel/session.d.ts +22 -0
- package/dist/tunnel/session.d.ts.map +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# @seedprotocol/gateway-hyper
|
|
2
2
|
|
|
3
|
-
Expose a local **seed-protocol-server + ar.io gateway** stack (via Traefik or direct upstream) to SDK users over **HyperDHT
|
|
3
|
+
Expose a local **seed-protocol-server + ar.io gateway** stack (via Traefik or direct upstream) to SDK users over **HyperDHT**.
|
|
4
|
+
|
|
5
|
+
Two client shapes:
|
|
6
|
+
|
|
7
|
+
- **Path A — Local sidecar:** `connectTunnel` → `http://127.0.0.1:1984` (local / Electron)
|
|
8
|
+
- **Path B — App-server proxy:** `createGatewayProxy` → mount on `/api/seed-gateway` (hosted web browsers)
|
|
4
9
|
|
|
5
10
|
See [docs/GATEWAY_HYPER.md](../../docs/GATEWAY_HYPER.md) for operator and developer runbooks.
|
|
6
11
|
|
|
@@ -10,7 +15,7 @@ See [docs/GATEWAY_HYPER.md](../../docs/GATEWAY_HYPER.md) for operator and develo
|
|
|
10
15
|
bun add @seedprotocol/gateway-hyper
|
|
11
16
|
```
|
|
12
17
|
|
|
13
|
-
Requires Node 20+ and Holepunch native addons.
|
|
18
|
+
Requires Node 20+ and Holepunch native addons. Prefer Node over Bun.
|
|
14
19
|
|
|
15
20
|
## CLI
|
|
16
21
|
|
|
@@ -18,22 +23,46 @@ Requires Node 20+ and Holepunch native addons.
|
|
|
18
23
|
# Operator
|
|
19
24
|
seed gateway tunnel serve --upstream http://127.0.0.1:80 --key-file ./.seed/gateway-tunnel/operator.key.json
|
|
20
25
|
|
|
21
|
-
# Client sidecar
|
|
26
|
+
# Client sidecar (Path A)
|
|
22
27
|
seed gateway tunnel connect <z32-key> --port 1984
|
|
23
28
|
```
|
|
24
29
|
|
|
25
30
|
## Library
|
|
26
31
|
|
|
27
32
|
```ts
|
|
28
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
serveTunnel,
|
|
35
|
+
connectTunnel,
|
|
36
|
+
createGatewayProxy,
|
|
37
|
+
} from '@seedprotocol/gateway-hyper'
|
|
29
38
|
|
|
30
39
|
const operator = await serveTunnel({ upstream: 'http://127.0.0.1:80' })
|
|
31
40
|
console.log(operator.key)
|
|
32
41
|
|
|
42
|
+
// Path A
|
|
33
43
|
const client = await connectTunnel({ key: operator.key, port: 1984 })
|
|
34
44
|
console.log(client.baseUrl) // http://127.0.0.1:1984
|
|
45
|
+
|
|
46
|
+
// Path B — Next.js App Router (Node runtime)
|
|
47
|
+
const proxy = createGatewayProxy({
|
|
48
|
+
key: process.env.SEED_GATEWAY_HYPER_KEY ?? operator.key,
|
|
49
|
+
mountPath: '/api/seed-gateway',
|
|
50
|
+
})
|
|
51
|
+
export const GET = (req: Request) => proxy.handleFetch(req)
|
|
52
|
+
export const POST = (req: Request) => proxy.handleFetch(req)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Web SDK config for Path B:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
gateway: {
|
|
59
|
+
transport: 'hybrid',
|
|
60
|
+
proxyBaseUrl: '/api/seed-gateway',
|
|
61
|
+
}
|
|
35
62
|
```
|
|
36
63
|
|
|
64
|
+
Keep `SEED_GATEWAY_HYPER_KEY` on the server only. Authenticate / rate-limit the proxy route in production.
|
|
65
|
+
|
|
37
66
|
## Tests
|
|
38
67
|
|
|
39
68
|
- Unit tests always run.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export { encodePublicKeyZ32, decodePublicKey, loadOrCreateOperatorKeypair } from './keys';
|
|
2
2
|
export { serveTunnel } from './tunnel/serve';
|
|
3
3
|
export { connectTunnel, localGatewayUrl } from './tunnel/connect';
|
|
4
|
+
export { createGatewayProxy } from './tunnel/proxyApp';
|
|
5
|
+
export { stripMountPath } from './tunnel/mountPath';
|
|
6
|
+
export { createTunnelSession } from './tunnel/session';
|
|
4
7
|
export { writeFrame, readFrame, writeTunnelRequest, readTunnelRequest, writeTunnelResponse, readTunnelResponse, flattenRequestHeaders, filterResponseHeaders, MAX_FRAME_BYTES, } from './tunnel/protocol';
|
|
5
|
-
export type { TunnelMeta, TunnelResponseMeta, ServeTunnelOptions, ServeTunnelResult, ConnectTunnelOptions, ConnectTunnelResult, OperatorKeyPair, } from './types';
|
|
8
|
+
export type { TunnelMeta, TunnelResponseMeta, ServeTunnelOptions, ServeTunnelResult, ConnectTunnelOptions, ConnectTunnelResult, OperatorKeyPair, CreateGatewayProxyOptions, GatewayProxy, } from './types';
|
|
6
9
|
export { DEFAULT_SEED_GATEWAY_HYPER_KEY } from './constants';
|
|
7
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,QAAQ,CAAA;AAEzF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,QAAQ,CAAA;AAEzF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAEtD,OAAO,EACL,UAAU,EACV,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,GAChB,MAAM,mBAAmB,CAAA;AAE1B,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,yBAAyB,EACzB,YAAY,GACb,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -59,73 +59,117 @@ function d() {
|
|
|
59
59
|
}
|
|
60
60
|
//#endregion
|
|
61
61
|
//#region src/tunnel/protocol.ts
|
|
62
|
-
var f = 64 * 1024 * 1024
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
var f = 64 * 1024 * 1024, p = class {
|
|
63
|
+
stream;
|
|
64
|
+
buffer = Buffer.alloc(0);
|
|
65
|
+
waiters = [];
|
|
66
|
+
ended = !1;
|
|
67
|
+
onData;
|
|
68
|
+
onError;
|
|
69
|
+
onEnd;
|
|
70
|
+
constructor(e) {
|
|
71
|
+
this.stream = e, this.onData = (e) => {
|
|
72
|
+
this.buffer = Buffer.concat([this.buffer, e]), this.drain();
|
|
73
|
+
}, this.onError = (e) => {
|
|
74
|
+
let t = this.waiters.splice(0);
|
|
75
|
+
for (let n of t) n.reject(e);
|
|
76
|
+
}, this.onEnd = () => {
|
|
77
|
+
if (this.ended = !0, this.drain(), this.waiters.length > 0) {
|
|
78
|
+
let e = this.waiters.splice(0);
|
|
79
|
+
for (let t of e) t.reject(/* @__PURE__ */ Error(`Unexpected end of stream (expected ${t.length} bytes, got ${this.buffer.length})`));
|
|
72
80
|
}
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
}, e.on("data", this.onData), e.on("error", this.onError), e.on("end", this.onEnd);
|
|
82
|
+
}
|
|
83
|
+
readExact(e) {
|
|
84
|
+
return new Promise((t, n) => {
|
|
85
|
+
if (e === 0) {
|
|
86
|
+
t(Buffer.alloc(0));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (this.waiters.push({
|
|
90
|
+
length: e,
|
|
91
|
+
resolve: t,
|
|
92
|
+
reject: n
|
|
93
|
+
}), this.drain(), this.ended && this.waiters.length > 0) {
|
|
94
|
+
let e = this.waiters.splice(0);
|
|
95
|
+
for (let t of e) t.reject(/* @__PURE__ */ Error(`Unexpected end of stream (expected ${t.length} bytes, got ${this.buffer.length})`));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
drain() {
|
|
100
|
+
for (; this.waiters.length > 0;) {
|
|
101
|
+
let e = this.waiters[0];
|
|
102
|
+
if (!e || this.buffer.length < e.length) break;
|
|
103
|
+
this.waiters.shift();
|
|
104
|
+
let t = this.buffer.subarray(0, e.length);
|
|
105
|
+
this.buffer = this.buffer.subarray(e.length), e.resolve(t);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}, m = /* @__PURE__ */ new WeakMap();
|
|
109
|
+
function h(e) {
|
|
110
|
+
let t = m.get(e);
|
|
111
|
+
return t || (t = new p(e), m.set(e, t)), t;
|
|
82
112
|
}
|
|
83
|
-
function
|
|
113
|
+
function g(e, t) {
|
|
114
|
+
return h(e).readExact(t);
|
|
115
|
+
}
|
|
116
|
+
function _(e, t) {
|
|
84
117
|
return new Promise((n, r) => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
118
|
+
try {
|
|
119
|
+
if (e.write(t)) {
|
|
120
|
+
n();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
let i = () => {
|
|
124
|
+
e.off("error", a), n();
|
|
125
|
+
}, a = (t) => {
|
|
126
|
+
e.off("drain", i), r(t);
|
|
127
|
+
};
|
|
128
|
+
e.once("drain", i), e.once("error", a);
|
|
129
|
+
} catch (e) {
|
|
130
|
+
r(e instanceof Error ? e : Error(String(e)));
|
|
131
|
+
}
|
|
88
132
|
});
|
|
89
133
|
}
|
|
90
|
-
async function
|
|
134
|
+
async function v(e, t) {
|
|
91
135
|
if (t.length > 67108864) throw Error(`Frame too large: ${t.length} bytes (max ${f})`);
|
|
92
136
|
let n = Buffer.alloc(4);
|
|
93
|
-
n.writeUInt32BE(t.length, 0), await
|
|
137
|
+
n.writeUInt32BE(t.length, 0), await _(e, Buffer.concat([n, t]));
|
|
94
138
|
}
|
|
95
|
-
async function
|
|
96
|
-
let t = (await
|
|
139
|
+
async function y(e) {
|
|
140
|
+
let t = (await g(e, 4)).readUInt32BE(0);
|
|
97
141
|
if (t === 0) return Buffer.alloc(0);
|
|
98
142
|
if (t > 67108864) throw Error(`Frame too large: ${t} bytes (max ${f})`);
|
|
99
|
-
return
|
|
143
|
+
return g(e, t);
|
|
100
144
|
}
|
|
101
|
-
async function
|
|
102
|
-
await
|
|
145
|
+
async function b(e, t, n) {
|
|
146
|
+
await v(e, Buffer.from(JSON.stringify(t), "utf8")), await v(e, n);
|
|
103
147
|
}
|
|
104
|
-
async function
|
|
105
|
-
let t = await
|
|
148
|
+
async function x(e) {
|
|
149
|
+
let t = await y(e), n = JSON.parse(t.toString("utf8"));
|
|
106
150
|
if (!n.method || !n.path) throw Error("Invalid tunnel request meta: missing method or path");
|
|
107
151
|
return n.headers = n.headers ?? {}, {
|
|
108
152
|
meta: n,
|
|
109
|
-
body: await
|
|
153
|
+
body: await y(e)
|
|
110
154
|
};
|
|
111
155
|
}
|
|
112
|
-
async function
|
|
113
|
-
await
|
|
156
|
+
async function S(e, t, n) {
|
|
157
|
+
await v(e, Buffer.from(JSON.stringify(t), "utf8")), await v(e, n);
|
|
114
158
|
}
|
|
115
|
-
async function
|
|
116
|
-
let t = await
|
|
159
|
+
async function C(e) {
|
|
160
|
+
let t = await y(e), n = JSON.parse(t.toString("utf8"));
|
|
117
161
|
if (typeof n.status != "number") throw Error("Invalid tunnel response meta: missing status");
|
|
118
162
|
return n.headers = n.headers ?? {}, {
|
|
119
163
|
meta: n,
|
|
120
|
-
body: await
|
|
164
|
+
body: await y(e)
|
|
121
165
|
};
|
|
122
166
|
}
|
|
123
|
-
function
|
|
167
|
+
function w(e) {
|
|
124
168
|
let t = {};
|
|
125
169
|
for (let [n, r] of Object.entries(e)) r != null && (t[n] = Array.isArray(r) ? r.join(", ") : r);
|
|
126
170
|
return t;
|
|
127
171
|
}
|
|
128
|
-
function
|
|
172
|
+
function T(e) {
|
|
129
173
|
let t = new Set([
|
|
130
174
|
"connection",
|
|
131
175
|
"keep-alive",
|
|
@@ -137,7 +181,7 @@ function S(e) {
|
|
|
137
181
|
}
|
|
138
182
|
//#endregion
|
|
139
183
|
//#region src/tunnel/proxy.ts
|
|
140
|
-
async function
|
|
184
|
+
async function E(e, t, n) {
|
|
141
185
|
let r = e.replace(/\/$/, ""), o = new a(t.path, `${r}/`);
|
|
142
186
|
return new Promise((e, r) => {
|
|
143
187
|
let a = i.request(o, {
|
|
@@ -148,7 +192,7 @@ async function C(e, t, n) {
|
|
|
148
192
|
t.on("data", (e) => n.push(e)), t.on("end", () => {
|
|
149
193
|
e({
|
|
150
194
|
status: t.statusCode ?? 502,
|
|
151
|
-
headers:
|
|
195
|
+
headers: T(t.headers),
|
|
152
196
|
body: Buffer.concat(n)
|
|
153
197
|
});
|
|
154
198
|
}), t.on("error", r);
|
|
@@ -158,24 +202,25 @@ async function C(e, t, n) {
|
|
|
158
202
|
}
|
|
159
203
|
//#endregion
|
|
160
204
|
//#region src/tunnel/serve.ts
|
|
161
|
-
function
|
|
205
|
+
function D(e, t) {
|
|
206
|
+
let n = x(e);
|
|
162
207
|
(async () => {
|
|
163
208
|
try {
|
|
164
209
|
for (;;) {
|
|
165
|
-
let { meta:
|
|
166
|
-
await
|
|
167
|
-
status:
|
|
168
|
-
headers:
|
|
169
|
-
},
|
|
210
|
+
let { meta: r, body: i } = await n, a = await E(t, r, i);
|
|
211
|
+
await S(e, {
|
|
212
|
+
status: a.status,
|
|
213
|
+
headers: a.headers
|
|
214
|
+
}, a.body), n = x(e);
|
|
170
215
|
}
|
|
171
216
|
} catch {
|
|
172
217
|
e.destroy();
|
|
173
218
|
}
|
|
174
219
|
})();
|
|
175
220
|
}
|
|
176
|
-
async function
|
|
221
|
+
async function O(e) {
|
|
177
222
|
let t = e.upstream.replace(/\/$/, ""), r = u(e.keyFile ?? ".seed/gateway-tunnel/operator.key.json"), i = new n(), a = i.createServer((e) => {
|
|
178
|
-
|
|
223
|
+
D(e, t);
|
|
179
224
|
});
|
|
180
225
|
return await a.listen(r), {
|
|
181
226
|
key: r.z32,
|
|
@@ -186,22 +231,48 @@ async function T(e) {
|
|
|
186
231
|
};
|
|
187
232
|
}
|
|
188
233
|
//#endregion
|
|
234
|
+
//#region src/tunnel/session.ts
|
|
235
|
+
async function k(e) {
|
|
236
|
+
let t = l(e.key), r = d(), i = new n(), a = null, o = null, s = async () => a && !a.destroyed ? a : o || (o = new Promise((e, n) => {
|
|
237
|
+
let s = i.connect(t, { keyPair: {
|
|
238
|
+
publicKey: r.publicKey,
|
|
239
|
+
secretKey: r.secretKey
|
|
240
|
+
} }), c = () => {
|
|
241
|
+
a = s, o = null, e(s);
|
|
242
|
+
};
|
|
243
|
+
s.once("open", c), s.once("connect", c), s.once("error", (e) => {
|
|
244
|
+
o = null, n(e);
|
|
245
|
+
});
|
|
246
|
+
}), o), c = Promise.resolve(), u = async (e) => {
|
|
247
|
+
let t = c.then(() => s()).then(e);
|
|
248
|
+
return c = t.catch(() => {}), t;
|
|
249
|
+
};
|
|
250
|
+
return {
|
|
251
|
+
key: e.key,
|
|
252
|
+
runOnTunnel: u,
|
|
253
|
+
forwardHttp: async (e, t) => u(async (n) => (await b(n, e, t), C(n))),
|
|
254
|
+
close: async () => {
|
|
255
|
+
a && !a.destroyed && a.destroy(), await i.destroy();
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
189
260
|
//#region src/tunnel/connect.ts
|
|
190
|
-
async function
|
|
261
|
+
async function A(e) {
|
|
191
262
|
let t = [];
|
|
192
263
|
for await (let n of e) t.push(Buffer.isBuffer(n) ? n : Buffer.from(n));
|
|
193
264
|
return Buffer.concat(t);
|
|
194
265
|
}
|
|
195
|
-
function
|
|
266
|
+
function j(e) {
|
|
196
267
|
return i.createServer((t, n) => {
|
|
197
268
|
(async () => {
|
|
198
269
|
try {
|
|
199
|
-
let r = await
|
|
270
|
+
let r = await A(t), i = t.url ?? "/", { meta: a, body: o } = await e({
|
|
200
271
|
method: t.method ?? "GET",
|
|
201
272
|
path: i,
|
|
202
|
-
headers:
|
|
203
|
-
},
|
|
204
|
-
n.writeHead(
|
|
273
|
+
headers: w(t.headers)
|
|
274
|
+
}, r);
|
|
275
|
+
n.writeHead(a.status, a.headers), n.end(o);
|
|
205
276
|
} catch (e) {
|
|
206
277
|
let t = e instanceof Error ? e.message : String(e);
|
|
207
278
|
n.headersSent || n.writeHead(502, { "Content-Type": "text/plain; charset=utf-8" }), n.end(`Gateway Hyper tunnel error: ${t}`);
|
|
@@ -209,43 +280,145 @@ function D(e) {
|
|
|
209
280
|
})();
|
|
210
281
|
});
|
|
211
282
|
}
|
|
212
|
-
async function
|
|
213
|
-
let t = e.host ?? "127.0.0.1",
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
};
|
|
220
|
-
n.once("open", r), n.once("connect", r), n.once("error", (e) => {
|
|
221
|
-
c = null, t(e);
|
|
222
|
-
});
|
|
223
|
-
}), c), f = Promise.resolve(), p = async (e) => {
|
|
224
|
-
let t = f.then(() => u()).then(e);
|
|
225
|
-
return f = t.catch(() => {}), t;
|
|
226
|
-
}, m = D((e) => p(e));
|
|
227
|
-
await new Promise((e, n) => {
|
|
228
|
-
m.once("error", n), m.listen(r, t, () => e());
|
|
283
|
+
async function M(e) {
|
|
284
|
+
let t = e.host ?? "127.0.0.1", n = e.port ?? 1984, r = await k({
|
|
285
|
+
key: e.key,
|
|
286
|
+
storePath: e.storePath
|
|
287
|
+
}), i = j((e, t) => r.forwardHttp(e, t));
|
|
288
|
+
await new Promise((e, r) => {
|
|
289
|
+
i.once("error", r), i.listen(n, t, () => e());
|
|
229
290
|
});
|
|
230
|
-
let
|
|
291
|
+
let a = i.address(), o = a && typeof a == "object" ? a.port : n, s = `http://${t}:${o}`;
|
|
231
292
|
return {
|
|
232
293
|
key: e.key,
|
|
233
294
|
host: t,
|
|
234
|
-
port:
|
|
235
|
-
baseUrl:
|
|
295
|
+
port: o,
|
|
296
|
+
baseUrl: s,
|
|
236
297
|
close: async () => {
|
|
237
|
-
await new Promise((e) =>
|
|
298
|
+
await new Promise((e) => i.close(() => e())), await r.close();
|
|
238
299
|
}
|
|
239
300
|
};
|
|
240
301
|
}
|
|
241
|
-
function
|
|
302
|
+
function N(e, t = "") {
|
|
242
303
|
let n = e.replace(/\/$/, "");
|
|
243
304
|
return t ? t.startsWith("/") ? `${n}${t}` : `${n}/${t}` : n;
|
|
244
305
|
}
|
|
245
306
|
//#endregion
|
|
307
|
+
//#region src/tunnel/mountPath.ts
|
|
308
|
+
function P(e, t) {
|
|
309
|
+
let n = e || "/", r = n.indexOf("?"), i = r === -1 ? n : n.slice(0, r), a = r === -1 ? "" : n.slice(r), o = (t || "").replace(/\/$/, "");
|
|
310
|
+
if (!o || o === "/") return `${i.startsWith("/") ? i : `/${i}`}${a}` || "/";
|
|
311
|
+
if (i === o || i.startsWith(`${o}/`)) {
|
|
312
|
+
let e = i.slice(o.length);
|
|
313
|
+
return `${e.startsWith("/") ? e : e ? `/${e}` : "/"}${a}`;
|
|
314
|
+
}
|
|
315
|
+
return `${i.startsWith("/") ? i : `/${i}`}${a}`;
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/tunnel/forward.ts
|
|
319
|
+
async function F(e) {
|
|
320
|
+
let t = [];
|
|
321
|
+
for await (let n of e) t.push(Buffer.isBuffer(n) ? n : Buffer.from(n));
|
|
322
|
+
return Buffer.concat(t);
|
|
323
|
+
}
|
|
324
|
+
async function I(e, t, n, r) {
|
|
325
|
+
try {
|
|
326
|
+
let i = await F(t), a = P(t.url ?? "/", r), o = {
|
|
327
|
+
method: t.method ?? "GET",
|
|
328
|
+
path: a,
|
|
329
|
+
headers: w(t.headers)
|
|
330
|
+
}, { meta: s, body: c } = await e.forwardHttp(o, i);
|
|
331
|
+
L(n, s, c);
|
|
332
|
+
} catch (e) {
|
|
333
|
+
let t = e instanceof Error ? e.message : String(e);
|
|
334
|
+
n.headersSent || n.writeHead(502, { "Content-Type": "text/plain; charset=utf-8" }), n.end(`Gateway Hyper tunnel error: ${t}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function L(e, t, n) {
|
|
338
|
+
e.writeHead(t.status, T(t.headers)), e.end(n);
|
|
339
|
+
}
|
|
340
|
+
async function R(e, t, n) {
|
|
341
|
+
try {
|
|
342
|
+
let r = new URL(t.url), i = P(`${r.pathname}${r.search}`, n), a = Buffer.from(await t.arrayBuffer()), o = {};
|
|
343
|
+
t.headers.forEach((e, t) => {
|
|
344
|
+
o[t] = e;
|
|
345
|
+
});
|
|
346
|
+
let s = {
|
|
347
|
+
method: t.method || "GET",
|
|
348
|
+
path: i,
|
|
349
|
+
headers: w(o)
|
|
350
|
+
}, { meta: c, body: l } = await e.forwardHttp(s, a);
|
|
351
|
+
return new Response(l, {
|
|
352
|
+
status: c.status,
|
|
353
|
+
headers: T(c.headers)
|
|
354
|
+
});
|
|
355
|
+
} catch (e) {
|
|
356
|
+
let t = e instanceof Error ? e.message : String(e);
|
|
357
|
+
return new Response(`Gateway Hyper tunnel error: ${t}`, {
|
|
358
|
+
status: 502,
|
|
359
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
//#endregion
|
|
364
|
+
//#region src/tunnel/proxyApp.ts
|
|
365
|
+
function z(e) {
|
|
366
|
+
let t = e.key?.trim() || (typeof process < "u" ? process.env.SEED_GATEWAY_HYPER_KEY?.trim() : "") || "";
|
|
367
|
+
if (!t) throw Error("createGatewayProxy requires options.key or SEED_GATEWAY_HYPER_KEY environment variable");
|
|
368
|
+
return t;
|
|
369
|
+
}
|
|
370
|
+
function B(e = {}) {
|
|
371
|
+
let t = e.mountPath?.trim() || "/api/seed-gateway", n = z(e), r = null, i = null, a = async () => {
|
|
372
|
+
if (r) return r;
|
|
373
|
+
if (i) return i;
|
|
374
|
+
i = k({
|
|
375
|
+
key: n,
|
|
376
|
+
storePath: e.storePath
|
|
377
|
+
}).then((e) => (r = e, i = null, e));
|
|
378
|
+
try {
|
|
379
|
+
return await i;
|
|
380
|
+
} catch (e) {
|
|
381
|
+
throw i = null, e;
|
|
382
|
+
}
|
|
383
|
+
}, o = {
|
|
384
|
+
mountPath: t,
|
|
385
|
+
start: async () => {
|
|
386
|
+
await a();
|
|
387
|
+
},
|
|
388
|
+
close: async () => {
|
|
389
|
+
if (i) try {
|
|
390
|
+
await i;
|
|
391
|
+
} catch {}
|
|
392
|
+
r &&= (await r.close(), null);
|
|
393
|
+
},
|
|
394
|
+
handleNode: (e, n) => {
|
|
395
|
+
(async () => {
|
|
396
|
+
try {
|
|
397
|
+
await I(await a(), e, n, t);
|
|
398
|
+
} catch (e) {
|
|
399
|
+
let t = e instanceof Error ? e.message : String(e);
|
|
400
|
+
n.headersSent || n.writeHead(502, { "Content-Type": "text/plain; charset=utf-8" }), n.end(`Gateway Hyper tunnel error: ${t}`);
|
|
401
|
+
}
|
|
402
|
+
})();
|
|
403
|
+
},
|
|
404
|
+
handleFetch: async (e) => {
|
|
405
|
+
try {
|
|
406
|
+
return await R(await a(), e, t);
|
|
407
|
+
} catch (e) {
|
|
408
|
+
let t = e instanceof Error ? e.message : String(e);
|
|
409
|
+
return new Response(`Gateway Hyper tunnel error: ${t}`, {
|
|
410
|
+
status: 502,
|
|
411
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
return e.eager && o.start().catch(() => {}), o;
|
|
417
|
+
}
|
|
418
|
+
//#endregion
|
|
246
419
|
//#region src/constants.ts
|
|
247
|
-
var
|
|
420
|
+
var V = "";
|
|
248
421
|
//#endregion
|
|
249
|
-
export {
|
|
422
|
+
export { V as DEFAULT_SEED_GATEWAY_HYPER_KEY, f as MAX_FRAME_BYTES, M as connectTunnel, B as createGatewayProxy, k as createTunnelSession, l as decodePublicKey, c as encodePublicKeyZ32, T as filterResponseHeaders, w as flattenRequestHeaders, u as loadOrCreateOperatorKeypair, N as localGatewayUrl, y as readFrame, x as readTunnelRequest, C as readTunnelResponse, O as serveTunnel, P as stripMountPath, v as writeFrame, b as writeTunnelRequest, S as writeTunnelResponse };
|
|
250
423
|
|
|
251
424
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/keys.ts","../src/tunnel/protocol.ts","../src/tunnel/proxy.ts","../src/tunnel/serve.ts","../src/tunnel/connect.ts","../src/constants.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport HyperDHT from 'hyperdht'\nimport ID from 'hypercore-id-encoding'\nimport type { OperatorKeyPair } from './types'\n\nexport interface PersistedKeyFile {\n publicKey: string\n secretKey: string\n encoding: 'hex'\n}\n\nfunction toHex(buf: Buffer): string {\n return buf.toString('hex')\n}\n\nfunction fromHex(hex: string): Buffer {\n return Buffer.from(hex, 'hex')\n}\n\nexport function encodePublicKeyZ32(publicKey: Buffer): string {\n return ID.encode(publicKey)\n}\n\nexport function decodePublicKey(key: string): Buffer {\n const trimmed = key.trim()\n if (!trimmed) {\n throw new Error('Gateway Hyper key is empty')\n }\n try {\n return Buffer.from(ID.decode(trimmed))\n } catch {\n if (/^[a-fA-F0-9]{64}$/.test(trimmed)) {\n return fromHex(trimmed)\n }\n throw new Error(`Invalid Gateway Hyper key (expected z32 or 64-char hex): ${trimmed.slice(0, 12)}…`)\n }\n}\n\nexport function loadOrCreateOperatorKeypair(keyFile: string): OperatorKeyPair {\n const resolved = path.resolve(keyFile)\n if (fs.existsSync(resolved)) {\n const raw = fs.readFileSync(resolved, 'utf8')\n const parsed = JSON.parse(raw) as PersistedKeyFile\n if (!parsed.publicKey || !parsed.secretKey) {\n throw new Error(`Invalid key file at ${resolved}: missing publicKey or secretKey`)\n }\n const publicKey = fromHex(parsed.publicKey)\n const secretKey = fromHex(parsed.secretKey)\n return {\n publicKey,\n secretKey,\n z32: encodePublicKeyZ32(publicKey),\n }\n }\n\n const dir = path.dirname(resolved)\n fs.mkdirSync(dir, { recursive: true })\n const kp = HyperDHT.keyPair()\n const persisted: PersistedKeyFile = {\n publicKey: toHex(kp.publicKey),\n secretKey: toHex(kp.secretKey),\n encoding: 'hex',\n }\n fs.writeFileSync(resolved, `${JSON.stringify(persisted, null, 2)}\\n`, { mode: 0o600 })\n return {\n publicKey: kp.publicKey,\n secretKey: kp.secretKey,\n z32: encodePublicKeyZ32(kp.publicKey),\n }\n}\n\nexport function createEphemeralClientKeypair(): OperatorKeyPair {\n const kp = HyperDHT.keyPair()\n return {\n publicKey: kp.publicKey,\n secretKey: kp.secretKey,\n z32: encodePublicKeyZ32(kp.publicKey),\n }\n}\n","import type { Readable, Writable } from 'node:stream'\nimport type { TunnelMeta, TunnelResponseMeta } from '../types'\n\n/** Maximum single frame payload (64 MiB). */\nexport const MAX_FRAME_BYTES = 64 * 1024 * 1024\n\nfunction readExact(stream: Readable, length: number): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = []\n let total = 0\n\n const onData = (chunk: Buffer) => {\n chunks.push(chunk)\n total += chunk.length\n if (total >= length) {\n cleanup()\n const buf = Buffer.concat(chunks)\n resolve(buf.subarray(0, length))\n const extra = buf.subarray(length)\n if (extra.length > 0) {\n stream.unshift(extra)\n }\n }\n }\n\n const onError = (err: Error) => {\n cleanup()\n reject(err)\n }\n\n const onEnd = () => {\n cleanup()\n reject(new Error(`Unexpected end of stream (expected ${length} bytes, got ${total})`))\n }\n\n const cleanup = () => {\n stream.off('data', onData)\n stream.off('error', onError)\n stream.off('end', onEnd)\n }\n\n stream.on('data', onData)\n stream.on('error', onError)\n stream.on('end', onEnd)\n })\n}\n\nfunction writeBuffer(stream: Writable, buf: Buffer): Promise<void> {\n return new Promise((resolve, reject) => {\n stream.write(buf, (err) => {\n if (err) reject(err)\n else resolve()\n })\n })\n}\n\nexport async function writeFrame(stream: Writable, payload: Buffer): Promise<void> {\n if (payload.length > MAX_FRAME_BYTES) {\n throw new Error(`Frame too large: ${payload.length} bytes (max ${MAX_FRAME_BYTES})`)\n }\n const header = Buffer.alloc(4)\n header.writeUInt32BE(payload.length, 0)\n await writeBuffer(stream, header)\n if (payload.length > 0) {\n await writeBuffer(stream, payload)\n }\n}\n\nexport async function readFrame(stream: Readable): Promise<Buffer> {\n const header = await readExact(stream, 4)\n const length = header.readUInt32BE(0)\n if (length === 0) {\n return Buffer.alloc(0)\n }\n if (length > MAX_FRAME_BYTES) {\n throw new Error(`Frame too large: ${length} bytes (max ${MAX_FRAME_BYTES})`)\n }\n return readExact(stream, length)\n}\n\nexport async function writeTunnelRequest(\n stream: Writable,\n meta: TunnelMeta,\n body: Buffer,\n): Promise<void> {\n const metaFrame = Buffer.from(JSON.stringify(meta), 'utf8')\n await writeFrame(stream, metaFrame)\n await writeFrame(stream, body)\n}\n\nexport async function readTunnelRequest(\n stream: Readable,\n): Promise<{ meta: TunnelMeta; body: Buffer }> {\n const metaBuf = await readFrame(stream)\n const meta = JSON.parse(metaBuf.toString('utf8')) as TunnelMeta\n if (!meta.method || !meta.path) {\n throw new Error('Invalid tunnel request meta: missing method or path')\n }\n meta.headers = meta.headers ?? {}\n const body = await readFrame(stream)\n return { meta, body }\n}\n\nexport async function writeTunnelResponse(\n stream: Writable,\n meta: TunnelResponseMeta,\n body: Buffer,\n): Promise<void> {\n const metaFrame = Buffer.from(JSON.stringify(meta), 'utf8')\n await writeFrame(stream, metaFrame)\n await writeFrame(stream, body)\n}\n\nexport async function readTunnelResponse(\n stream: Readable,\n): Promise<{ meta: TunnelResponseMeta; body: Buffer }> {\n const metaBuf = await readFrame(stream)\n const meta = JSON.parse(metaBuf.toString('utf8')) as TunnelResponseMeta\n if (typeof meta.status !== 'number') {\n throw new Error('Invalid tunnel response meta: missing status')\n }\n meta.headers = meta.headers ?? {}\n const body = await readFrame(stream)\n return { meta, body }\n}\n\n/** Normalize header values to strings for tunnel transport. */\nexport function flattenRequestHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (value == null) continue\n out[key] = Array.isArray(value) ? value.join(', ') : value\n }\n return out\n}\n\n/** Pick response headers safe to forward to the local HTTP client. */\nexport function filterResponseHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const skip = new Set([\n 'connection',\n 'keep-alive',\n 'transfer-encoding',\n 'upgrade',\n ])\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (skip.has(key.toLowerCase())) continue\n if (value == null) continue\n out[key] = Array.isArray(value) ? value.join(', ') : value\n }\n return out\n}\n","import http from 'node:http'\nimport { URL } from 'node:url'\nimport { filterResponseHeaders } from './protocol'\nimport type { TunnelMeta } from '../types'\n\nexport async function proxyTunnelRequest(\n upstreamBase: string,\n meta: TunnelMeta,\n body: Buffer,\n): Promise<{ status: number; headers: Record<string, string>; body: Buffer }> {\n const base = upstreamBase.replace(/\\/$/, '')\n const targetUrl = new URL(meta.path, `${base}/`)\n\n return new Promise((resolve, reject) => {\n const req = http.request(\n targetUrl,\n {\n method: meta.method,\n headers: meta.headers,\n },\n (res) => {\n const chunks: Buffer[] = []\n res.on('data', (chunk: Buffer) => chunks.push(chunk))\n res.on('end', () => {\n resolve({\n status: res.statusCode ?? 502,\n headers: filterResponseHeaders(res.headers as Record<string, string | string[] | undefined>),\n body: Buffer.concat(chunks),\n })\n })\n res.on('error', reject)\n },\n )\n req.on('error', reject)\n if (body.length > 0) {\n req.write(body)\n }\n req.end()\n })\n}\n","import type { Duplex } from 'node:stream'\nimport HyperDHT from 'hyperdht'\nimport { readTunnelRequest, writeTunnelResponse } from './protocol'\nimport { proxyTunnelRequest } from './proxy'\nimport { loadOrCreateOperatorKeypair } from '../keys'\nimport type { ServeTunnelOptions, ServeTunnelResult } from '../types'\n\nfunction handleConnection(\n socket: Duplex,\n upstream: string,\n): void {\n void (async () => {\n try {\n while (true) {\n const { meta, body } = await readTunnelRequest(socket)\n const result = await proxyTunnelRequest(upstream, meta, body)\n await writeTunnelResponse(\n socket,\n { status: result.status, headers: result.headers },\n result.body,\n )\n }\n } catch {\n socket.destroy()\n }\n })()\n}\n\n/**\n * Operator: accept Hyperswarm/DHT connections and proxy framed HTTP to upstream Traefik.\n */\nexport async function serveTunnel(options: ServeTunnelOptions): Promise<ServeTunnelResult> {\n const upstream = options.upstream.replace(/\\/$/, '')\n const keyFile = options.keyFile ?? '.seed/gateway-tunnel/operator.key.json'\n const keypair = loadOrCreateOperatorKeypair(keyFile)\n\n const dht = new HyperDHT()\n const server = dht.createServer((socket) => {\n handleConnection(socket as Duplex, upstream)\n })\n\n await server.listen(keypair)\n\n return {\n key: keypair.z32,\n upstream,\n close: async () => {\n await server.close()\n await dht.destroy()\n },\n }\n}\n","import http from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport HyperDHT from 'hyperdht'\nimport {\n flattenRequestHeaders,\n readTunnelResponse,\n writeTunnelRequest,\n} from './protocol'\nimport { createEphemeralClientKeypair, decodePublicKey } from '../keys'\nimport type { ConnectTunnelOptions, ConnectTunnelResult } from '../types'\n\nasync function readRequestBody(req: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n }\n return Buffer.concat(chunks)\n}\n\nfunction createLocalServer(\n runOnTunnel: <T>(fn: (socket: Duplex) => Promise<T>) => Promise<T>,\n): http.Server {\n return http.createServer((req, res) => {\n void (async () => {\n try {\n const body = await readRequestBody(req)\n const path = req.url ?? '/'\n const meta = {\n method: req.method ?? 'GET',\n path,\n headers: flattenRequestHeaders(req.headers as Record<string, string | string[] | undefined>),\n }\n\n const { meta: responseMeta, body: responseBody } = await runOnTunnel(async (socket) => {\n await writeTunnelRequest(socket, meta, body)\n return readTunnelResponse(socket)\n })\n\n res.writeHead(responseMeta.status, responseMeta.headers)\n res.end(responseBody)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' })\n }\n res.end(`Gateway Hyper tunnel error: ${message}`)\n }\n })()\n })\n}\n\n/**\n * Client: dial operator key and expose a localhost HTTP server that forwards through the tunnel.\n */\nexport async function connectTunnel(options: ConnectTunnelOptions): Promise<ConnectTunnelResult> {\n const host = options.host ?? '127.0.0.1'\n const port = options.port ?? 1984\n const remotePublicKey = decodePublicKey(options.key)\n const clientKey = createEphemeralClientKeypair()\n\n const dht = new HyperDHT()\n let activeSocket: Duplex | null = null\n let connecting: Promise<Duplex> | null = null\n\n const getSocket = async (): Promise<Duplex> => {\n if (activeSocket && !activeSocket.destroyed) {\n return activeSocket\n }\n if (connecting) {\n return connecting\n }\n connecting = new Promise<Duplex>((resolve, reject) => {\n const socket = dht.connect(remotePublicKey, {\n keyPair: { publicKey: clientKey.publicKey, secretKey: clientKey.secretKey },\n }) as Duplex\n const onReady = () => {\n activeSocket = socket\n connecting = null\n resolve(socket)\n }\n socket.once('open', onReady)\n socket.once('connect', onReady)\n socket.once('error', (err: Error) => {\n connecting = null\n reject(err)\n })\n })\n return connecting\n }\n\n /** Serialize tunnel transactions on one persistent socket. */\n let tunnelQueue: Promise<unknown> = Promise.resolve()\n const runOnTunnel = async <T>(fn: (socket: Duplex) => Promise<T>): Promise<T> => {\n const run = tunnelQueue.then(() => getSocket()).then(fn)\n tunnelQueue = run.catch(() => {})\n return run\n }\n\n const server = createLocalServer((handler) => runOnTunnel(handler))\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, host, () => resolve())\n })\n\n const address = server.address()\n const boundPort =\n address && typeof address === 'object' ? address.port : port\n\n const baseUrl = `http://${host}:${boundPort}`\n\n return {\n key: options.key,\n host,\n port: boundPort,\n baseUrl,\n close: async () => {\n await new Promise<void>((resolve) => server.close(() => resolve()))\n if (activeSocket && !activeSocket.destroyed) {\n activeSocket.destroy()\n }\n await dht.destroy()\n },\n }\n}\n\nexport function localGatewayUrl(baseUrl: string, path = ''): string {\n const base = baseUrl.replace(/\\/$/, '')\n if (!path) return base\n return path.startsWith('/') ? `${base}${path}` : `${base}/${path}`\n}\n","/**\n * Official Seed Protocol gateway Hyper tunnel operator key (z32).\n * Empty until ops generate and back up the first operator keypair.\n */\nexport const DEFAULT_SEED_GATEWAY_HYPER_KEY = ''\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/keys.ts","../src/tunnel/protocol.ts","../src/tunnel/proxy.ts","../src/tunnel/serve.ts","../src/tunnel/session.ts","../src/tunnel/connect.ts","../src/tunnel/mountPath.ts","../src/tunnel/forward.ts","../src/tunnel/proxyApp.ts","../src/constants.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport HyperDHT from 'hyperdht'\nimport ID from 'hypercore-id-encoding'\nimport type { OperatorKeyPair } from './types'\n\nexport interface PersistedKeyFile {\n publicKey: string\n secretKey: string\n encoding: 'hex'\n}\n\nfunction toHex(buf: Buffer): string {\n return buf.toString('hex')\n}\n\nfunction fromHex(hex: string): Buffer {\n return Buffer.from(hex, 'hex')\n}\n\nexport function encodePublicKeyZ32(publicKey: Buffer): string {\n return ID.encode(publicKey)\n}\n\nexport function decodePublicKey(key: string): Buffer {\n const trimmed = key.trim()\n if (!trimmed) {\n throw new Error('Gateway Hyper key is empty')\n }\n try {\n return Buffer.from(ID.decode(trimmed))\n } catch {\n if (/^[a-fA-F0-9]{64}$/.test(trimmed)) {\n return fromHex(trimmed)\n }\n throw new Error(`Invalid Gateway Hyper key (expected z32 or 64-char hex): ${trimmed.slice(0, 12)}…`)\n }\n}\n\nexport function loadOrCreateOperatorKeypair(keyFile: string): OperatorKeyPair {\n const resolved = path.resolve(keyFile)\n if (fs.existsSync(resolved)) {\n const raw = fs.readFileSync(resolved, 'utf8')\n const parsed = JSON.parse(raw) as PersistedKeyFile\n if (!parsed.publicKey || !parsed.secretKey) {\n throw new Error(`Invalid key file at ${resolved}: missing publicKey or secretKey`)\n }\n const publicKey = fromHex(parsed.publicKey)\n const secretKey = fromHex(parsed.secretKey)\n return {\n publicKey,\n secretKey,\n z32: encodePublicKeyZ32(publicKey),\n }\n }\n\n const dir = path.dirname(resolved)\n fs.mkdirSync(dir, { recursive: true })\n const kp = HyperDHT.keyPair()\n const persisted: PersistedKeyFile = {\n publicKey: toHex(kp.publicKey),\n secretKey: toHex(kp.secretKey),\n encoding: 'hex',\n }\n fs.writeFileSync(resolved, `${JSON.stringify(persisted, null, 2)}\\n`, { mode: 0o600 })\n return {\n publicKey: kp.publicKey,\n secretKey: kp.secretKey,\n z32: encodePublicKeyZ32(kp.publicKey),\n }\n}\n\nexport function createEphemeralClientKeypair(): OperatorKeyPair {\n const kp = HyperDHT.keyPair()\n return {\n publicKey: kp.publicKey,\n secretKey: kp.secretKey,\n z32: encodePublicKeyZ32(kp.publicKey),\n }\n}\n","import type { Readable, Writable } from 'node:stream'\nimport type { TunnelMeta, TunnelResponseMeta } from '../types'\n\n/** Maximum single frame payload (64 MiB). */\nexport const MAX_FRAME_BYTES = 64 * 1024 * 1024\n\ntype ByteWaiter = {\n length: number\n resolve: (buf: Buffer) => void\n reject: (err: Error) => void\n}\n\n/**\n * Per-stream byte buffer. HyperDHT duplexes do not reliably support `unshift`,\n * so we accumulate and slice locally instead of pushing leftovers back.\n */\nclass StreamByteReader {\n private buffer = Buffer.alloc(0)\n private waiters: ByteWaiter[] = []\n private ended = false\n private readonly onData: (chunk: Buffer) => void\n private readonly onError: (err: Error) => void\n private readonly onEnd: () => void\n\n constructor(private readonly stream: Readable) {\n this.onData = (chunk: Buffer) => {\n this.buffer = Buffer.concat([this.buffer, chunk])\n this.drain()\n }\n this.onError = (err: Error) => {\n const waiters = this.waiters.splice(0)\n for (const w of waiters) w.reject(err)\n }\n this.onEnd = () => {\n this.ended = true\n this.drain()\n if (this.waiters.length > 0) {\n const waiters = this.waiters.splice(0)\n for (const w of waiters) {\n w.reject(\n new Error(\n `Unexpected end of stream (expected ${w.length} bytes, got ${this.buffer.length})`,\n ),\n )\n }\n }\n }\n stream.on('data', this.onData)\n stream.on('error', this.onError)\n stream.on('end', this.onEnd)\n }\n\n readExact(length: number): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n if (length === 0) {\n resolve(Buffer.alloc(0))\n return\n }\n this.waiters.push({ length, resolve, reject })\n this.drain()\n if (this.ended && this.waiters.length > 0) {\n const waiters = this.waiters.splice(0)\n for (const w of waiters) {\n w.reject(\n new Error(\n `Unexpected end of stream (expected ${w.length} bytes, got ${this.buffer.length})`,\n ),\n )\n }\n }\n })\n }\n\n private drain(): void {\n while (this.waiters.length > 0) {\n const next = this.waiters[0]\n if (!next || this.buffer.length < next.length) break\n this.waiters.shift()\n const out = this.buffer.subarray(0, next.length)\n this.buffer = this.buffer.subarray(next.length)\n next.resolve(out)\n }\n }\n}\n\nconst readers = new WeakMap<Readable, StreamByteReader>()\n\nfunction getReader(stream: Readable): StreamByteReader {\n let reader = readers.get(stream)\n if (!reader) {\n reader = new StreamByteReader(stream)\n readers.set(stream, reader)\n }\n return reader\n}\n\nfunction readExact(stream: Readable, length: number): Promise<Buffer> {\n return getReader(stream).readExact(length)\n}\n\nfunction writeBuffer(stream: Writable, buf: Buffer): Promise<void> {\n return new Promise((resolve, reject) => {\n // HyperDHT duplexes often never invoke write(callback). Use return value + drain.\n try {\n const ok = stream.write(buf)\n if (ok) {\n resolve()\n return\n }\n const onDrain = () => {\n stream.off('error', onError)\n resolve()\n }\n const onError = (err: Error) => {\n stream.off('drain', onDrain)\n reject(err)\n }\n stream.once('drain', onDrain)\n stream.once('error', onError)\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)))\n }\n })\n}\n\nexport async function writeFrame(stream: Writable, payload: Buffer): Promise<void> {\n if (payload.length > MAX_FRAME_BYTES) {\n throw new Error(`Frame too large: ${payload.length} bytes (max ${MAX_FRAME_BYTES})`)\n }\n const header = Buffer.alloc(4)\n header.writeUInt32BE(payload.length, 0)\n // Single write — HyperDHT can drop/stall subsequent writes in the same turn.\n await writeBuffer(stream, Buffer.concat([header, payload]))\n}\n\nexport async function readFrame(stream: Readable): Promise<Buffer> {\n const header = await readExact(stream, 4)\n const length = header.readUInt32BE(0)\n if (length === 0) {\n return Buffer.alloc(0)\n }\n if (length > MAX_FRAME_BYTES) {\n throw new Error(`Frame too large: ${length} bytes (max ${MAX_FRAME_BYTES})`)\n }\n return readExact(stream, length)\n}\n\nexport async function writeTunnelRequest(\n stream: Writable,\n meta: TunnelMeta,\n body: Buffer,\n): Promise<void> {\n const metaFrame = Buffer.from(JSON.stringify(meta), 'utf8')\n await writeFrame(stream, metaFrame)\n await writeFrame(stream, body)\n}\n\nexport async function readTunnelRequest(\n stream: Readable,\n): Promise<{ meta: TunnelMeta; body: Buffer }> {\n const metaBuf = await readFrame(stream)\n const meta = JSON.parse(metaBuf.toString('utf8')) as TunnelMeta\n if (!meta.method || !meta.path) {\n throw new Error('Invalid tunnel request meta: missing method or path')\n }\n meta.headers = meta.headers ?? {}\n const body = await readFrame(stream)\n return { meta, body }\n}\n\nexport async function writeTunnelResponse(\n stream: Writable,\n meta: TunnelResponseMeta,\n body: Buffer,\n): Promise<void> {\n const metaFrame = Buffer.from(JSON.stringify(meta), 'utf8')\n await writeFrame(stream, metaFrame)\n await writeFrame(stream, body)\n}\n\nexport async function readTunnelResponse(\n stream: Readable,\n): Promise<{ meta: TunnelResponseMeta; body: Buffer }> {\n const metaBuf = await readFrame(stream)\n const meta = JSON.parse(metaBuf.toString('utf8')) as TunnelResponseMeta\n if (typeof meta.status !== 'number') {\n throw new Error('Invalid tunnel response meta: missing status')\n }\n meta.headers = meta.headers ?? {}\n const body = await readFrame(stream)\n return { meta, body }\n}\n\n/** Normalize header values to strings for tunnel transport. */\nexport function flattenRequestHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (value == null) continue\n out[key] = Array.isArray(value) ? value.join(', ') : value\n }\n return out\n}\n\n/** Pick response headers safe to forward to the local HTTP client. */\nexport function filterResponseHeaders(\n headers: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const skip = new Set([\n 'connection',\n 'keep-alive',\n 'transfer-encoding',\n 'upgrade',\n ])\n const out: Record<string, string> = {}\n for (const [key, value] of Object.entries(headers)) {\n if (skip.has(key.toLowerCase())) continue\n if (value == null) continue\n out[key] = Array.isArray(value) ? value.join(', ') : value\n }\n return out\n}\n","import http from 'node:http'\nimport { URL } from 'node:url'\nimport { filterResponseHeaders } from './protocol'\nimport type { TunnelMeta } from '../types'\n\nexport async function proxyTunnelRequest(\n upstreamBase: string,\n meta: TunnelMeta,\n body: Buffer,\n): Promise<{ status: number; headers: Record<string, string>; body: Buffer }> {\n const base = upstreamBase.replace(/\\/$/, '')\n const targetUrl = new URL(meta.path, `${base}/`)\n\n return new Promise((resolve, reject) => {\n const req = http.request(\n targetUrl,\n {\n method: meta.method,\n headers: meta.headers,\n },\n (res) => {\n const chunks: Buffer[] = []\n res.on('data', (chunk: Buffer) => chunks.push(chunk))\n res.on('end', () => {\n resolve({\n status: res.statusCode ?? 502,\n headers: filterResponseHeaders(res.headers as Record<string, string | string[] | undefined>),\n body: Buffer.concat(chunks),\n })\n })\n res.on('error', reject)\n },\n )\n req.on('error', reject)\n if (body.length > 0) {\n req.write(body)\n }\n req.end()\n })\n}\n","import type { Duplex } from 'node:stream'\nimport HyperDHT from 'hyperdht'\nimport { readTunnelRequest, writeTunnelResponse } from './protocol'\nimport { proxyTunnelRequest } from './proxy'\nimport { loadOrCreateOperatorKeypair } from '../keys'\nimport type { ServeTunnelOptions, ServeTunnelResult } from '../types'\n\nfunction handleConnection(\n socket: Duplex,\n upstream: string,\n): void {\n // Attach framed readers synchronously so HyperDHT early bytes are not lost.\n let pendingRequest = readTunnelRequest(socket)\n void (async () => {\n try {\n while (true) {\n const { meta, body } = await pendingRequest\n const result = await proxyTunnelRequest(upstream, meta, body)\n await writeTunnelResponse(\n socket,\n { status: result.status, headers: result.headers },\n result.body,\n )\n pendingRequest = readTunnelRequest(socket)\n }\n } catch {\n socket.destroy()\n }\n })()\n}\n\n/**\n * Operator: accept Hyperswarm/DHT connections and proxy framed HTTP to upstream Traefik.\n */\nexport async function serveTunnel(options: ServeTunnelOptions): Promise<ServeTunnelResult> {\n const upstream = options.upstream.replace(/\\/$/, '')\n const keyFile = options.keyFile ?? '.seed/gateway-tunnel/operator.key.json'\n const keypair = loadOrCreateOperatorKeypair(keyFile)\n\n const dht = new HyperDHT()\n const server = dht.createServer((socket) => {\n handleConnection(socket as Duplex, upstream)\n })\n\n await server.listen(keypair)\n\n return {\n key: keypair.z32,\n upstream,\n close: async () => {\n await server.close()\n await dht.destroy()\n },\n }\n}\n","import type { Duplex } from 'node:stream'\nimport HyperDHT from 'hyperdht'\nimport { createEphemeralClientKeypair, decodePublicKey } from '../keys'\nimport type { TunnelMeta, TunnelResponseMeta } from '../types'\nimport { readTunnelResponse, writeTunnelRequest } from './protocol'\n\nexport type TunnelSessionOptions = {\n /** Operator public key (z32 or hex) */\n key: string\n /** Optional store path for DHT client state */\n storePath?: string\n}\n\nexport type TunnelSession = {\n key: string\n runOnTunnel: <T>(fn: (socket: Duplex) => Promise<T>) => Promise<T>\n forwardHttp: (\n meta: TunnelMeta,\n body: Buffer,\n ) => Promise<{ meta: TunnelResponseMeta; body: Buffer }>\n close: () => Promise<void>\n}\n\n/**\n * Dial the operator HyperDHT key and expose a serialized request/response forwarder.\n */\nexport async function createTunnelSession(options: TunnelSessionOptions): Promise<TunnelSession> {\n const remotePublicKey = decodePublicKey(options.key)\n const clientKey = createEphemeralClientKeypair()\n\n const dht = new HyperDHT()\n let activeSocket: Duplex | null = null\n let connecting: Promise<Duplex> | null = null\n\n const getSocket = async (): Promise<Duplex> => {\n if (activeSocket && !activeSocket.destroyed) {\n return activeSocket\n }\n if (connecting) {\n return connecting\n }\n connecting = new Promise<Duplex>((resolve, reject) => {\n const socket = dht.connect(remotePublicKey, {\n keyPair: { publicKey: clientKey.publicKey, secretKey: clientKey.secretKey },\n }) as Duplex\n const onReady = () => {\n activeSocket = socket\n connecting = null\n resolve(socket)\n }\n socket.once('open', onReady)\n socket.once('connect', onReady)\n socket.once('error', (err: Error) => {\n connecting = null\n reject(err)\n })\n })\n return connecting\n }\n\n /** Serialize tunnel transactions on one persistent socket. */\n let tunnelQueue: Promise<unknown> = Promise.resolve()\n const runOnTunnel = async <T>(fn: (socket: Duplex) => Promise<T>): Promise<T> => {\n const run = tunnelQueue.then(() => getSocket()).then(fn)\n tunnelQueue = run.catch(() => {})\n return run\n }\n\n const forwardHttp = async (\n meta: TunnelMeta,\n body: Buffer,\n ): Promise<{ meta: TunnelResponseMeta; body: Buffer }> => {\n return runOnTunnel(async (socket) => {\n await writeTunnelRequest(socket, meta, body)\n return readTunnelResponse(socket)\n })\n }\n\n return {\n key: options.key,\n runOnTunnel,\n forwardHttp,\n close: async () => {\n if (activeSocket && !activeSocket.destroyed) {\n activeSocket.destroy()\n }\n await dht.destroy()\n },\n }\n}\n","import http from 'node:http'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n flattenRequestHeaders,\n} from './protocol'\nimport { createTunnelSession } from './session'\nimport type { ConnectTunnelOptions, ConnectTunnelResult } from '../types'\n\nasync function readRequestBody(req: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n }\n return Buffer.concat(chunks)\n}\n\nfunction createLocalServer(\n forwardHttp: (\n meta: { method: string; path: string; headers: Record<string, string> },\n body: Buffer,\n ) => Promise<{ meta: { status: number; headers: Record<string, string> }; body: Buffer }>,\n): http.Server {\n return http.createServer((req, res) => {\n void (async () => {\n try {\n const body = await readRequestBody(req)\n const path = req.url ?? '/'\n const meta = {\n method: req.method ?? 'GET',\n path,\n headers: flattenRequestHeaders(req.headers as Record<string, string | string[] | undefined>),\n }\n\n const { meta: responseMeta, body: responseBody } = await forwardHttp(meta, body)\n\n res.writeHead(responseMeta.status, responseMeta.headers)\n res.end(responseBody)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' })\n }\n res.end(`Gateway Hyper tunnel error: ${message}`)\n }\n })()\n })\n}\n\n/**\n * Client: dial operator key and expose a localhost HTTP server that forwards through the tunnel.\n */\nexport async function connectTunnel(options: ConnectTunnelOptions): Promise<ConnectTunnelResult> {\n const host = options.host ?? '127.0.0.1'\n const port = options.port ?? 1984\n\n const session = await createTunnelSession({\n key: options.key,\n storePath: options.storePath,\n })\n\n const server = createLocalServer((meta, body) => session.forwardHttp(meta, body))\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, host, () => resolve())\n })\n\n const address = server.address()\n const boundPort =\n address && typeof address === 'object' ? address.port : port\n\n const baseUrl = `http://${host}:${boundPort}`\n\n return {\n key: options.key,\n host,\n port: boundPort,\n baseUrl,\n close: async () => {\n await new Promise<void>((resolve) => server.close(() => resolve()))\n await session.close()\n },\n }\n}\n\nexport function localGatewayUrl(baseUrl: string, path = ''): string {\n const base = baseUrl.replace(/\\/$/, '')\n if (!path) return base\n return path.startsWith('/') ? `${base}${path}` : `${base}/${path}`\n}\n","/**\n * Strip a mount path prefix from an incoming request URL path (including query string).\n * If the path is already unmounted (framework stripped it), returns it unchanged (with leading `/`).\n */\nexport function stripMountPath(urlPath: string, mountPath: string): string {\n const raw = urlPath || '/'\n const qIndex = raw.indexOf('?')\n const pathname = qIndex === -1 ? raw : raw.slice(0, qIndex)\n const query = qIndex === -1 ? '' : raw.slice(qIndex)\n\n const mount = (mountPath || '').replace(/\\/$/, '')\n if (!mount || mount === '/') {\n const path = pathname.startsWith('/') ? pathname : `/${pathname}`\n return `${path}${query}` || '/'\n }\n\n if (pathname === mount || pathname.startsWith(`${mount}/`)) {\n const rest = pathname.slice(mount.length)\n const path = rest.startsWith('/') ? rest : rest ? `/${rest}` : '/'\n return `${path}${query}`\n }\n\n // Already stripped by the framework (e.g. Express mount)\n const path = pathname.startsWith('/') ? pathname : `/${pathname}`\n return `${path}${query}`\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n filterResponseHeaders,\n flattenRequestHeaders,\n} from './protocol'\nimport { stripMountPath } from './mountPath'\nimport type { TunnelSession } from './session'\nimport type { TunnelMeta, TunnelResponseMeta } from '../types'\n\nasync function readNodeBody(req: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n }\n return Buffer.concat(chunks)\n}\n\nexport async function forwardNodeRequest(\n session: TunnelSession,\n req: IncomingMessage,\n res: ServerResponse,\n mountPath: string,\n): Promise<void> {\n try {\n const body = await readNodeBody(req)\n const path = stripMountPath(req.url ?? '/', mountPath)\n const meta: TunnelMeta = {\n method: req.method ?? 'GET',\n path,\n headers: flattenRequestHeaders(req.headers as Record<string, string | string[] | undefined>),\n }\n const { meta: responseMeta, body: responseBody } = await session.forwardHttp(meta, body)\n writeNodeResponse(res, responseMeta, responseBody)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' })\n }\n res.end(`Gateway Hyper tunnel error: ${message}`)\n }\n}\n\nfunction writeNodeResponse(\n res: ServerResponse,\n meta: TunnelResponseMeta,\n body: Buffer,\n): void {\n res.writeHead(meta.status, filterResponseHeaders(meta.headers))\n res.end(body)\n}\n\nexport async function forwardFetchRequest(\n session: TunnelSession,\n request: Request,\n mountPath: string,\n): Promise<Response> {\n try {\n const url = new URL(request.url)\n const path = stripMountPath(`${url.pathname}${url.search}`, mountPath)\n const bodyBuf = Buffer.from(await request.arrayBuffer())\n const headerRecord: Record<string, string> = {}\n request.headers.forEach((value, key) => {\n headerRecord[key] = value\n })\n const meta: TunnelMeta = {\n method: request.method || 'GET',\n path,\n headers: flattenRequestHeaders(headerRecord),\n }\n const { meta: responseMeta, body: responseBody } = await session.forwardHttp(meta, bodyBuf)\n return new Response(responseBody, {\n status: responseMeta.status,\n headers: filterResponseHeaders(responseMeta.headers),\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n return new Response(`Gateway Hyper tunnel error: ${message}`, {\n status: 502,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n })\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createTunnelSession, type TunnelSession } from './session'\nimport { forwardFetchRequest, forwardNodeRequest } from './forward'\nimport type { CreateGatewayProxyOptions, GatewayProxy } from '../types'\n\nfunction resolveProxyKey(options: CreateGatewayProxyOptions): string {\n const key =\n options.key?.trim() ||\n (typeof process !== 'undefined' ? process.env.SEED_GATEWAY_HYPER_KEY?.trim() : '') ||\n ''\n if (!key) {\n throw new Error(\n 'createGatewayProxy requires options.key or SEED_GATEWAY_HYPER_KEY environment variable',\n )\n }\n return key\n}\n\n/**\n * App-server Hyper gateway proxy for hosted web apps (Path B).\n *\n * Mount on a Node route (Next.js Node runtime, Express, etc.). Keep the operator\n * Hyper key on the server only — browsers talk HTTP to `proxyBaseUrl`.\n *\n * Prefer Node.js over Bun for Holepunch natives.\n */\nexport function createGatewayProxy(options: CreateGatewayProxyOptions = {}): GatewayProxy {\n const mountPath = options.mountPath?.trim() || '/api/seed-gateway'\n const key = resolveProxyKey(options)\n\n let session: TunnelSession | null = null\n let starting: Promise<TunnelSession> | null = null\n\n const ensureSession = async (): Promise<TunnelSession> => {\n if (session) return session\n if (starting) return starting\n starting = createTunnelSession({ key, storePath: options.storePath }).then((s) => {\n session = s\n starting = null\n return s\n })\n try {\n return await starting\n } catch (err) {\n starting = null\n throw err\n }\n }\n\n const proxy: GatewayProxy = {\n mountPath,\n start: async () => {\n await ensureSession()\n },\n close: async () => {\n if (starting) {\n try {\n await starting\n } catch {\n /* ignore start failure during close */\n }\n }\n if (session) {\n await session.close()\n session = null\n }\n },\n handleNode: (req: IncomingMessage, res: ServerResponse) => {\n void (async () => {\n try {\n const s = await ensureSession()\n await forwardNodeRequest(s, req, res, mountPath)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' })\n }\n res.end(`Gateway Hyper tunnel error: ${message}`)\n }\n })()\n },\n handleFetch: async (request: Request): Promise<Response> => {\n try {\n const s = await ensureSession()\n return await forwardFetchRequest(s, request, mountPath)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n return new Response(`Gateway Hyper tunnel error: ${message}`, {\n status: 502,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n })\n }\n },\n }\n\n if (options.eager) {\n void proxy.start().catch(() => {\n /* first request will surface the error */\n })\n }\n\n return proxy\n}\n","/**\n * Official Seed Protocol gateway Hyper tunnel operator key (z32).\n * Empty until ops generate and back up the first operator keypair.\n */\nexport const DEFAULT_SEED_GATEWAY_HYPER_KEY = ''\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/tunnel/connect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/tunnel/connect.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AA0CzE;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAgC/F;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,MAAM,CAIlE"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { TunnelSession } from './session';
|
|
3
|
+
export declare function forwardNodeRequest(session: TunnelSession, req: IncomingMessage, res: ServerResponse, mountPath: string): Promise<void>;
|
|
4
|
+
export declare function forwardFetchRequest(session: TunnelSession, request: Request, mountPath: string): Promise<Response>;
|
|
5
|
+
//# sourceMappingURL=forward.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forward.d.ts","sourceRoot":"","sources":["../../src/tunnel/forward.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAMhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAW9C,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAkBf;AAWD,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,CAAC,CA0BnB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip a mount path prefix from an incoming request URL path (including query string).
|
|
3
|
+
* If the path is already unmounted (framework stripped it), returns it unchanged (with leading `/`).
|
|
4
|
+
*/
|
|
5
|
+
export declare function stripMountPath(urlPath: string, mountPath: string): string;
|
|
6
|
+
//# sourceMappingURL=mountPath.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mountPath.d.ts","sourceRoot":"","sources":["../../src/tunnel/mountPath.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAqBzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/tunnel/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAE9D,6CAA6C;AAC7C,eAAO,MAAM,eAAe,QAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/tunnel/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAE9D,6CAA6C;AAC7C,eAAO,MAAM,eAAe,QAAmB,CAAA;AAyH/C,wBAAsB,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQjF;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAUjE;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,QAAQ,EAChB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,QAAQ,GACf,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAS7C;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,QAAQ,EAChB,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,QAAQ,GACf,OAAO,CAAC;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CASrD;AAED,+DAA+D;AAC/D,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB;AAED,sEAAsE;AACtE,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcxB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CreateGatewayProxyOptions, GatewayProxy } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* App-server Hyper gateway proxy for hosted web apps (Path B).
|
|
4
|
+
*
|
|
5
|
+
* Mount on a Node route (Next.js Node runtime, Express, etc.). Keep the operator
|
|
6
|
+
* Hyper key on the server only — browsers talk HTTP to `proxyBaseUrl`.
|
|
7
|
+
*
|
|
8
|
+
* Prefer Node.js over Bun for Holepunch natives.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createGatewayProxy(options?: CreateGatewayProxyOptions): GatewayProxy;
|
|
11
|
+
//# sourceMappingURL=proxyApp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxyApp.d.ts","sourceRoot":"","sources":["../../src/tunnel/proxyApp.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAevE;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,YAAY,CA4ExF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/tunnel/serve.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/tunnel/serve.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AA0BrE;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoBzF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Duplex } from 'node:stream';
|
|
2
|
+
import type { TunnelMeta, TunnelResponseMeta } from '../types';
|
|
3
|
+
export type TunnelSessionOptions = {
|
|
4
|
+
/** Operator public key (z32 or hex) */
|
|
5
|
+
key: string;
|
|
6
|
+
/** Optional store path for DHT client state */
|
|
7
|
+
storePath?: string;
|
|
8
|
+
};
|
|
9
|
+
export type TunnelSession = {
|
|
10
|
+
key: string;
|
|
11
|
+
runOnTunnel: <T>(fn: (socket: Duplex) => Promise<T>) => Promise<T>;
|
|
12
|
+
forwardHttp: (meta: TunnelMeta, body: Buffer) => Promise<{
|
|
13
|
+
meta: TunnelResponseMeta;
|
|
14
|
+
body: Buffer;
|
|
15
|
+
}>;
|
|
16
|
+
close: () => Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Dial the operator HyperDHT key and expose a serialized request/response forwarder.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createTunnelSession(options: TunnelSessionOptions): Promise<TunnelSession>;
|
|
22
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/tunnel/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGzC,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAG9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IAClE,WAAW,EAAE,CACX,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,KACT,OAAO,CAAC;QAAE,IAAI,EAAE,kBAAkB,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACxD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CA+D/F"}
|
package/dist/types.d.ts
CHANGED
|
@@ -43,4 +43,33 @@ export type OperatorKeyPair = {
|
|
|
43
43
|
secretKey: Buffer;
|
|
44
44
|
z32: string;
|
|
45
45
|
};
|
|
46
|
+
export type CreateGatewayProxyOptions = {
|
|
47
|
+
/**
|
|
48
|
+
* Operator public key (z32 or hex).
|
|
49
|
+
* Defaults to `process.env.SEED_GATEWAY_HYPER_KEY`.
|
|
50
|
+
*/
|
|
51
|
+
key?: string;
|
|
52
|
+
/**
|
|
53
|
+
* URL path prefix the app mounts the proxy on (stripped before tunneling).
|
|
54
|
+
* Default `/api/seed-gateway`.
|
|
55
|
+
*/
|
|
56
|
+
mountPath?: string;
|
|
57
|
+
/** Optional DHT client state directory */
|
|
58
|
+
storePath?: string;
|
|
59
|
+
/** Start the Hyper session immediately (default: lazy on first request) */
|
|
60
|
+
eager?: boolean;
|
|
61
|
+
};
|
|
62
|
+
export type GatewayProxy = {
|
|
63
|
+
mountPath: string;
|
|
64
|
+
/** Ensure the Hyper tunnel session is connected */
|
|
65
|
+
start: () => Promise<void>;
|
|
66
|
+
close: () => Promise<void>;
|
|
67
|
+
/** Express / `node:http` style handler */
|
|
68
|
+
handleNode: (req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse) => void;
|
|
69
|
+
/**
|
|
70
|
+
* Fetch API handler for Next.js App Router (Node runtime only — not Edge).
|
|
71
|
+
* Export as GET/POST/PUT/PATCH/DELETE.
|
|
72
|
+
*/
|
|
73
|
+
handleFetch: (request: Request) => Promise<Response>;
|
|
74
|
+
};
|
|
46
75
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAA;IAChB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAA;IAChB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,mDAAmD;IACnD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,0CAA0C;IAC1C,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,WAAW,EAAE,eAAe,EAAE,GAAG,EAAE,OAAO,WAAW,EAAE,cAAc,KAAK,IAAI,CAAA;IACvG;;;OAGG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CACrD,CAAA"}
|