@aryanbansal-launch/edge-utils 0.1.0 → 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/dist/index.js +1 -0
- package/dist/nextjs/rsc.js +16 -0
- package/package.json +5 -2
- package/readme.md +115 -52
package/dist/index.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function handleNextJS_RSC(request, options) {
|
|
2
|
+
const RSC_HEADER = 'rsc';
|
|
3
|
+
const RSC_HEADER_VALUE = '1';
|
|
4
|
+
const RSC_QUERY_PARAM = '_rsc';
|
|
5
|
+
const parsedUrl = new URL(request.url);
|
|
6
|
+
const route = parsedUrl.pathname;
|
|
7
|
+
const rscQueryParamExists = !!parsedUrl.searchParams.get(RSC_QUERY_PARAM);
|
|
8
|
+
const rscHeaderExists = request.headers.get(RSC_HEADER) === RSC_HEADER_VALUE;
|
|
9
|
+
const isAffectedPath = options.affectedPaths.includes(route);
|
|
10
|
+
if (isAffectedPath && !rscQueryParamExists && rscHeaderExists) {
|
|
11
|
+
const modifiedRequest = new Request(request);
|
|
12
|
+
modifiedRequest.headers.delete(RSC_HEADER);
|
|
13
|
+
return fetch(modifiedRequest);
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aryanbansal-launch/edge-utils",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"license": "MIT",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"exports": {
|
|
7
8
|
".": "./dist/index.js"
|
|
8
9
|
},
|
|
9
|
-
"files": [
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
10
13
|
"scripts": {
|
|
11
14
|
"build": "tsc"
|
|
12
15
|
}
|
package/readme.md
CHANGED
|
@@ -1,29 +1,35 @@
|
|
|
1
|
-
# Edge Utils
|
|
1
|
+
# 🚀 Edge Utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@launch/edge-utils)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](#platform-support)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
A lightweight, developer-friendly toolkit for **Edge Computing**. Speed up your development on platforms like Cloudflare Workers, Vercel Edge, and Contentstack Launch with production-ready utilities for security, authentication, and routing.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
- [Usage Example](#usage-example)
|
|
9
|
-
- [API Reference](#api-reference)
|
|
10
|
-
- [Security](#security)
|
|
11
|
-
- [Authentication](#authentication)
|
|
12
|
-
- [Redirection](#redirection)
|
|
13
|
-
- [Geo Location](#geo-location)
|
|
14
|
-
- [Responses](#responses)
|
|
9
|
+
---
|
|
15
10
|
|
|
16
|
-
##
|
|
11
|
+
## ✨ Features
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
- 🛡️ **Security First**: Block AI crawlers and manage IP access with ease.
|
|
14
|
+
- 🔐 **Edge Auth**: Implement Basic Auth directly at the edge for specific hostnames.
|
|
15
|
+
- 📍 **Geo-Aware**: Easily extract location data from request headers.
|
|
16
|
+
- ⚛️ **Next.js Ready**: Built-in fixes for RSC header issues on edge proxies.
|
|
17
|
+
- 🔀 **Smart Routing**: Declarative redirects based on path and method.
|
|
18
|
+
- ⚡ **Zero Dependencies**: Lightweight and optimized for edge runtime limits.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
19
23
|
|
|
20
24
|
```bash
|
|
21
25
|
npm install @launch/edge-utils
|
|
22
26
|
```
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 🛠️ Usage Example
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
Transform your edge handler into a powerful middleware chain:
|
|
27
33
|
|
|
28
34
|
```typescript
|
|
29
35
|
import {
|
|
@@ -33,77 +39,134 @@ import {
|
|
|
33
39
|
protectWithBasicAuth,
|
|
34
40
|
ipAccessControl,
|
|
35
41
|
blockAICrawlers,
|
|
36
|
-
getGeoHeaders
|
|
42
|
+
getGeoHeaders,
|
|
43
|
+
handleNextJS_RSC
|
|
37
44
|
} from "@launch/edge-utils";
|
|
38
45
|
|
|
39
46
|
export default async function handler(request: Request) {
|
|
40
|
-
// 1.
|
|
47
|
+
// 1. ⚛️ Fix Next.js RSC issues for specific paths
|
|
48
|
+
const rscResponse = await handleNextJS_RSC(request, {
|
|
49
|
+
affectedPaths: ["/my-rsc-page", "/another-page"]
|
|
50
|
+
});
|
|
51
|
+
if (rscResponse) return rscResponse;
|
|
52
|
+
|
|
53
|
+
// 2. 🛡️ Block AI bots immediately
|
|
41
54
|
const botResponse = blockAICrawlers(request);
|
|
42
55
|
if (botResponse) return botResponse;
|
|
43
56
|
|
|
44
|
-
// 2. IP
|
|
57
|
+
// 2. 🧱 IP Whitelisting
|
|
45
58
|
const ipResponse = ipAccessControl(request, { allow: ["203.0.113.10"] });
|
|
46
59
|
if (ipResponse) return ipResponse;
|
|
47
60
|
|
|
48
|
-
// 3.
|
|
61
|
+
// 3. 🔐 Domain-specific Basic Auth
|
|
49
62
|
const authResponse = await protectWithBasicAuth(request, {
|
|
50
|
-
hostnameIncludes: "
|
|
63
|
+
hostnameIncludes: "staging.myapp.com",
|
|
51
64
|
username: "admin",
|
|
52
|
-
password: "
|
|
65
|
+
password: "securepassword123"
|
|
53
66
|
});
|
|
54
67
|
if (authResponse && authResponse.status === 401) return authResponse;
|
|
55
68
|
|
|
56
|
-
// 4.
|
|
69
|
+
// 4. 🔀 SEO-friendly Redirects
|
|
57
70
|
const redirectResponse = redirectIfMatch(request, {
|
|
58
|
-
path: "/
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
path: "/legacy-url",
|
|
72
|
+
to: "/modern-url",
|
|
73
|
+
status: 301
|
|
61
74
|
});
|
|
62
75
|
if (redirectResponse) return redirectResponse;
|
|
63
76
|
|
|
64
|
-
// 5.
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
// 5. 📍 Geo-Location Access
|
|
78
|
+
const geo = getGeoHeaders(request);
|
|
79
|
+
if (geo.country === "US") {
|
|
80
|
+
console.log(`User from ${geo.city}, ${geo.region}`);
|
|
67
81
|
}
|
|
68
82
|
|
|
69
|
-
// 6.
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
// 6. 📤 Custom JSON Responses
|
|
84
|
+
if (new URL(request.url).pathname === "/api/health") {
|
|
85
|
+
return jsonResponse({ status: "healthy", region: geo.region });
|
|
86
|
+
}
|
|
72
87
|
|
|
73
|
-
// 7. Pass through
|
|
88
|
+
// 7. 🚀 Pass through to origin
|
|
74
89
|
return passThrough(request);
|
|
75
90
|
}
|
|
76
91
|
```
|
|
77
92
|
|
|
78
|
-
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 📖 API Reference
|
|
96
|
+
|
|
97
|
+
### 🛡️ Security
|
|
98
|
+
|
|
99
|
+
#### `blockAICrawlers`
|
|
100
|
+
Blocks common AI crawlers (GPTBot, ClaudeBot, etc.) to protect your content from scraping.
|
|
101
|
+
- **Parameters**: `request: Request`, `bots?: string[]` (optional list to override defaults)
|
|
102
|
+
- **Returns**: `Response` (403) or `null`.
|
|
103
|
+
|
|
104
|
+
#### `ipAccessControl`
|
|
105
|
+
Simple firewall for your edge function.
|
|
106
|
+
- **Options**:
|
|
107
|
+
- `allow`: Array of IPs allowed to access.
|
|
108
|
+
- `deny`: Array of IPs to block.
|
|
109
|
+
- **Returns**: `Response` (403) or `null`.
|
|
110
|
+
|
|
111
|
+
### 🔐 Authentication
|
|
112
|
+
|
|
113
|
+
#### `protectWithBasicAuth`
|
|
114
|
+
Prompt for credentials based on the hostname.
|
|
115
|
+
- **Options**:
|
|
116
|
+
- `hostnameIncludes`: Match substring in hostname (e.g., ".dev").
|
|
117
|
+
- `username`: Required username.
|
|
118
|
+
- `password`: Required password.
|
|
119
|
+
- `realm`: Optional realm name for the auth prompt.
|
|
120
|
+
- **Returns**: `Promise<Response>` or `null`.
|
|
121
|
+
|
|
122
|
+
### 🔀 Redirection
|
|
123
|
+
|
|
124
|
+
#### `redirectIfMatch`
|
|
125
|
+
Perform redirects directly at the edge to reduce latency.
|
|
126
|
+
- **Options**:
|
|
127
|
+
- `path`: The path to match.
|
|
128
|
+
- `method`: HTTP method to match (optional, defaults to any).
|
|
129
|
+
- `to`: Target path or URL.
|
|
130
|
+
- `status`: HTTP status code (default: 301).
|
|
131
|
+
- **Returns**: `Response` (Redirect) or `null`.
|
|
132
|
+
|
|
133
|
+
### 📍 Geo Location
|
|
134
|
+
|
|
135
|
+
#### `getGeoHeaders`
|
|
136
|
+
Extracts geo-information provided by edge platform headers.
|
|
137
|
+
- **Returns**: Object with `country`, `region`, `city`, `latitude`, `longitude`.
|
|
79
138
|
|
|
80
|
-
###
|
|
139
|
+
### ⚛️ Next.js
|
|
81
140
|
|
|
82
|
-
#### `
|
|
83
|
-
|
|
141
|
+
#### `handleNextJS_RSC`
|
|
142
|
+
Handles Next.js React Server Component (RSC) header issues on Contentstack Launch. It detects requests to "affected paths" that have the `rsc` header but lack the `_rsc` query parameter, and strips the header to prevent proxy/caching issues.
|
|
143
|
+
- **Options**:
|
|
144
|
+
- `affectedPaths`: Array of pathnames (e.g., `['/shop', '/about']`) where this fix should apply.
|
|
145
|
+
- **Returns**: `Promise<Response>` (the re-fetched request without RSC header) or `null`.
|
|
84
146
|
|
|
85
|
-
|
|
86
|
-
Restricts access based on the client's IP address. You can provide an `allow` list or a `deny` list.
|
|
147
|
+
---
|
|
87
148
|
|
|
88
|
-
|
|
149
|
+
## 🌐 Platform Support
|
|
89
150
|
|
|
90
|
-
|
|
91
|
-
|
|
151
|
+
Optimized for:
|
|
152
|
+
- [Contentstack Launch](https://www.contentstack.com/docs/developers/launch)
|
|
153
|
+
- [Vercel Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions)
|
|
154
|
+
- [Cloudflare Workers](https://workers.cloudflare.com/)
|
|
92
155
|
|
|
93
|
-
|
|
156
|
+
---
|
|
94
157
|
|
|
95
|
-
|
|
96
|
-
Redirects the request if the URL path and HTTP method match the provided options.
|
|
158
|
+
## 🤝 Contributing
|
|
97
159
|
|
|
98
|
-
|
|
160
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
99
161
|
|
|
100
|
-
|
|
101
|
-
|
|
162
|
+
1. Fork the Project
|
|
163
|
+
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
164
|
+
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
|
165
|
+
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
|
166
|
+
5. Open a Pull Request
|
|
102
167
|
|
|
103
|
-
|
|
168
|
+
---
|
|
104
169
|
|
|
105
|
-
|
|
106
|
-
A helper to return a JSON response with the correct `Content-Type` header.
|
|
170
|
+
## 📄 License
|
|
107
171
|
|
|
108
|
-
|
|
109
|
-
Continues the request processing by calling `fetch(request)`. Useful at the end of an edge function.
|
|
172
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|