Skip to content

Commit f53a5b0

Browse files
author
Frank Fiegel
committed
feat: release pipenet
0 parents  commit f53a5b0

26 files changed

+6968
-0
lines changed

.github/workflows/feature.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Run Tests
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
types:
7+
- opened
8+
- synchronize
9+
- reopened
10+
- ready_for_review
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
name: Test
15+
strategy:
16+
fail-fast: true
17+
matrix:
18+
node:
19+
- 24
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v4
23+
with:
24+
fetch-depth: 0
25+
- uses: pnpm/action-setup@v4
26+
with:
27+
version: 9
28+
- name: Setup NodeJS ${{ matrix.node }}
29+
uses: actions/setup-node@v4
30+
with:
31+
node-version: ${{ matrix.node }}
32+
cache: "pnpm"
33+
cache-dependency-path: "**/pnpm-lock.yaml"
34+
- name: Install dependencies
35+
run: pnpm install
36+
- name: Run lint
37+
run: pnpm lint
38+
- name: Run tests
39+
run: pnpm test

.github/workflows/main.yaml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Release
2+
on:
3+
push:
4+
branches:
5+
- main
6+
jobs:
7+
test:
8+
environment: release
9+
name: Test
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
issues: write
14+
pull-requests: write
15+
id-token: write
16+
steps:
17+
- name: setup repository
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
- uses: pnpm/action-setup@v4
22+
with:
23+
version: 9
24+
- name: Setup NodeJS
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: 24
28+
cache: "pnpm"
29+
cache-dependency-path: "**/pnpm-lock.yaml"
30+
- name: Install dependencies
31+
run: pnpm install
32+
- name: Run lint
33+
run: pnpm lint
34+
- name: Run tests
35+
run: pnpm test
36+
- name: Build
37+
run: pnpm build
38+
- name: Release
39+
run: pnpm semantic-release
40+
env:
41+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dist
2+
node_modules

LICENSE

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
The MIT License (MIT)
2+
=====================
3+
4+
Copyright © 2026 Frank Fiegel (frank@glama.ai)
5+
6+
Permission is hereby granted, free of charge, to any person
7+
obtaining a copy of this software and associated documentation
8+
files (the “Software”), to deal in the Software without
9+
restriction, including without limitation the rights to use,
10+
copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the
12+
Software is furnished to do so, subject to the following
13+
conditions:
14+
15+
The above copyright notice and this permission notice shall be
16+
included in all copies or substantial portions of the Software.
17+
18+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
19+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25+
OTHER DEALINGS IN THE SOFTWARE.
26+
27+
---
28+
29+
MIT License
30+
31+
Copyright (c) 2018 Roman Shtylman
32+
33+
Permission is hereby granted, free of charge, to any person obtaining a copy
34+
of this software and associated documentation files (the "Software"), to deal
35+
in the Software without restriction, including without limitation the rights
36+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37+
copies of the Software, and to permit persons to whom the Software is
38+
furnished to do so, subject to the following conditions:
39+
40+
The above copyright notice and this permission notice shall be included in all
41+
copies or substantial portions of the Software.
42+
43+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49+
SOFTWARE.

