shiplocal 0.1.1 → 0.1.3

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
@@ -20,15 +20,16 @@ Requires **Node.js 20+**.
20
20
  ### 1. Log in
21
21
 
22
22
  ```bash
23
- export SHIPLOCAL_API_URL=https://shiplocal.cloud
24
23
  shiplocal login
25
24
  ```
26
25
 
27
26
  Create an account at https://app.shiplocal.cloud if you don't have one.
28
27
 
28
+ No `export` needed — the CLI defaults to **https://shiplocal.cloud** and saves your server URL when you log in.
29
+
29
30
  ### 2. Start your local app
30
31
 
31
- Run your project locally (Next.js, Vite, Rails, etc.) on a port — for example **3000**:
32
+ Run your project locally on a port — for example **3000**:
32
33
 
33
34
  ```bash
34
35
  npm run dev
@@ -36,10 +37,7 @@ npm run dev
36
37
 
37
38
  ### 3. Open a tunnel
38
39
 
39
- In a second terminal:
40
-
41
40
  ```bash
42
- export SHIPLOCAL_API_URL=https://shiplocal.cloud
43
41
  shiplocal 3000
44
42
  ```
45
43
 
@@ -69,30 +67,38 @@ Clients open the public URL, click **💬**, pick an element, and leave feedback
69
67
  | `shiplocal 3000` | Tunnel local port 3000 |
70
68
  | `shiplocal 3000 --password secret` | Password-protect the preview URL |
71
69
 
72
- ## Environment variables
70
+ ## Environment variables (optional)
73
71
 
74
- | Variable | Default | Description |
75
- | ------------------- | ----------------------- | ------------------------------------------------------------- |
76
- | `SHIPLOCAL_API_URL` | `http://localhost:4000` | API server URL. Use `https://shiplocal.cloud` for production. |
77
- | `SHIPLOCAL_TOKEN` | — | API token (alternative to `shiplocal login`) |
72
+ | Variable | When to use |
73
+ | ------------------- | -------------------------------------------------------------- |
74
+ | `SHIPLOCAL_API_URL` | Self-hosted server or local dev (e.g. `http://localhost:4000`) |
75
+ | `SHIPLOCAL_TOKEN` | Skip `login` by providing an API token directly |
78
76
 
79
- Credentials are saved to `~/.shiplocal/config.json`.
77
+ Credentials are saved to `~/.shiplocal/config.json` including the API URL from your last login.
80
78
 
81
- ## Self-hosting
79
+ **You do not need `export SHIPLOCAL_API_URL` for ShipLocal Cloud** — only set it if you use a custom server.
82
80
 
83
- To run your own ShipLocal server instead of ShipLocal Cloud, point `SHIPLOCAL_API_URL` at your server:
81
+ ### Local development against your own server
82
+
83
+ ```bash
84
+ export SHIPLOCAL_API_URL=http://localhost:4000
85
+ shiplocal login
86
+ shiplocal 3000
87
+ ```
88
+
89
+ ## Self-hosting
84
90
 
85
91
  ```bash
86
92
  export SHIPLOCAL_API_URL=https://your-server.example.com
87
93
  shiplocal login
88
94
  ```
89
95
 
