@assetsart/nylon-mesh 1.0.1
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/.github/workflows/release.yml +98 -0
- package/Cargo.lock +2965 -0
- package/Cargo.toml +33 -0
- package/README.md +104 -0
- package/bin/nylon-mesh.js +213 -0
- package/bun.lock +360 -0
- package/docs/content/docs/caching.mdx +85 -0
- package/docs/content/docs/configuration.mdx +115 -0
- package/docs/content/docs/index.mdx +58 -0
- package/docs/content/docs/load-balancing.mdx +69 -0
- package/docs/content/docs/meta.json +9 -0
- package/docs/next.config.mjs +11 -0
- package/docs/package-lock.json +6099 -0
- package/docs/package.json +32 -0
- package/docs/postcss.config.mjs +7 -0
- package/docs/source.config.ts +23 -0
- package/docs/src/app/(home)/layout.tsx +6 -0
- package/docs/src/app/(home)/page.tsx +125 -0
- package/docs/src/app/api/search/route.ts +9 -0
- package/docs/src/app/docs/[[...slug]]/page.tsx +46 -0
- package/docs/src/app/docs/layout.tsx +11 -0
- package/docs/src/app/global.css +7 -0
- package/docs/src/app/layout.tsx +31 -0
- package/docs/src/app/llms-full.txt/route.ts +10 -0
- package/docs/src/app/llms.txt/route.ts +13 -0
- package/docs/src/app/og/docs/[...slug]/route.tsx +27 -0
- package/docs/src/components/ai/page-actions.tsx +240 -0
- package/docs/src/components/architecture-diagram.tsx +88 -0
- package/docs/src/components/benchmark.tsx +129 -0
- package/docs/src/components/configuration.tsx +107 -0
- package/docs/src/components/copy-button.tsx +29 -0
- package/docs/src/components/footer.tsx +37 -0
- package/docs/src/components/framework-logos.tsx +35 -0
- package/docs/src/lib/cn.ts +1 -0
- package/docs/src/lib/layout.shared.tsx +23 -0
- package/docs/src/lib/source.ts +27 -0
- package/docs/src/mdx-components.tsx +9 -0
- package/docs/tsconfig.json +46 -0
- package/nylon-mesh.yaml +41 -0
- package/package.json +23 -0
- package/scripts/publish.mjs +18 -0
- package/scripts/release.mjs +52 -0
- package/src/config.rs +91 -0
- package/src/main.rs +214 -0
- package/src/proxy/cache.rs +304 -0
- package/src/proxy/handlers.rs +76 -0
- package/src/proxy/load_balancer.rs +23 -0
- package/src/proxy/mod.rs +232 -0
- package/src/tls_accept.rs +119 -0
package/Cargo.toml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "nylon-mesh"
|
|
3
|
+
version = "1.0.1"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
|
|
6
|
+
[dependencies]
|
|
7
|
+
mimalloc = { version = "0.1", default-features = false, features = ["secure"] }
|
|
8
|
+
async-trait = "0.1"
|
|
9
|
+
bytes = "1.11.1"
|
|
10
|
+
http = "1.4.0"
|
|
11
|
+
moka = { version = "0.12.13", features = ["future", "sync"] }
|
|
12
|
+
once_cell = "1.19"
|
|
13
|
+
pingora = { version = "0.7.0", features = ["openssl"] }
|
|
14
|
+
pingora-core = { version = "0.7.0", features = ["openssl"] }
|
|
15
|
+
pingora-proxy = "0.7.0"
|
|
16
|
+
pingora-cache = "0.7.0"
|
|
17
|
+
pingora-load-balancing = "0.7.0"
|
|
18
|
+
redis = { version = "1.0.4", features = ["tokio-comp"] }
|
|
19
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
20
|
+
serde_yaml = "0.9"
|
|
21
|
+
tokio = { version = "1.40", features = ["full"] }
|
|
22
|
+
tracing = "0.1"
|
|
23
|
+
tracing-subscriber = "0.3"
|
|
24
|
+
openssl = { version = "0.10", default-features = false }
|
|
25
|
+
serde_json = "1.0.149"
|
|
26
|
+
|
|
27
|
+
[profile.release]
|
|
28
|
+
overflow-checks = true
|
|
29
|
+
strip = true
|
|
30
|
+
opt-level = "z"
|
|
31
|
+
lto = true
|
|
32
|
+
codegen-units = 1
|
|
33
|
+
panic = "abort"
|
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🕸️ Nylon Mesh
|
|
4
|
+
|
|
5
|
+
**Cache Everything. Scale Instantly.**
|
|
6
|
+
|
|
7
|
+
[](https://www.rust-lang.org/)
|
|
8
|
+
[](#)
|
|
9
|
+
|
|
10
|
+
*A blazing-fast edge proxy built to solve the headaches of caching for modern SSR frameworks.*
|
|
11
|
+
|
|
12
|
+
[Why Nylon Mesh?](#why-nylon-mesh) • [Features](#key-features) • [Quick Start](#installation) • [Configuration](#configuration-example-nylon-meshyaml)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Why Nylon Mesh?
|
|
19
|
+
|
|
20
|
+
Frameworks like **Next.js, Nuxt, React (SSR), Angular, and Vue** are powerful—but server-side rendering is computationally expensive. Running Node.js under heavy traffic without a dedicated caching layer leads to:
|
|
21
|
+
|
|
22
|
+
- High CPU usage and slow Time to First Byte (TTFB)
|
|
23
|
+
- Potential crashes under traffic spikes
|
|
24
|
+
- Complex custom caching logic inside your app
|
|
25
|
+
|
|
26
|
+
**Nylon Mesh sits in front of your app.** It intercepts HTTP requests, caches the expensive SSR-generated HTML in RAM (Tier 1) and Redis (Tier 2), and serves subsequent users instantly—dropping your backend load to near zero.
|
|
27
|
+
|
|
28
|
+
## Key Features
|
|
29
|
+
|
|
30
|
+
- ⚡️ **Blazing Fast**: Built in Rust on top of Pingora.
|
|
31
|
+
- 🚀 **Two-Tier Caching**: Uses a lightning-fast in-memory RAM cache (Tier-1) and falls back to Redis (Tier-2).
|
|
32
|
+
- ⚖️ **Load Balancing**: Built-in support for Round Robin and Weighted routing to multiple upstream servers.
|
|
33
|
+
- đź› **Zero-Code Integration**: Works with any backend by just placing it in front of your existing app. No SDKs needed.
|
|
34
|
+
- ⚙️ **Simple Configuration**: Easy to set up using a single YAML file.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Nylon Mesh is written in **Rust** for maximum performance. You can compile and run it directly:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cargo build --release
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or install it via Bun into your Node.js project:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bun add nylon-mesh
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Initialization
|
|
51
|
+
|
|
52
|
+
Generate a ready-to-use configuration file in your project:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
bunx nylon-mesh init
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This creates a `nylon-mesh.yaml` in your project folder. **No code changes are required in your application.**
|
|
59
|
+
|
|
60
|
+
## Running the Proxy
|
|
61
|
+
|
|
62
|
+
Start by pointing the proxy at your generated config file:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
cargo run --release -- nylon-mesh.yaml
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or via the CLI wrapper if installed via package manager:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
bunx nylon-mesh start nylon-mesh.yaml
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Traffic hitting port `3000` (default) is now being cached and routed to your backend efficiently!
|
|
75
|
+
|
|
76
|
+
## Configuration Example (`nylon-mesh.yaml`)
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
listen: "0.0.0.0:3000"
|
|
80
|
+
upstreams:
|
|
81
|
+
- "127.0.0.1:3001"
|
|
82
|
+
load_balancer_algo: "round_robin"
|
|
83
|
+
redis_url: "redis://localhost:6379"
|
|
84
|
+
|
|
85
|
+
cache:
|
|
86
|
+
tier1_capacity: 10000
|
|
87
|
+
tier1_ttl_seconds: 3
|
|
88
|
+
tier2_ttl_seconds: 60
|
|
89
|
+
status: [200, 404]
|
|
90
|
+
content_types:
|
|
91
|
+
- "text/html"
|
|
92
|
+
|
|
93
|
+
bypass:
|
|
94
|
+
paths:
|
|
95
|
+
- "/_next/"
|
|
96
|
+
- "/api/"
|
|
97
|
+
extensions:
|
|
98
|
+
- ".ico"
|
|
99
|
+
- ".png"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { spawnSync } = require('child_process');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const command = args[0] || 'start';
|
|
9
|
+
|
|
10
|
+
const REPO = 'AssetsArt/nylon-mesh';
|
|
11
|
+
const BINARY_NAME = 'nylon-mesh';
|
|
12
|
+
|
|
13
|
+
const DEFAULT_YAML = `# threads: 10
|
|
14
|
+
# liveness_path: "/_health/live"
|
|
15
|
+
# readiness_path: "/_health/ready"
|
|
16
|
+
# grace_period_seconds: 0
|
|
17
|
+
# graceful_shutdown_timeout_seconds: 0
|
|
18
|
+
listen: "0.0.0.0:3000"
|
|
19
|
+
# tls:
|
|
20
|
+
# listen: "0.0.0.0:443"
|
|
21
|
+
# certs:
|
|
22
|
+
# - host: "default"
|
|
23
|
+
# cert_path: "cert.pem"
|
|
24
|
+
# key_path: "key.pem"
|
|
25
|
+
upstreams:
|
|
26
|
+
- "127.0.0.1:3001"
|
|
27
|
+
# - address: "127.0.0.1:3002"
|
|
28
|
+
# weight: 5
|
|
29
|
+
load_balancer_algo: "round_robin"
|
|
30
|
+
redis_url: "redis://localhost:6379"
|
|
31
|
+
cache:
|
|
32
|
+
tier1_capacity: 10000
|
|
33
|
+
tier1_ttl_seconds: 3
|
|
34
|
+
tier2_ttl_seconds: 60
|
|
35
|
+
status:
|
|
36
|
+
- 200
|
|
37
|
+
content_types:
|
|
38
|
+
- "text/html"
|
|
39
|
+
bypass:
|
|
40
|
+
paths:
|
|
41
|
+
- "/_next/"
|
|
42
|
+
- "/api/"
|
|
43
|
+
extensions:
|
|
44
|
+
- ".ico"
|
|
45
|
+
- ".png"
|
|
46
|
+
# cache_control:
|
|
47
|
+
# - value: "public, max-age=31536000, immutable"
|
|
48
|
+
# paths:
|
|
49
|
+
# - "/_next/static/"
|
|
50
|
+
# extensions:
|
|
51
|
+
# - ".ico"
|
|
52
|
+
# - ".png"
|
|
53
|
+
# - ".jpg"
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
function getPlatformString() {
|
|
57
|
+
const platform = process.platform;
|
|
58
|
+
const arch = process.arch;
|
|
59
|
+
|
|
60
|
+
let osStr = '';
|
|
61
|
+
switch (platform) {
|
|
62
|
+
case 'win32': osStr = 'windows'; break;
|
|
63
|
+
case 'darwin': osStr = 'macos'; break;
|
|
64
|
+
case 'linux': osStr = 'linux-gnu'; break; // Default to gnu, musl support can be added if statically linked or specify manually
|
|
65
|
+
default: throw new Error(`Unsupported platform: ${platform}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let archStr = '';
|
|
69
|
+
switch (arch) {
|
|
70
|
+
case 'x64': archStr = 'x86_64'; break;
|
|
71
|
+
case 'arm64': archStr = 'aarch64'; break;
|
|
72
|
+
default: throw new Error(`Unsupported architecture: ${arch}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Windows suffix
|
|
76
|
+
const ext = platform === 'win32' ? '.exe' : '';
|
|
77
|
+
|
|
78
|
+
// Format: nylon-mesh-{platform}-{arch}{ext}
|
|
79
|
+
return `nylon-mesh-${osStr}-${archStr}${ext}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function httpsGet(url, options = {}) {
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
https.get(url, { headers: { 'User-Agent': 'nylon-mesh-cli' }, ...options }, (res) => {
|
|
85
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
86
|
+
resolve(httpsGet(res.headers.location, options));
|
|
87
|
+
} else if (res.statusCode === 200) {
|
|
88
|
+
resolve(res);
|
|
89
|
+
} else {
|
|
90
|
+
reject(new Error(`Failed with status code: ${res.statusCode}`));
|
|
91
|
+
}
|
|
92
|
+
}).on('error', reject);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function getLatestReleaseVersion() {
|
|
97
|
+
try {
|
|
98
|
+
const res = await httpsGet(`https://api.github.com/repos/${REPO}/releases/latest`);
|
|
99
|
+
let data = '';
|
|
100
|
+
for await (const chunk of res) { data += chunk; }
|
|
101
|
+
const release = JSON.parse(data);
|
|
102
|
+
return release.tag_name;
|
|
103
|
+
} catch (err) {
|
|
104
|
+
console.error('Failed to fetch latest release from GitHub API.', err.message);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function downloadBinary(targetPath) {
|
|
110
|
+
const version = await getLatestReleaseVersion();
|
|
111
|
+
if (!version) {
|
|
112
|
+
console.error('Could not determine latest version. Please build from source using `cargo build --release`.');
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let platformName;
|
|
117
|
+
try {
|
|
118
|
+
platformName = getPlatformString();
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error(e.message);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const downloadUrl = `https://github.com/${REPO}/releases/download/${version}/${platformName}`;
|
|
125
|
+
console.log(`Downloading ${platformName} (${version})...`);
|
|
126
|
+
console.log(`From: ${downloadUrl}`);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const res = await httpsGet(downloadUrl);
|
|
130
|
+
const fileStream = fs.createWriteStream(targetPath);
|
|
131
|
+
await new Promise((resolve, reject) => {
|
|
132
|
+
res.pipe(fileStream);
|
|
133
|
+
res.on('error', reject);
|
|
134
|
+
fileStream.on('finish', () => {
|
|
135
|
+
fileStream.close();
|
|
136
|
+
resolve();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
fs.chmodSync(targetPath, 0o755); // Make it executable
|
|
140
|
+
console.log('Download complete.');
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error('Download failed:', err.message);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function main() {
|
|
148
|
+
const localBinDir = path.join(__dirname, '..', 'bin');
|
|
149
|
+
const exeExt = process.platform === 'win32' ? '.exe' : '';
|
|
150
|
+
const downloadedBinaryPath = path.join(localBinDir, `${BINARY_NAME}-bin${exeExt}`);
|
|
151
|
+
|
|
152
|
+
if (command === 'init') {
|
|
153
|
+
const targetPath = path.join(process.cwd(), 'nylon-mesh.yaml');
|
|
154
|
+
if (fs.existsSync(targetPath)) {
|
|
155
|
+
console.error('nylon-mesh.yaml already exists.');
|
|
156
|
+
} else {
|
|
157
|
+
fs.writeFileSync(targetPath, DEFAULT_YAML);
|
|
158
|
+
console.log('Created nylon-mesh.yaml!');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!fs.existsSync(downloadedBinaryPath)) {
|
|
162
|
+
await downloadBinary(downloadedBinaryPath);
|
|
163
|
+
} else {
|
|
164
|
+
console.log('Binary already downloaded.');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log('Run `npx nylon-mesh start` to start the proxy.');
|
|
168
|
+
process.exit(0);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (command === 'start') {
|
|
172
|
+
const targetReleasePath = path.join(__dirname, '..', 'target', 'release', `${BINARY_NAME}${exeExt}`);
|
|
173
|
+
const targetDebugPath = path.join(__dirname, '..', 'target', 'debug', `${BINARY_NAME}${exeExt}`);
|
|
174
|
+
|
|
175
|
+
let exeToRun = null;
|
|
176
|
+
if (fs.existsSync(downloadedBinaryPath)) {
|
|
177
|
+
exeToRun = downloadedBinaryPath;
|
|
178
|
+
} else if (fs.existsSync(targetReleasePath)) {
|
|
179
|
+
exeToRun = targetReleasePath;
|
|
180
|
+
} else if (fs.existsSync(targetDebugPath)) {
|
|
181
|
+
exeToRun = targetDebugPath;
|
|
182
|
+
} else {
|
|
183
|
+
console.log('Nylon Mesh binary not found. Downloading...');
|
|
184
|
+
await downloadBinary(downloadedBinaryPath);
|
|
185
|
+
exeToRun = downloadedBinaryPath;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let yamlPath = path.join(process.cwd(), 'nylon-mesh.yaml');
|
|
189
|
+
if (args[1]) {
|
|
190
|
+
yamlPath = path.resolve(process.cwd(), args[1]);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!fs.existsSync(yamlPath)) {
|
|
194
|
+
console.error(`Config file not found at ${yamlPath}. Run \`npx nylon-mesh init\` first.`);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log(`Starting Nylon Mesh with config: ${yamlPath}`);
|
|
199
|
+
const child = spawnSync(exeToRun, [yamlPath], { stdio: 'inherit' });
|
|
200
|
+
process.exit(child.status || 0);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
console.error(`Unknown command: ${command}`);
|
|
204
|
+
console.error(`Usage:`);
|
|
205
|
+
console.error(` npx nylon-mesh init - Create a default config file and download binary`);
|
|
206
|
+
console.error(` npx nylon-mesh start - Start the proxy server`);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
main().catch(err => {
|
|
211
|
+
console.error(err);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
});
|