@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 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**, with a localhost HTTP sidecar for apps and RSS-style tooling.
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 { serveTunnel, connectTunnel } from '@seedprotocol/gateway-hyper'
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
@@ -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;AAEjE,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,GAChB,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,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
- function p(e, t) {
64
- return new Promise((n, r) => {
65
- let i = [], a = 0, o = (r) => {
66
- if (i.push(r), a += r.length, a >= t) {
67
- l();
68
- let r = Buffer.concat(i);
69
- n(r.subarray(0, t));
70
- let a = r.subarray(t);
71
- a.length > 0 && e.unshift(a);
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
- }, s = (e) => {
74
- l(), r(e);
75
- }, c = () => {
76
- l(), r(/* @__PURE__ */ Error(`Unexpected end of stream (expected ${t} bytes, got ${a})`));
77
- }, l = () => {
78
- e.off("data", o), e.off("error", s), e.off("end", c);
79
- };
80
- e.on("data", o), e.on("error", s), e.on("end", c);
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 m(e, t) {
113
+ function g(e, t) {
114
+ return h(e).readExact(t);
115
+ }
116
+ function _(e, t) {
84
117
  return new Promise((n, r) => {
85
- e.write(t, (e) => {
86
- e ? r(e) : n();
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 h(e, t) {
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 m(e, n), t.length > 0 && await m(e, t);
137
+ n.writeUInt32BE(t.length, 0), await _(e, Buffer.concat([n, t]));
94
138
  }
95
- async function g(e) {
96
- let t = (await p(e, 4)).readUInt32BE(0);
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 p(e, t);
143
+ return g(e, t);
100
144
  }
101
- async function _(e, t, n) {
102
- await h(e, Buffer.from(JSON.stringify(t), "utf8")), await h(e, n);
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 v(e) {
105
- let t = await g(e), n = JSON.parse(t.toString("utf8"));
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 g(e)
153
+ body: await y(e)
110
154
  };
111
155
  }
112
- async function y(e, t, n) {
113
- await h(e, Buffer.from(JSON.stringify(t), "utf8")), await h(e, n);
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 b(e) {
116
- let t = await g(e), n = JSON.parse(t.toString("utf8"));
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 g(e)
164
+ body: await y(e)
121
165
  };
122
166
  }
123
- function x(e) {
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 S(e) {
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 C(e, t, n) {
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: S(t.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 w(e, t) {
205
+ function D(e, t) {
206
+ let n = x(e);
162
207
  (async () => {
163
208
  try {
164
209
  for (;;) {
165
- let { meta: n, body: r } = await v(e), i = await C(t, n, r);
166
- await y(e, {
167
- status: i.status,
168
- headers: i.headers
169
- }, i.body);
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 T(e) {
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
- w(e, t);
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 E(e) {
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 D(e) {
266
+ function j(e) {
196
267
  return i.createServer((t, n) => {
197
268
  (async () => {
198
269
  try {
199
- let r = await E(t), i = t.url ?? "/", a = {
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: x(t.headers)
203
- }, { meta: o, body: s } = await e(async (e) => (await _(e, a, r), b(e)));
204
- n.writeHead(o.status, o.headers), n.end(s);
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 O(e) {
213
- let t = e.host ?? "127.0.0.1", r = e.port ?? 1984, i = l(e.key), a = d(), o = new n(), s = null, c = null, u = async () => s && !s.destroyed ? s : c || (c = new Promise((e, t) => {
214
- let n = o.connect(i, { keyPair: {
215
- publicKey: a.publicKey,
216
- secretKey: a.secretKey
217
- } }), r = () => {
218
- s = n, c = null, e(n);
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 h = m.address(), g = h && typeof h == "object" ? h.port : r, _ = `http://${t}:${g}`;
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: g,
235
- baseUrl: _,
295
+ port: o,
296
+ baseUrl: s,
236
297
  close: async () => {
237
- await new Promise((e) => m.close(() => e())), s && !s.destroyed && s.destroy(), await o.destroy();
298
+ await new Promise((e) => i.close(() => e())), await r.close();
238
299
  }
239
300
  };
240
301
  }
241
- function k(e, t = "") {
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 A = "";
420
+ var V = "";
248
421
  //#endregion
249
- export { A as DEFAULT_SEED_GATEWAY_HYPER_KEY, f as MAX_FRAME_BYTES, O as connectTunnel, l as decodePublicKey, c as encodePublicKeyZ32, S as filterResponseHeaders, x as flattenRequestHeaders, u as loadOrCreateOperatorKeypair, k as localGatewayUrl, g as readFrame, v as readTunnelRequest, b as readTunnelResponse, T as serveTunnel, h as writeFrame, _ as writeTunnelRequest, y as writeTunnelResponse };
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":"AAUA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AA0CzE;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsE/F;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,MAAM,CAIlE"}
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;AAoD/C,wBAAsB,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUjF;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"}
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;AAuBrE;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoBzF"}
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
@@ -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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seedprotocol/gateway-hyper",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Hyperswarm HTTP tunnel for Seed Protocol gateway + upload API infrastructure",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",