@liveport/agent-sdk 0.1.1 ā 0.1.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 +2 -2
- package/dist/index.d.mts +90 -10
- package/dist/index.d.ts +90 -10
- package/dist/index.js +278 -75
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +267 -75
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/examples/01-basic-usage.ts +0 -40
- package/examples/02-testing-integration.ts +0 -75
- package/examples/03-ai-agent-workflow.ts +0 -175
- package/examples/04-multiple-tunnels.ts +0 -84
- package/examples/05-error-handling.ts +0 -148
- package/examples/README.md +0 -172
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI Agent Workflow Example
|
|
3
|
-
*
|
|
4
|
-
* This example demonstrates how an AI agent might use the SDK
|
|
5
|
-
* to interact with a developer's local application.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { LivePortAgent, ApiError } from "@liveport/agent-sdk";
|
|
9
|
-
|
|
10
|
-
interface TestResult {
|
|
11
|
-
endpoint: string;
|
|
12
|
-
status: "pass" | "fail";
|
|
13
|
-
responseTime: number;
|
|
14
|
-
statusCode?: number;
|
|
15
|
-
error?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
class AIAgentTester {
|
|
19
|
-
private agent: LivePortAgent;
|
|
20
|
-
private results: TestResult[] = [];
|
|
21
|
-
|
|
22
|
-
constructor(bridgeKey: string) {
|
|
23
|
-
this.agent = new LivePortAgent({
|
|
24
|
-
key: bridgeKey,
|
|
25
|
-
timeout: 120000, // 2 minute timeout for tunnel
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async run(): Promise<void> {
|
|
30
|
-
console.log("š¤ AI Agent Test Runner Starting...\n");
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
// Step 1: Wait for tunnel
|
|
34
|
-
console.log("ā³ Waiting for developer to start tunnel...");
|
|
35
|
-
const tunnel = await this.agent.waitForTunnel({
|
|
36
|
-
timeout: 120000,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
console.log(`ā Tunnel established: ${tunnel.url}`);
|
|
40
|
-
console.log(` Port: ${tunnel.localPort}`);
|
|
41
|
-
console.log(` Expires: ${tunnel.expiresAt.toLocaleString()}\n`);
|
|
42
|
-
|
|
43
|
-
// Step 2: Discover available endpoints
|
|
44
|
-
console.log("š Discovering API endpoints...");
|
|
45
|
-
const baseUrl = tunnel.url;
|
|
46
|
-
|
|
47
|
-
// Test common endpoints
|
|
48
|
-
const endpoints = [
|
|
49
|
-
"/",
|
|
50
|
-
"/api/health",
|
|
51
|
-
"/api/users",
|
|
52
|
-
"/api/products",
|
|
53
|
-
"/api/auth/login",
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
// Step 3: Test each endpoint
|
|
57
|
-
for (const endpoint of endpoints) {
|
|
58
|
-
await this.testEndpoint(baseUrl, endpoint);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Step 4: Report results
|
|
62
|
-
this.printResults();
|
|
63
|
-
|
|
64
|
-
// Step 5: Analyze and provide recommendations
|
|
65
|
-
this.analyzeResults();
|
|
66
|
-
} catch (error) {
|
|
67
|
-
if (error instanceof ApiError) {
|
|
68
|
-
console.error(`ā API Error [${error.code}]: ${error.message}`);
|
|
69
|
-
} else {
|
|
70
|
-
console.error("ā Error:", error);
|
|
71
|
-
}
|
|
72
|
-
} finally {
|
|
73
|
-
await this.agent.disconnect();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
private async testEndpoint(
|
|
78
|
-
baseUrl: string,
|
|
79
|
-
endpoint: string
|
|
80
|
-
): Promise<void> {
|
|
81
|
-
const start = Date.now();
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
const response = await fetch(`${baseUrl}${endpoint}`, {
|
|
85
|
-
signal: AbortSignal.timeout(5000), // 5 second timeout per request
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const responseTime = Date.now() - start;
|
|
89
|
-
|
|
90
|
-
this.results.push({
|
|
91
|
-
endpoint,
|
|
92
|
-
status: response.ok ? "pass" : "fail",
|
|
93
|
-
responseTime,
|
|
94
|
-
statusCode: response.status,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const emoji = response.ok ? "ā" : "ā";
|
|
98
|
-
console.log(
|
|
99
|
-
` ${emoji} ${endpoint.padEnd(20)} [${response.status}] ${responseTime}ms`
|
|
100
|
-
);
|
|
101
|
-
} catch (error) {
|
|
102
|
-
const responseTime = Date.now() - start;
|
|
103
|
-
|
|
104
|
-
this.results.push({
|
|
105
|
-
endpoint,
|
|
106
|
-
status: "fail",
|
|
107
|
-
responseTime,
|
|
108
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
console.log(` ā ${endpoint.padEnd(20)} [ERROR] ${error}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private printResults(): void {
|
|
116
|
-
console.log("\nš Test Results Summary");
|
|
117
|
-
console.log("=" .repeat(50));
|
|
118
|
-
|
|
119
|
-
const passed = this.results.filter((r) => r.status === "pass").length;
|
|
120
|
-
const failed = this.results.filter((r) => r.status === "fail").length;
|
|
121
|
-
const avgTime =
|
|
122
|
-
this.results.reduce((sum, r) => sum + r.responseTime, 0) /
|
|
123
|
-
this.results.length;
|
|
124
|
-
|
|
125
|
-
console.log(`Total Endpoints: ${this.results.length}`);
|
|
126
|
-
console.log(`Passed: ${passed}`);
|
|
127
|
-
console.log(`Failed: ${failed}`);
|
|
128
|
-
console.log(`Average Response Time: ${avgTime.toFixed(0)}ms`);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private analyzeResults(): void {
|
|
132
|
-
console.log("\nš” AI Analysis & Recommendations");
|
|
133
|
-
console.log("=" .repeat(50));
|
|
134
|
-
|
|
135
|
-
const slowEndpoints = this.results.filter((r) => r.responseTime > 1000);
|
|
136
|
-
if (slowEndpoints.length > 0) {
|
|
137
|
-
console.log("\nā ļø Slow Endpoints (>1000ms):");
|
|
138
|
-
slowEndpoints.forEach((r) => {
|
|
139
|
-
console.log(` - ${r.endpoint}: ${r.responseTime}ms`);
|
|
140
|
-
});
|
|
141
|
-
console.log(" š” Consider optimizing database queries or adding caching");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const failedEndpoints = this.results.filter((r) => r.status === "fail");
|
|
145
|
-
if (failedEndpoints.length > 0) {
|
|
146
|
-
console.log("\nā Failed Endpoints:");
|
|
147
|
-
failedEndpoints.forEach((r) => {
|
|
148
|
-
console.log(` - ${r.endpoint}: ${r.statusCode || r.error}`);
|
|
149
|
-
});
|
|
150
|
-
console.log(" š” Review error handling and endpoint implementations");
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const avgTime =
|
|
154
|
-
this.results.reduce((sum, r) => sum + r.responseTime, 0) /
|
|
155
|
-
this.results.length;
|
|
156
|
-
if (avgTime < 100) {
|
|
157
|
-
console.log("\n⨠Great performance! All endpoints responding quickly.");
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Run the AI agent
|
|
163
|
-
async function main() {
|
|
164
|
-
const bridgeKey = process.env.LIVEPORT_KEY;
|
|
165
|
-
|
|
166
|
-
if (!bridgeKey) {
|
|
167
|
-
console.error("Error: LIVEPORT_KEY environment variable not set");
|
|
168
|
-
process.exit(1);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const tester = new AIAgentTester(bridgeKey);
|
|
172
|
-
await tester.run();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
main();
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Multiple Tunnels Example
|
|
3
|
-
*
|
|
4
|
-
* This example shows how to work with multiple tunnels
|
|
5
|
-
* when testing microservices or multi-service architectures.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { LivePortAgent } from "@liveport/agent-sdk";
|
|
9
|
-
|
|
10
|
-
async function main() {
|
|
11
|
-
const agent = new LivePortAgent({
|
|
12
|
-
key: process.env.LIVEPORT_KEY!,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
console.log("š Checking for available tunnels...\n");
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
// List all active tunnels for this bridge key
|
|
19
|
-
const tunnels = await agent.listTunnels();
|
|
20
|
-
|
|
21
|
-
if (tunnels.length === 0) {
|
|
22
|
-
console.log("No tunnels available.");
|
|
23
|
-
console.log("Start one or more tunnels with:");
|
|
24
|
-
console.log(" liveport connect 3000 # API server");
|
|
25
|
-
console.log(" liveport connect 3001 # Web server");
|
|
26
|
-
console.log(" liveport connect 5432 # Database");
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
console.log(`Found ${tunnels.length} active tunnel(s):\n`);
|
|
31
|
-
|
|
32
|
-
// Display tunnel information
|
|
33
|
-
for (const tunnel of tunnels) {
|
|
34
|
-
console.log(`š ${tunnel.subdomain}`);
|
|
35
|
-
console.log(` URL: ${tunnel.url}`);
|
|
36
|
-
console.log(` Local Port: ${tunnel.localPort}`);
|
|
37
|
-
console.log(` Created: ${tunnel.createdAt.toLocaleString()}`);
|
|
38
|
-
console.log(` Expires: ${tunnel.expiresAt.toLocaleString()}`);
|
|
39
|
-
console.log();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Example: Test each tunnel
|
|
43
|
-
console.log("Testing each tunnel...\n");
|
|
44
|
-
|
|
45
|
-
for (const tunnel of tunnels) {
|
|
46
|
-
try {
|
|
47
|
-
const response = await fetch(tunnel.url, {
|
|
48
|
-
signal: AbortSignal.timeout(5000),
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
console.log(
|
|
52
|
-
`ā ${tunnel.subdomain.padEnd(30)} [${response.status}] ${response.statusText}`
|
|
53
|
-
);
|
|
54
|
-
} catch (error) {
|
|
55
|
-
console.log(`ā ${tunnel.subdomain.padEnd(30)} [ERROR] ${error}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Example: Find specific service by port
|
|
60
|
-
const apiTunnel = tunnels.find((t) => t.localPort === 3000);
|
|
61
|
-
if (apiTunnel) {
|
|
62
|
-
console.log(`\nšÆ Found API server at ${apiTunnel.url}`);
|
|
63
|
-
|
|
64
|
-
// Run API-specific tests
|
|
65
|
-
const health = await fetch(`${apiTunnel.url}/health`);
|
|
66
|
-
console.log(` Health check: ${health.status}`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const webTunnel = tunnels.find((t) => t.localPort === 3001);
|
|
70
|
-
if (webTunnel) {
|
|
71
|
-
console.log(`\nšÆ Found web server at ${webTunnel.url}`);
|
|
72
|
-
|
|
73
|
-
// Run web-specific tests
|
|
74
|
-
const homepage = await fetch(webTunnel.url);
|
|
75
|
-
console.log(` Homepage: ${homepage.status}`);
|
|
76
|
-
}
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.error("Error:", error);
|
|
79
|
-
} finally {
|
|
80
|
-
await agent.disconnect();
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
main();
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error Handling Example
|
|
3
|
-
*
|
|
4
|
-
* This example demonstrates proper error handling patterns
|
|
5
|
-
* when using the LivePort Agent SDK.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
LivePortAgent,
|
|
10
|
-
TunnelTimeoutError,
|
|
11
|
-
ApiError,
|
|
12
|
-
} from "@liveport/agent-sdk";
|
|
13
|
-
|
|
14
|
-
async function robustTunnelAccess() {
|
|
15
|
-
const agent = new LivePortAgent({
|
|
16
|
-
key: process.env.LIVEPORT_KEY!,
|
|
17
|
-
timeout: 30000,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
console.log("ā³ Waiting for tunnel...");
|
|
22
|
-
|
|
23
|
-
const tunnel = await agent.waitForTunnel({
|
|
24
|
-
timeout: 30000,
|
|
25
|
-
pollInterval: 2000,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
console.log(`ā Connected to ${tunnel.url}`);
|
|
29
|
-
|
|
30
|
-
// Your application logic here
|
|
31
|
-
await runTests(tunnel.url);
|
|
32
|
-
} catch (error) {
|
|
33
|
-
// Handle specific error types
|
|
34
|
-
if (error instanceof TunnelTimeoutError) {
|
|
35
|
-
console.error("\nā Tunnel Timeout");
|
|
36
|
-
console.error(" No tunnel became available within the timeout period.");
|
|
37
|
-
console.error("\nš” Make sure to:");
|
|
38
|
-
console.error(" 1. Start your local server (e.g., npm run dev)");
|
|
39
|
-
console.error(" 2. Run: liveport connect <port>");
|
|
40
|
-
console.error(" 3. Wait for tunnel URL to appear");
|
|
41
|
-
console.error(" 4. Then run this script again");
|
|
42
|
-
process.exit(1);
|
|
43
|
-
} else if (error instanceof ApiError) {
|
|
44
|
-
console.error(`\nā API Error [${error.code}]`);
|
|
45
|
-
console.error(` ${error.message}`);
|
|
46
|
-
console.error(` Status: ${error.statusCode}`);
|
|
47
|
-
|
|
48
|
-
// Handle specific error codes
|
|
49
|
-
switch (error.code) {
|
|
50
|
-
case "INVALID_KEY":
|
|
51
|
-
console.error("\nš” Your bridge key is invalid or expired.");
|
|
52
|
-
console.error(" Get a new key at: https://liveport.dev/keys");
|
|
53
|
-
break;
|
|
54
|
-
|
|
55
|
-
case "EXPIRED_KEY":
|
|
56
|
-
console.error("\nš” Your bridge key has expired.");
|
|
57
|
-
console.error(" Create a new key at: https://liveport.dev/keys");
|
|
58
|
-
break;
|
|
59
|
-
|
|
60
|
-
case "REVOKED_KEY":
|
|
61
|
-
console.error("\nš” Your bridge key has been revoked.");
|
|
62
|
-
console.error(" Create a new key at: https://liveport.dev/keys");
|
|
63
|
-
break;
|
|
64
|
-
|
|
65
|
-
case "USAGE_LIMIT_EXCEEDED":
|
|
66
|
-
console.error("\nš” Your bridge key has exceeded its usage limit.");
|
|
67
|
-
console.error(" Create a new key or increase the limit at:");
|
|
68
|
-
console.error(" https://liveport.dev/keys");
|
|
69
|
-
break;
|
|
70
|
-
|
|
71
|
-
case "RATE_LIMIT_EXCEEDED":
|
|
72
|
-
console.error("\nš” Too many requests. Please wait and try again.");
|
|
73
|
-
break;
|
|
74
|
-
|
|
75
|
-
default:
|
|
76
|
-
console.error("\nš” Please check your configuration and try again.");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
process.exit(1);
|
|
80
|
-
} else if (error instanceof Error) {
|
|
81
|
-
console.error("\nā Unexpected Error");
|
|
82
|
-
console.error(` ${error.message}`);
|
|
83
|
-
console.error("\nš” Please report this issue at:");
|
|
84
|
-
console.error(" https://github.com/dundas/liveport/issues");
|
|
85
|
-
process.exit(1);
|
|
86
|
-
} else {
|
|
87
|
-
console.error("\nā Unknown Error");
|
|
88
|
-
console.error(error);
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
} finally {
|
|
92
|
-
// Always clean up
|
|
93
|
-
await agent.disconnect();
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async function runTests(tunnelUrl: string) {
|
|
98
|
-
console.log("\nš§Ŗ Running tests...");
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
// Example test with retry logic
|
|
102
|
-
const response = await fetchWithRetry(`${tunnelUrl}/api/health`, {
|
|
103
|
-
maxRetries: 3,
|
|
104
|
-
retryDelay: 1000,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
if (!response.ok) {
|
|
108
|
-
throw new Error(`Health check failed: ${response.status}`);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
console.log("ā Health check passed");
|
|
112
|
-
|
|
113
|
-
// More tests...
|
|
114
|
-
console.log("ā All tests passed!");
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error("ā Tests failed:", error);
|
|
117
|
-
throw error;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function fetchWithRetry(
|
|
122
|
-
url: string,
|
|
123
|
-
options: { maxRetries?: number; retryDelay?: number } = {}
|
|
124
|
-
): Promise<Response> {
|
|
125
|
-
const { maxRetries = 3, retryDelay = 1000 } = options;
|
|
126
|
-
|
|
127
|
-
for (let i = 0; i <= maxRetries; i++) {
|
|
128
|
-
try {
|
|
129
|
-
const response = await fetch(url, {
|
|
130
|
-
signal: AbortSignal.timeout(5000),
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
return response;
|
|
134
|
-
} catch (error) {
|
|
135
|
-
if (i === maxRetries) {
|
|
136
|
-
throw error;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
console.log(` Retry ${i + 1}/${maxRetries} after ${retryDelay}ms...`);
|
|
140
|
-
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
throw new Error("Max retries exceeded");
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Run with proper error handling
|
|
148
|
-
robustTunnelAccess();
|
package/examples/README.md
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
# LivePort Agent SDK Examples
|
|
2
|
-
|
|
3
|
-
This directory contains practical examples demonstrating different use cases for the LivePort Agent SDK.
|
|
4
|
-
|
|
5
|
-
## Running the Examples
|
|
6
|
-
|
|
7
|
-
1. **Install dependencies:**
|
|
8
|
-
```bash
|
|
9
|
-
pnpm install
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
2. **Set your bridge key:**
|
|
13
|
-
```bash
|
|
14
|
-
export LIVEPORT_KEY=lpk_your_bridge_key_here
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
3. **Start a local tunnel:**
|
|
18
|
-
```bash
|
|
19
|
-
# In a separate terminal
|
|
20
|
-
liveport connect 3000
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
4. **Run an example:**
|
|
24
|
-
```bash
|
|
25
|
-
npx tsx examples/01-basic-usage.ts
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Examples Overview
|
|
29
|
-
|
|
30
|
-
### `01-basic-usage.ts`
|
|
31
|
-
**Simplest example** showing how to wait for a tunnel and make a request.
|
|
32
|
-
|
|
33
|
-
**Use case:** Quick start, proof of concept
|
|
34
|
-
|
|
35
|
-
**Key concepts:**
|
|
36
|
-
- Creating an agent instance
|
|
37
|
-
- Waiting for tunnel
|
|
38
|
-
- Making HTTP requests
|
|
39
|
-
- Basic error handling
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
### `02-testing-integration.ts`
|
|
44
|
-
**Testing framework integration** using Vitest (works with Jest too).
|
|
45
|
-
|
|
46
|
-
**Use case:** Automated testing, CI/CD pipelines
|
|
47
|
-
|
|
48
|
-
**Key concepts:**
|
|
49
|
-
- beforeAll/afterAll hooks
|
|
50
|
-
- Multiple test cases
|
|
51
|
-
- Sharing tunnel across tests
|
|
52
|
-
- Timeout configuration
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
### `03-ai-agent-workflow.ts`
|
|
57
|
-
**Complete AI agent workflow** that discovers, tests, and analyzes endpoints.
|
|
58
|
-
|
|
59
|
-
**Use case:** AI-powered testing, endpoint discovery, performance analysis
|
|
60
|
-
|
|
61
|
-
**Key concepts:**
|
|
62
|
-
- Endpoint discovery
|
|
63
|
-
- Performance measurement
|
|
64
|
-
- Result analysis
|
|
65
|
-
- AI recommendations
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
### `04-multiple-tunnels.ts`
|
|
70
|
-
**Working with multiple tunnels** for microservices testing.
|
|
71
|
-
|
|
72
|
-
**Use case:** Multi-service architectures, microservices
|
|
73
|
-
|
|
74
|
-
**Key concepts:**
|
|
75
|
-
- Listing all tunnels
|
|
76
|
-
- Finding tunnels by port
|
|
77
|
-
- Testing multiple services
|
|
78
|
-
- Service-specific logic
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
### `05-error-handling.ts`
|
|
83
|
-
**Comprehensive error handling** patterns and best practices.
|
|
84
|
-
|
|
85
|
-
**Use case:** Production applications, robust error handling
|
|
86
|
-
|
|
87
|
-
**Key concepts:**
|
|
88
|
-
- Specific error types
|
|
89
|
-
- User-friendly error messages
|
|
90
|
-
- Retry logic
|
|
91
|
-
- Cleanup and recovery
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## Common Patterns
|
|
96
|
-
|
|
97
|
-
### Pattern 1: Wait with Timeout
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
const agent = new LivePortAgent({ key: "lpk_..." });
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
const tunnel = await agent.waitForTunnel({ timeout: 60000 });
|
|
104
|
-
// Use tunnel.url
|
|
105
|
-
} catch (error) {
|
|
106
|
-
if (error instanceof TunnelTimeoutError) {
|
|
107
|
-
console.log("No tunnel available");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Pattern 2: Test Suite Integration
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
let tunnelUrl: string;
|
|
116
|
-
|
|
117
|
-
beforeAll(async () => {
|
|
118
|
-
const agent = new LivePortAgent({ key: process.env.LIVEPORT_KEY! });
|
|
119
|
-
const tunnel = await agent.waitForTunnel();
|
|
120
|
-
tunnelUrl = tunnel.url;
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("API test", async () => {
|
|
124
|
-
const response = await fetch(`${tunnelUrl}/api/endpoint`);
|
|
125
|
-
// assertions...
|
|
126
|
-
});
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Pattern 3: Multiple Services
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
const agent = new LivePortAgent({ key: "lpk_..." });
|
|
133
|
-
const tunnels = await agent.listTunnels();
|
|
134
|
-
|
|
135
|
-
const apiTunnel = tunnels.find(t => t.localPort === 3000);
|
|
136
|
-
const webTunnel = tunnels.find(t => t.localPort === 3001);
|
|
137
|
-
|
|
138
|
-
// Test API
|
|
139
|
-
await fetch(`${apiTunnel.url}/health`);
|
|
140
|
-
|
|
141
|
-
// Test Web
|
|
142
|
-
await fetch(`${webTunnel.url}/`);
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
## Tips
|
|
146
|
-
|
|
147
|
-
1. **Always set a timeout** - Use reasonable timeouts to avoid hanging indefinitely
|
|
148
|
-
2. **Handle errors gracefully** - Provide helpful error messages for users
|
|
149
|
-
3. **Clean up resources** - Always call `agent.disconnect()` in finally blocks
|
|
150
|
-
4. **Use environment variables** - Never hardcode bridge keys
|
|
151
|
-
5. **Add retry logic** - Network requests can fail, especially in CI/CD
|
|
152
|
-
|
|
153
|
-
## Environment Variables
|
|
154
|
-
|
|
155
|
-
All examples support these environment variables:
|
|
156
|
-
|
|
157
|
-
- `LIVEPORT_KEY` - Your bridge key (required)
|
|
158
|
-
- `LIVEPORT_API_URL` - Custom API URL (optional)
|
|
159
|
-
|
|
160
|
-
## Next Steps
|
|
161
|
-
|
|
162
|
-
After trying these examples, check out:
|
|
163
|
-
|
|
164
|
-
- [API Documentation](../README.md)
|
|
165
|
-
- [CLI Documentation](../../cli/README.md)
|
|
166
|
-
- [Dashboard](https://liveport.dev/dashboard)
|
|
167
|
-
|
|
168
|
-
## Need Help?
|
|
169
|
-
|
|
170
|
-
- š [Full Documentation](https://liveport.dev/docs)
|
|
171
|
-
- š [Report Issues](https://github.com/dundas/liveport/issues)
|
|
172
|
-
- š¬ [Community Support](https://liveport.dev/support)
|