README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# pipenet
2+
3+
pipenet exposes your localhost to the world for easy testing and sharing! No need to mess with DNS or deploy just to have others test out your changes.
4+
5+
## Installation
6+
7+
```bash
8+
npm install pipenet
9+
# or
10+
pnpm add pipenet
11+
```
12+
13+
## API
14+
15+
The pipenet client is also usable through an API (for test integration, automation, etc)
16+
17+
### pipenet(port [,options][,callback])
18+
19+
Creates a new pipenet tunnel to the specified local `port`. Will return a Promise that resolves once you have been assigned a public tunnel url. `options` can be used to request a specific `subdomain`. A `callback` function can be passed, in which case it won't return a Promise. This exists for backwards compatibility with the old Node-style callback API. You may also pass a single options object with `port` as a property.
20+
21+
```js
22+
import pipenet from 'pipenet';
23+
24+
const tunnel = await pipenet({ port: 3000 });
25+
26+
// the assigned public url for your tunnel
27+
// i.e. https://abcdefgjhij.pipenet.me
28+
tunnel.url;
29+
30+
tunnel.on('close', () => {
31+
// tunnels are closed
32+
});
33+
```
34+
35+
#### options
36+
37+
- `port` (number) [required] The local port number to expose through pipenet.
38+
- `subdomain` (string) Request a specific subdomain on the proxy server. **Note** You may not actually receive this name depending on availability.
39+
- `host` (string) URL for the upstream proxy server. Defaults to `https://pipenet.me`.
40+
- `local_host` (string) Proxy to this hostname instead of `localhost`. This will also cause the `Host` header to be re-written to this value in proxied requests.
41+
- `local_https` (boolean) Enable tunneling to local HTTPS server.
42+
- `local_cert` (string) Path to certificate PEM file for local HTTPS server.
43+
- `local_key` (string) Path to certificate key file for local HTTPS server.
44+
- `local_ca` (string) Path to certificate authority file for self-signed certificates.
45+
- `allow_invalid_cert` (boolean) Disable certificate checks for your local HTTPS server (ignore cert/key/ca options).
46+
47+
Refer to [tls.createSecureContext](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) for details on the certificate options.
48+
49+
### Tunnel
50+
51+
The `tunnel` instance returned to your callback emits the following events
52+
53+
| event | args | description |
54+
| ------- | ---- | ------------------------------------------------------------------------------------ |
55+
| request | info | fires when a request is processed by the tunnel, contains _method_ and _path_ fields |
56+
| error | err | fires when an error happens on the tunnel |
57+
| close | | fires when the tunnel has closed |
58+
59+
The `tunnel` instance has the following methods
60+
61+
| method | args | description |
62+
| ------ | ---- | ---------------- |
63+
| close | | close the tunnel |
64+
65+
## Server
66+
67+
This package includes both the client and server components. You can run your own pipenet server.
68+
69+
### Running the Server
70+
71+
```bash
72+
# Using the CLI
73+
npx pipenet-server --port 3000
74+
75+
# Or programmatically
76+
```
77+
78+
```js
79+
import { createServer } from 'pipenet/server';
80+
81+
const server = createServer({
82+
domain: 'tunnel.example.com', // Optional: custom domain
83+
secure: false, // Optional: require HTTPS
84+
landing: 'https://example.com', // Optional: landing page URL
85+
max_tcp_sockets: 10, // Optional: max sockets per client
86+
});
87+
88+
server.listen(3000, () => {
89+
console.log('pipenet server listening on port 3000');
90+
});
91+
```
92+
93+
### Server Options
94+
95+
- `domain` (string) Custom domain for the tunnel server
96+
- `secure` (boolean) Require HTTPS connections
97+
- `landing` (string) URL to redirect root requests to
98+
- `max_tcp_sockets` (number) Maximum number of TCP sockets per client (default: 10)
99+
100+
### Server API Endpoints
101+
102+
- `GET /api/status` - Server status and tunnel count
103+
- `GET /api/tunnels/:id/status` - Status of a specific tunnel
104+
- `GET /:id` - Request a new tunnel with the specified ID
105+
106+
## Acknowledgments
107+
108+
pipenet is based on [localtunnel](https://github.com/localtunnel/localtunnel), an open-source project by [@defunctzombie](https://github.com/defunctzombie). We are grateful for the foundation it provided.
109+
110+
Development of pipenet is sponsored by [glama.ai](https://glama.ai).
111+
112+
## License
113+
114+
MIT

eslint.config.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import eslint from '@eslint/js';
2+
import tseslint from 'typescript-eslint';
3+
import globals from 'globals';
4+
5+
export default tseslint.config(
6+
eslint.configs.recommended,
7+
...tseslint.configs.recommended,
8+
{
9+
languageOptions: {
10+
globals: {
11+
...globals.node,
12+
},
13+
},
14+
rules: {
15+
'@typescript-eslint/no-unused-vars': [
16+
'error',
17+
{
18+
argsIgnorePattern: '^_',
19+
varsIgnorePattern: '^_',
20+
},
21+
],
22+
},
23+
},
24+
{
25+
ignores: ['dist/**', 'node_modules/**'],
26+
}
27+
);
28+

package.json

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"name": "pipenet",
3+
"description": "Expose localhost to the world",
4+
"version": "0.0.0",
5+
"private": true,
6+
"type": "module",
7+
"main": "./dist/pipenet.js",
8+
"types": "./dist/pipenet.d.ts",
9+
"exports": {
10+
".": {
11+
"types": "./dist/pipenet.d.ts",
12+
"import": "./dist/pipenet.js"
13+
},
14+
"./server": {
15+
"types": "./dist/server/index.d.ts",
16+
"import": "./dist/server/index.js"
17+
}
18+
},
19+
"bin": {
20+
"pipenet": "./dist/cli.js"
21+
},
22+
"release": {
23+
"branches": [
24+
"main"
25+
],
26+
"plugins": [
27+
"@semantic-release/commit-analyzer",
28+
"@semantic-release/release-notes-generator",
29+
"@semantic-release/npm",
30+
"@semantic-release/github"
31+
]
32+
},
33+
"scripts": {
34+
"build": "tsc",
35+
"lint": "eslint .",
36+
"test": "vitest run",
37+
"test:watch": "vitest",
38+
"start:server": "node dist/server/index.js"
39+
},
40+
"dependencies": {
41+
"axios": "^1.7.3",
42+
"debug": "^4.3.6",
43+
"human-readable-ids": "^1.0.4",
44+
"koa": "^3.1.1",
45+
"koa-router": "^14.0.0",
46+
"pump": "^3.0.3",
47+
"tldjs": "^2.3.2",
48+
"yargs": "^18.0.0"
49+
},
50+
"engines": {
51+
"node": ">=22.0.0"
52+
},
53+
"packageManager": "pnpm@9.15.4",
54+
"devDependencies": {
55+
"@eslint/js": "^9.39.2",
56+
"@types/debug": "^4.1.12",
57+
"@types/koa": "^3.0.1",
58+
"@types/koa-router": "^7.4.9",
59+
"@types/node": "^25.0.9",
60+
"@types/pump": "^1.1.3",
61+
"@types/supertest": "^6.0.3",
62+
"@types/tldjs": "^2.3.4",
63+
"@types/ws": "^8.18.1",
64+
"@types/yargs": "^17.0.35",
65+
"eslint": "^9.39.2",
66+
"globals": "^17.0.0",
67+
"jiti": "^2.6.1",
68+
"semantic-release": "^25.0.2",
69+
"supertest": "^7.2.2",
70+
"typescript": "^5.9.3",
71+
"typescript-eslint": "^8.53.0",
72+
"vitest": "^4.0.17",
73+
"ws": "^8.19.0"
74+
}
75+
}

0 commit comments

Comments
 (0)