heliumts 0.2.4 → 0.2.6

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
@@ -1,6 +1,6 @@
1
1
  [![Status](https://img.shields.io/badge/status-active-success.svg)]()
2
- [![GitHub Issues](https://img.shields.io/github/issues/heliobentes/heliumjs)](https://github.com/heliobentes/heliumjs/issues)
3
- [![GitHub Pull Requests](https://img.shields.io/github/issues-pr/heliobentes/heliumjs)](https://github.com/heliobentes/heliumjs/pulls)
2
+ [![GitHub Issues](https://img.shields.io/github/issues/heliobentes/heliumts)](https://github.com/heliobentes/heliumts/issues)
3
+ [![GitHub Pull Requests](https://img.shields.io/github/issues-pr/heliobentes/heliumts)](https://github.com/heliobentes/heliumts/pulls)
4
4
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](/LICENSE)
5
5
 
6
6
  # HeliumTS
@@ -31,49 +31,21 @@ HeliumTS is a blazing fast 🚀 and opinionated full-stack React + Vite framewor
31
31
 
32
32
  ### 1.1. Installation
33
33
 
34
- An installation script is coming soon! Meanwhile, follow these steps to set up a new HeliumTS project.
35
-
36
- #### 1.1.1. Install React + Vite
37
-
38
- ```bash
39
- npm create vite@latest my-helium-app -- --template react-ts
40
- ```
41
- #### 1.1.2. Install HeliumJS
34
+ The easiest way to get started with HeliumTS is by using the scaffolding tool:
42
35
 
43
36
  ```bash
44
- npm install heliumts
37
+ npm create heliumts-app@latest my-helium-app
45
38
  ```
46
39
 
47
- #### 1.1.3. Setup Vite Config
48
- Create or update `vite.config.ts` in the project root to include Helium's Vite plugin:
49
-
50
- ```typescript
51
- import react from '@vitejs/plugin-react';
52
- import helium from 'heliumts/vite';
53
- import { defineConfig } from 'vite';
40
+ Or (to create in the current directory):
54
41
 
55
- export default defineConfig({
56
- plugins: [react(), helium()]
57
- });
58
42
  ```
59
-
60
- #### 1.1.4. Delete **main.tsx**
61
- Delete the `src/main.tsx` file created by Vite, as HeliumTS handles the client entry point automatically.
62
- Also, remove its reference from `index.html` if present.
63
- ```html
64
- <!-- Remove this from index.html -->
65
- <script type="module" src="/src/main.tsx"></script>
43
+ npm create heliumts-app@latest .
66
44
  ```
67
45
 
68
- #### 1.1.5. Update `src/App.tsx`
69
- Replace the contents of `src/App.tsx` with the following content:
70
- ```tsx
71
- import { type AppShellProps } from "heliumts/client";
46
+ This command will guide you through setting up a new project with everything configured for you.
72
47
 
73
- export default function App({ Component, pageProps }: AppShellProps) {
74
- return <Component {...pageProps} />;
75
- }
76
- ```
48
+ If you prefer to set up the project manually, please refer to the [Manual Installation Guide](./docs/manual-installation.md).
77
49
 
78
50
  ### 1.2. Running the Development Server
79
51
 
@@ -91,7 +63,7 @@ npx helium build
91
63
  npx helium start
92
64
  ```
93
65
 
94
- Check the working Example APP at: [https://github.com/heliobentes/heliumjs-example-app](https://github.com/heliobentes/heliumjs-example-app)
66
+ Check the working Example APP at: [https://github.com/heliobentes/heliumts-example-app](https://github.com/heliobentes/heliumts-example-app)
95
67
 
96
68
  ## 2. Project Structure
97
69
 
@@ -336,6 +308,9 @@ See [SSG Documentation](./docs/ssg.md) for detailed information including limita
336
308
 
337
309
  ## 5. More Documentation
338
310
 
311
+ ### Getting Started
312
+ - [Manual Installation](./docs/manual-installation.md) - Step-by-step guide to setting up a HeliumTS project manually
313
+
339
314
  ### Core Features
340
315
  - [Routing & useRouter](./docs/routing.md) - File-based routing, dynamic routes, navigation, and the useRouter hook
341
316
  - [Configuration](./docs/helium-config.md) - Configure RPC encoding, compression, security, and proxy settings
File without changes
@@ -0,0 +1,48 @@
1
+ import type http from "http";
2
+ /**
3
+ * Extracts the client IP address from an HTTP request, taking into account proxy configurations.
4
+ *
5
+ * When behind proxies (like Vercel, Cloudflare, AWS ALB, etc.), the X-Forwarded-For header
6
+ * contains a chain of IP addresses. The format is: "client, proxy1, proxy2, ..."
7
+ *
8
+ * @param req - The HTTP request object
9
+ * @param trustProxyDepth - Number of proxy levels to trust
10
+ * - 0: Only use req.socket.remoteAddress (no proxy trust)
11
+ * - 1: Trust 1 proxy level (get the last IP before your server)
12
+ * - 2+: Trust multiple proxy levels (for complex setups)
13
+ *
14
+ * Examples:
15
+ * - trustProxyDepth=0: Direct connection, no proxies
16
+ * X-Forwarded-For: ignored
17
+ * Result: req.socket.remoteAddress
18
+ *
19
+ * - trustProxyDepth=1: Behind one proxy (e.g., Vercel, Netlify)
20
+ * X-Forwarded-For: "203.0.113.1, 198.51.100.1"
21
+ * Result: "203.0.113.1" (client IP)
22
+ *
23
+ * - trustProxyDepth=2: Behind two proxies (e.g., Cloudflare -> Load Balancer)
24
+ * X-Forwarded-For: "203.0.113.1, 198.51.100.1, 192.0.2.1"
25
+ * Result: "203.0.113.1" (client IP)
26
+ *
27
+ * Common configurations:
28
+ * - Vercel/Netlify/Railway: trustProxyDepth=1
29
+ * - Cloudflare -> Origin: trustProxyDepth=1 or 2 (depending on your setup)
30
+ * - AWS ALB -> EC2: trustProxyDepth=1
31
+ * - Nginx -> Node: trustProxyDepth=1
32
+ * - Cloudflare -> Nginx -> Node: trustProxyDepth=2
33
+ */
34
+ export declare function extractClientIP(req: http.IncomingMessage, trustProxyDepth?: number): string;
35
+ /**
36
+ * Alternative extraction method that works from the right (trusts the rightmost IPs).
37
+ * This is useful when you want to trust the last N proxies in the chain.
38
+ *
39
+ * @param req - The HTTP request object
40
+ * @param trustProxyDepth - Number of proxy levels to trust from the right
41
+ *
42
+ * Example:
43
+ * X-Forwarded-For: "203.0.113.1, 198.51.100.1, 192.0.2.1"
44
+ * trustProxyDepth=1: Result is "198.51.100.1" (skip the last trusted proxy)
45
+ * trustProxyDepth=2: Result is "203.0.113.1" (skip the last 2 trusted proxies)
46
+ */
47
+ export declare function extractClientIPFromRight(req: http.IncomingMessage, trustProxyDepth?: number): string;
48
+ //# sourceMappingURL=ipExtractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipExtractor.d.ts","sourceRoot":"","sources":["../../src/server/ipExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,GAAE,MAAU,GAAG,MAAM,CAqC9F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,GAAE,MAAU,GAAG,MAAM,CAsBvG"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Extracts the client IP address from an HTTP request, taking into account proxy configurations.
3
+ *
4
+ * When behind proxies (like Vercel, Cloudflare, AWS ALB, etc.), the X-Forwarded-For header
5
+ * contains a chain of IP addresses. The format is: "client, proxy1, proxy2, ..."
6
+ *
7
+ * @param req - The HTTP request object
8
+ * @param trustProxyDepth - Number of proxy levels to trust
9
+ * - 0: Only use req.socket.remoteAddress (no proxy trust)
10
+ * - 1: Trust 1 proxy level (get the last IP before your server)
11
+ * - 2+: Trust multiple proxy levels (for complex setups)
12
+ *
13
+ * Examples:
14
+ * - trustProxyDepth=0: Direct connection, no proxies
15
+ * X-Forwarded-For: ignored
16
+ * Result: req.socket.remoteAddress
17
+ *
18
+ * - trustProxyDepth=1: Behind one proxy (e.g., Vercel, Netlify)
19
+ * X-Forwarded-For: "203.0.113.1, 198.51.100.1"
20
+ * Result: "203.0.113.1" (client IP)
21
+ *
22
+ * - trustProxyDepth=2: Behind two proxies (e.g., Cloudflare -> Load Balancer)
23
+ * X-Forwarded-For: "203.0.113.1, 198.51.100.1, 192.0.2.1"
24
+ * Result: "203.0.113.1" (client IP)
25
+ *
26
+ * Common configurations:
27
+ * - Vercel/Netlify/Railway: trustProxyDepth=1
28
+ * - Cloudflare -> Origin: trustProxyDepth=1 or 2 (depending on your setup)
29
+ * - AWS ALB -> EC2: trustProxyDepth=1
30
+ * - Nginx -> Node: trustProxyDepth=1
31
+ * - Cloudflare -> Nginx -> Node: trustProxyDepth=2
32
+ */
33
+ export function extractClientIP(req, trustProxyDepth = 0) {
34
+ // If not trusting any proxies, return the direct connection IP
35
+ if (trustProxyDepth === 0) {
36
+ return req.socket.remoteAddress || "unknown";
37
+ }
38
+ // Get X-Forwarded-For header
39
+ const forwardedFor = req.headers["x-forwarded-for"];
40
+ if (!forwardedFor) {
41
+ // No X-Forwarded-For header, fall back to direct connection
42
+ return req.socket.remoteAddress || "unknown";
43
+ }
44
+ // Parse X-Forwarded-For header (can be a string or array of strings)
45
+ const forwardedIPs = (Array.isArray(forwardedFor) ? forwardedFor.join(",") : forwardedFor)
46
+ .split(",")
47
+ .map((ip) => ip.trim())
48
+ .filter((ip) => ip.length > 0);
49
+ if (forwardedIPs.length === 0) {
50
+ // Empty X-Forwarded-For, fall back to direct connection
51
+ return req.socket.remoteAddress || "unknown";
52
+ }
53
+ // The client IP is at the beginning of the chain
54
+ // We trust the chain up to trustProxyDepth levels
55
+ // Format: [clientIP, proxy1, proxy2, ..., lastProxy]
56
+ // We want the clientIP, but we need to verify we have enough trusted proxies
57
+ if (forwardedIPs.length < trustProxyDepth) {
58
+ // Not enough IPs in the chain, the chain might be incomplete or spoofed
59
+ // Fall back to direct connection for safety
60
+ return req.socket.remoteAddress || "unknown";
61
+ }
62
+ // Return the client IP (first in the chain)
63
+ return forwardedIPs[0];
64
+ }
65
+ /**
66
+ * Alternative extraction method that works from the right (trusts the rightmost IPs).
67
+ * This is useful when you want to trust the last N proxies in the chain.
68
+ *
69
+ * @param req - The HTTP request object
70
+ * @param trustProxyDepth - Number of proxy levels to trust from the right
71
+ *
72
+ * Example:
73
+ * X-Forwarded-For: "203.0.113.1, 198.51.100.1, 192.0.2.1"
74
+ * trustProxyDepth=1: Result is "198.51.100.1" (skip the last trusted proxy)
75
+ * trustProxyDepth=2: Result is "203.0.113.1" (skip the last 2 trusted proxies)
76
+ */
77
+ export function extractClientIPFromRight(req, trustProxyDepth = 0) {
78
+ if (trustProxyDepth === 0) {
79
+ return req.socket.remoteAddress || "unknown";
80
+ }
81
+ const forwardedFor = req.headers["x-forwarded-for"];
82
+ if (!forwardedFor) {
83
+ return req.socket.remoteAddress || "unknown";
84
+ }
85
+ const forwardedIPs = (Array.isArray(forwardedFor) ? forwardedFor.join(",") : forwardedFor)
86
+ .split(",")
87
+ .map((ip) => ip.trim())
88
+ .filter((ip) => ip.length > 0);
89
+ if (forwardedIPs.length === 0) {
90
+ return req.socket.remoteAddress || "unknown";
91
+ }
92
+ // Calculate which IP to trust by skipping the rightmost N trusted proxies
93
+ const clientIPIndex = Math.max(0, forwardedIPs.length - trustProxyDepth - 1);
94
+ return forwardedIPs[clientIPIndex];
95
+ }
96
+ //# sourceMappingURL=ipExtractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipExtractor.js","sourceRoot":"","sources":["../../src/server/ipExtractor.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,eAAe,CAAC,GAAyB,EAAE,kBAA0B,CAAC;IAClF,+DAA+D;IAC/D,IAAI,eAAe,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,6BAA6B;IAC7B,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACpD,IAAI,CAAC,YAAY,EAAE,CAAC;QAChB,4DAA4D;QAC5D,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,qEAAqE;IACrE,MAAM,YAAY,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;SACrF,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;SACtB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEnC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,wDAAwD;QACxD,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,iDAAiD;IACjD,kDAAkD;IAClD,qDAAqD;IACrD,6EAA6E;IAE7E,IAAI,YAAY,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QACxC,wEAAwE;QACxE,4CAA4C;QAC5C,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,4CAA4C;IAC5C,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAyB,EAAE,kBAA0B,CAAC;IAC3F,IAAI,eAAe,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACpD,IAAI,CAAC,YAAY,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;SACrF,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;SACtB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEnC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;IACjD,CAAC;IAED,0EAA0E;IAC1E,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,MAAM,GAAG,eAAe,GAAG,CAAC,CAAC,CAAC;IAC7E,OAAO,YAAY,CAAC,aAAa,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=deepEqual.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepEqual.d.ts","sourceRoot":"","sources":["../../src/utils/deepEqual.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=deepEqual.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepEqual.js","sourceRoot":"","sources":["../../src/utils/deepEqual.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function formatError(err: unknown): string;
2
+ //# sourceMappingURL=formatError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatError.d.ts","sourceRoot":"","sources":["../../src/utils/formatError.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAgBhD"}
@@ -0,0 +1,18 @@
1
+ export function formatError(err) {
2
+ console.log("🚀 ~ formatError ~ err:", err);
3
+ if (err instanceof Error) {
4
+ return err.message;
5
+ }
6
+ if (typeof err === "object" && err !== null) {
7
+ if ("message" in err) {
8
+ return String(err.message);
9
+ }
10
+ // Format Record<string, string> errors
11
+ return JSON.stringify(err, null, 2);
12
+ }
13
+ if (typeof err === "string") {
14
+ return err;
15
+ }
16
+ return String(err);
17
+ }
18
+ //# sourceMappingURL=formatError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatError.js","sourceRoot":"","sources":["../../src/utils/formatError.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,GAAY;IACpC,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC;IAC5C,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC1C,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QACD,uCAAuC;QACvC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heliumts",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "A lightweight full-stack React framework with file-based routing, RPC, and SSG support",
5
5
  "keywords": [
6
6
  "react",
@@ -15,11 +15,11 @@
15
15
  "license": "MIT",
16
16
  "repository": {
17
17
  "type": "git",
18
- "url": "https://github.com/heliobentes/heliumjs.git"
18
+ "url": "https://github.com/heliobentes/heliumts.git"
19
19
  },
20
- "homepage": "https://heliumjs.com",
20
+ "homepage": "https://heliumts.com",
21
21
  "bugs": {
22
- "url": "https://github.com/heliobentes/heliumjs/issues"
22
+ "url": "https://github.com/heliobentes/heliumts/issues"
23
23
  },
24
24
  "bin": {
25
25
  "helium": "dist/bin/helium.js"