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 +21 -18
- package/dist/index.js +25 -13
- package/dist/index.js.map +2 -2
- package/package.json +3 -3
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
|
|
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 |
|
|
75
|
-
| ------------------- |
|
|
76
|
-
| `SHIPLOCAL_API_URL` |
|
|
77
|
-
| `SHIPLOCAL_TOKEN` |
|
|
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
|
-
|
|
79
|
+
**You do not need `export SHIPLOCAL_API_URL` for ShipLocal Cloud** — only set it if you use a custom server.
|
|
82
80
|
|
|
83
|
-
|
|
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)
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
4448
|
-
|
|
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
|
|
4493
|
+
async function postJson(path, body, apiUrl) {
|
|
4494
|
+
const baseUrl = apiUrl ?? await resolveApiUrlAsync();
|
|
4483
4495
|
let response;
|
|
4484
4496
|
try {
|
|
4485
|
-
response = await fetch(`${
|
|
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(
|
|
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.
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
"",
|