90
- See the [self-hosting guide](https://github.com/ship-local/shiplocal/blob/main/docs/self-hosting.md) in the main repo.
96
+ See the [self-hosting guide](https://github.com/ship-local/shiplocal/blob/main/docs/self-hosting.md).
91
97
 
92
98
  ## Troubleshooting
93
99
 
94
100
  **`command not found: shiplocal`**
95
- Ensure npm global bin is on your PATH. Run `npm bin -g` and add that directory to your shell config.
101
+ Run `npm bin -g` and add that directory to your PATH.
96
102
 
97
103
  **Blank page on public URL**
98
104
  Make sure your local app is running on the port you passed to the CLI.
@@ -100,9 +106,6 @@ Make sure your local app is running on the port you passed to the CLI.
100
106
  **`Not authenticated`**
101
107
  Run `shiplocal login` first.
102
108
 
103
- **Wrong server / login fails**
104
- Check `SHIPLOCAL_API_URL` is set to `https://shiplocal.cloud` for production.
105
-
106
109
  ## Links
107
110
 
108
111
  - [GitHub](https://github.com/ship-local/shiplocal)
package/dist/index.js CHANGED
@@ -4071,7 +4071,7 @@ var DEFAULT_TUNNEL_PORT = 3e3;
4071
4071
  // ../shared/dist/protocol.js
4072
4072
  var TUNNEL_WS_PATH = "/tunnel";
4073
4073
  var DEFAULT_TUNNEL_EXPIRY_MS = 2 * 60 * 60 * 1e3;
4074
- var MAX_BODY_BYTES = 10 * 1024 * 1024;
4074
+ var MAX_BODY_BYTES = 50 * 1024 * 1024;
4075
4075
  var headersSchema = external_exports.record(external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]));
4076
4076
  var registerMessageSchema = external_exports.object({
4077
4077
  type: external_exports.literal("register"),
@@ -4168,7 +4168,7 @@ var STRIP_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
4168
4168
  "content-encoding",
4169
4169
  "content-length"
4170
4170
  ]);
4171
- function sanitizeRequestHeaders(headers) {
4171
+ function sanitizeRequestHeaders(headers, localPort) {
4172
4172
  const result = {};
4173
4173
  for (const [key, value] of Object.entries(headers)) {
4174
4174
  const lower = key.toLowerCase();
@@ -4177,6 +4177,7 @@ function sanitizeRequestHeaders(headers) {
4177
4177
  }
4178
4178
  result[key] = value;
4179
4179
  }
4180
+ result["host"] = `localhost:${String(localPort)}`;
4180
4181
  return result;
4181
4182
  }
4182
4183
  function sanitizeResponseHeaders(headers) {
@@ -4200,7 +4201,7 @@ async function forwardToLocal(localPort, message) {
4200
4201
  port: localPort,
4201
4202
  method: message.method,
4202
4203
  path: pathWithQuery,
4203
- headers: sanitizeRequestHeaders(message.headers)
4204
+ headers: sanitizeRequestHeaders(message.headers, localPort)
4204
4205
  }, (res) => {
4205
4206
  const chunks = [];
4206
4207
  let totalSize = 0;
@@ -4358,7 +4359,9 @@ function createTunnelClient(options) {
4358
4359
  ws.close();
4359
4360
  }
4360
4361
  }
4361
- const socket = new WebSocket(toWebSocketUrl(options.serverUrl));
4362
+ const socket = new WebSocket(toWebSocketUrl(options.serverUrl), {
4363
+ maxPayload: 64 * 1024 * 1024
4364
+ });
4362
4365
  ws = socket;
4363
4366
  socket.on("open", () => {
4364
4367
  socket.send(JSON.stringify({
@@ -4444,8 +4447,16 @@ async function clearConfig() {
4444
4447
  } catch {
4445
4448
  }
4446
4449
  }
4447
- function resolveApiUrl() {
4448
- return process.env["SHIPLOCAL_API_URL"] ?? "http://localhost:4000";
4450
+ var DEFAULT_CLOUD_API_URL = "https://shiplocal.cloud";
4451
+ async function resolveApiUrlAsync() {
4452
+ if (process.env["SHIPLOCAL_API_URL"]) {
4453
+ return process.env["SHIPLOCAL_API_URL"];
4454
+ }
4455
+ const config = await loadConfig();
4456
+ if (config?.apiUrl) {
4457
+ return config.apiUrl;
4458
+ }
4459
+ return DEFAULT_CLOUD_API_URL;
4449
4460
  }
4450
4461
  async function resolveToken() {
4451
4462
  if (process.env["SHIPLOCAL_TOKEN"]) return process.env["SHIPLOCAL_TOKEN"];
@@ -4479,16 +4490,17 @@ function formatServerConnectionError(apiUrl, err) {
4479
4490
  }
4480
4491
  return err instanceof Error ? err.message : "Request failed";
4481
4492
  }
4482
- async function postJson(path, body, apiUrl = resolveApiUrl()) {
4493
+ async function postJson(path, body, apiUrl) {
4494
+ const baseUrl = apiUrl ?? await resolveApiUrlAsync();
4483
4495
  let response;
4484
4496
  try {
4485
- response = await fetch(`${apiUrl}${path}`, {
4497
+ response = await fetch(`${baseUrl}${path}`, {
4486
4498
  method: "POST",
4487
4499
  headers: { "Content-Type": "application/json" },
4488
4500
  body: JSON.stringify(body)
4489
4501
  });
4490
4502
  } catch (err) {
4491
- throw new Error(formatServerConnectionError(apiUrl, err));
4503
+ throw new Error(formatServerConnectionError(baseUrl, err));
4492
4504
  }
4493
4505
  const data = await response.json().catch(() => ({}));
4494
4506
  return { response, data };
@@ -4519,10 +4531,10 @@ function isLocalPortOpen(port, timeoutMs = 2e3) {
4519
4531
 
4520
4532
  // src/index.ts
4521
4533
  var program = new Command();
4522
- program.name("shiplocal").description("Share localhost with clients in seconds").version("0.1.1");
4534
+ program.name("shiplocal").description("Share localhost with clients in seconds").version("0.1.3");
4523
4535
  program.command("login").description("Authenticate with ShipLocal Cloud").action(async () => {
4524
4536
  const rl = createInterface({ input, output });
4525
- const apiUrl = resolveApiUrl();
4537
+ const apiUrl = await resolveApiUrlAsync();
4526
4538
  try {
4527
4539
  const email = await rl.question("Email: ");
4528
4540
  const password = await rl.question("Password: ");
@@ -4556,7 +4568,7 @@ program.argument("[port]", "Local port to expose", String(DEFAULT_TUNNEL_PORT)).
4556
4568
  console.error("Error: port must be a number between 1 and 65535");
4557
4569
  process.exit(1);
4558
4570
  }
4559
- const serverUrl = resolveApiUrl();
4571
+ const serverUrl = await resolveApiUrlAsync();
4560
4572
  const token = await resolveToken();
4561
4573
  if (!token) {
4562
4574
  console.error("Not authenticated. Run `shiplocal login` first.");
@@ -4612,7 +4624,7 @@ program.argument("[port]", "Local port to expose", String(DEFAULT_TUNNEL_PORT)).
4612
4624
  try {
4613
4625
  await client.connect();
4614
4626
  } catch (err) {
4615
- const apiUrl = resolveApiUrl();
4627
+ const apiUrl = await resolveApiUrlAsync();
4616
4628
  const message = err instanceof Error && err.message.includes("ECONNREFUSED") ? [
4617
4629
  `Cannot reach ShipLocal server at ${apiUrl}.`,
4618
4630
  "",