shiplocal 0.1.3 → 0.1.5

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/dist/index.js CHANGED
@@ -4109,6 +4109,10 @@ var errorMessageSchema = external_exports.object({
4109
4109
  type: external_exports.literal("error"),
4110
4110
  message: external_exports.string()
4111
4111
  });
4112
+ var terminatedMessageSchema = external_exports.object({
4113
+ type: external_exports.literal("terminated"),
4114
+ message: external_exports.string()
4115
+ });
4112
4116
  var tunnelMessageSchema = external_exports.discriminatedUnion("type", [
4113
4117
  registerMessageSchema,
4114
4118
  registeredMessageSchema,
@@ -4116,7 +4120,8 @@ var tunnelMessageSchema = external_exports.discriminatedUnion("type", [
4116
4120
  pongMessageSchema,
4117
4121
  tunnelRequestMessageSchema,
4118
4122
  tunnelResponseMessageSchema,
4119
- errorMessageSchema
4123
+ errorMessageSchema,
4124
+ terminatedMessageSchema
4120
4125
  ]);
4121
4126
  function parseTunnelMessage(data) {
4122
4127
  const parsed = typeof data === "string" ? JSON.parse(data) : data;
@@ -4153,6 +4158,7 @@ import { WebSocket } from "ws";
4153
4158
 
4154
4159
  // ../tunnel-client/dist/local-proxy.js
4155
4160
  import http from "node:http";
4161
+ var LOOPBACK_HOSTS = ["127.0.0.1", "::1"];
4156
4162
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
4157
4163
  "connection",
4158
4164
  "keep-alive",
@@ -4168,6 +4174,9 @@ var STRIP_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
4168
4174
  "content-encoding",
4169
4175
  "content-length"
4170
4176
  ]);
4177
+ function isConnectionRefused(err) {
4178
+ return err instanceof Error && "code" in err && err.code === "ECONNREFUSED";
4179
+ }
4171
4180
  function sanitizeRequestHeaders(headers, localPort) {
4172
4181
  const result = {};
4173
4182
  for (const [key, value] of Object.entries(headers)) {
@@ -4192,12 +4201,10 @@ function sanitizeResponseHeaders(headers) {
4192
4201
  }
4193
4202
  return result;
4194
4203
  }
4195
- async function forwardToLocal(localPort, message) {
4196
- const body = decodeBody(message.body);
4197
- const pathWithQuery = message.query ? `${message.path}?${message.query}` : message.path;
4204
+ function forwardToHost(hostname, localPort, message, body, pathWithQuery) {
4198
4205
  return new Promise((resolve, reject) => {
4199
4206
  const req = http.request({
4200
- hostname: "127.0.0.1",
4207
+ hostname,
4201
4208
  port: localPort,
4202
4209
  method: message.method,
4203
4210
  path: pathWithQuery,
@@ -4234,6 +4241,23 @@ async function forwardToLocal(localPort, message) {
4234
4241
  req.end();
4235
4242
  });
4236
4243
  }
4244
+ async function forwardToLocal(localPort, message) {
4245
+ const body = decodeBody(message.body);
4246
+ const pathWithQuery = message.query ? `${message.path}?${message.query}` : message.path;
4247
+ let lastError;
4248
+ for (const hostname of LOOPBACK_HOSTS) {
4249
+ try {
4250
+ return await forwardToHost(hostname, localPort, message, body, pathWithQuery);
4251
+ } catch (err) {
4252
+ if (isConnectionRefused(err)) {
4253
+ lastError = err;
4254
+ continue;
4255
+ }
4256
+ throw err;
4257
+ }
4258
+ }
4259
+ throw lastError ?? new Error("Local server unreachable");
4260
+ }
4237
4261
 
4238
4262
  // ../tunnel-client/dist/client.js
4239
4263
  function toWebSocketUrl(serverUrl) {
@@ -4247,9 +4271,11 @@ function toWebSocketUrl(serverUrl) {
4247
4271
  function formatLocalProxyError(err, localPort) {
4248
4272
  if (err instanceof Error && "code" in err && err.code === "ECONNREFUSED") {
4249
4273
  return [
4250
- `Nothing is running on http://127.0.0.1:${String(localPort)}.`,
4274
+ `Nothing is listening on port ${String(localPort)} (tried 127.0.0.1 and ::1).`,
4251
4275
  "",
4252
4276
  "Start your local server on that port, then refresh this page.",
4277
+ "If your browser works on localhost but the tunnel does not, your dev server may only",
4278
+ "listen on IPv6 \u2014 bind to 0.0.0.0 or 127.0.0.1 (e.g. next dev -H 0.0.0.0).",
4253
4279
  `If your app uses a different port, restart the CLI with that port (e.g. shiplocal ${String(localPort)}).`
4254
4280
  ].join("\n");
4255
4281
  }
@@ -4328,6 +4354,13 @@ function createTunnelClient(options) {
4328
4354
  }
4329
4355
  return;
4330
4356
  }
4357
+ if (message.type === "terminated") {
4358
+ intentionalClose = true;
4359
+ publicUrl = null;
4360
+ options.onTerminated?.(message.message);
4361
+ ws?.close();
4362
+ return;
4363
+ }
4331
4364
  if (message.type === "ping") {
4332
4365
  ws?.send(JSON.stringify({ type: "pong" }));
4333
4366
  return;
@@ -4465,7 +4498,7 @@ async function resolveToken() {
4465
4498
  }
4466
4499
 
4467
4500
  // src/api.ts
4468
- function isConnectionRefused(err) {
4501
+ function isConnectionRefused2(err) {
4469
4502
  if (!(err instanceof Error)) return false;
4470
4503
  const cause = err.cause;
4471
4504
  if (cause instanceof Error && "code" in cause && cause.code === "ECONNREFUSED") {
@@ -4477,7 +4510,7 @@ function isConnectionRefused(err) {
4477
4510
  return err.message.includes("ECONNREFUSED") || err.message.includes("fetch failed");
4478
4511
  }
4479
4512
  function formatServerConnectionError(apiUrl, err) {
4480
- if (isConnectionRefused(err)) {
4513
+ if (isConnectionRefused2(err)) {
4481
4514
  return [
4482
4515
  `Cannot reach ShipLocal server at ${apiUrl}.`,
4483
4516
  "",
@@ -4508,9 +4541,10 @@ async function postJson(path, body, apiUrl) {
4508
4541
 
4509
4542
  // src/local-port.ts
4510
4543
  import net from "node:net";
4511
- function isLocalPortOpen(port, timeoutMs = 2e3) {
4544
+ var LOOPBACK_HOSTS2 = ["127.0.0.1", "::1"];
4545
+ function canConnect(host, port, timeoutMs) {
4512
4546
  return new Promise((resolve) => {
4513
- const socket = net.connect({ port, host: "127.0.0.1" });
4547
+ const socket = net.connect({ port, host });
4514
4548
  const finish = (open) => {
4515
4549
  socket.removeAllListeners();
4516
4550
  socket.destroy();
@@ -4528,10 +4562,18 @@ function isLocalPortOpen(port, timeoutMs = 2e3) {
4528
4562
  });
4529
4563
  });
4530
4564
  }
4565
+ async function isLocalPortOpen(port, timeoutMs = 2e3) {
4566
+ for (const host of LOOPBACK_HOSTS2) {
4567
+ if (await canConnect(host, port, timeoutMs)) {
4568
+ return true;
4569
+ }
4570
+ }
4571
+ return false;
4572
+ }
4531
4573
 
4532
4574
  // src/index.ts
4533
4575
  var program = new Command();
4534
- program.name("shiplocal").description("Share localhost with clients in seconds").version("0.1.3");
4576
+ program.name("shiplocal").description("Share localhost with clients in seconds").version("0.1.5");
4535
4577
  program.command("login").description("Authenticate with ShipLocal Cloud").action(async () => {
4536
4578
  const rl = createInterface({ input, output });
4537
4579
  const apiUrl = await resolveApiUrlAsync();
@@ -4612,6 +4654,12 @@ program.argument("[port]", "Local port to expose", String(DEFAULT_TUNNEL_PORT)).
4612
4654
  },
4613
4655
  onReconnecting: (attempt) => {
4614
4656
  console.log(`Reconnecting\u2026 (attempt ${String(attempt)})`);
4657
+ },
4658
+ onTerminated: (message) => {
4659
+ console.log("");
4660
+ console.log(message);
4661
+ console.log("Run shiplocal again to start a new tunnel.");
4662
+ console.log("");
4615
4663
  }
4616
4664
  });
4617
4665
  const shutdown = () => {