@aryanbansal-launch/edge-utils 0.1.4 → 0.1.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/bin/launch-init.js +12 -11
- package/dist/index.js +1 -0
- package/dist/security/block-default-domains.js +15 -0
- package/package.json +1 -1
- package/readme.md +36 -97
package/bin/launch-init.js
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
protectWithBasicAuth,
|
|
25
25
|
ipAccessControl,
|
|
26
26
|
blockAICrawlers,
|
|
27
|
+
blockDefaultDomains,
|
|
27
28
|
getGeoHeaders,
|
|
28
29
|
handleNextJS_RSC
|
|
29
30
|
} from "@aryanbansal-launch/edge-utils";
|
|
@@ -33,7 +34,11 @@ import {
|
|
|
33
34
|
* This file was automatically generated by @aryanbansal-launch/edge-utils
|
|
34
35
|
*/
|
|
35
36
|
export default async function handler(request, context) {
|
|
36
|
-
// 1.
|
|
37
|
+
// 1. 🛡️ Block access via default Launch domains
|
|
38
|
+
const defaultDomainResponse = blockDefaultDomains(request);
|
|
39
|
+
if (defaultDomainResponse) return defaultDomainResponse;
|
|
40
|
+
|
|
41
|
+
// 2. ⚛️ Fix Next.js RSC issues for specific paths
|
|
37
42
|
const rscResponse = await handleNextJS_RSC(request, {
|
|
38
43
|
affectedPaths: ["/my-rsc-page", "/another-page"]
|
|
39
44
|
});
|
|
@@ -83,15 +88,15 @@ async function init() {
|
|
|
83
88
|
console.log(`\n${colors.bright}${colors.cyan}🚀 Edge Utilities: Contentstack Launch Setup${colors.reset}\n`);
|
|
84
89
|
|
|
85
90
|
let actionsTaken = 0;
|
|
86
|
-
let isRoot = true;
|
|
87
91
|
|
|
88
92
|
// 1. Root level check: Look for package.json
|
|
89
93
|
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
90
94
|
if (!fs.existsSync(packageJsonPath)) {
|
|
91
|
-
|
|
92
|
-
console.log(`${colors.yellow}
|
|
93
|
-
console.log(`${colors.yellow}
|
|
94
|
-
console.log(
|
|
95
|
+
console.log(`${colors.red}❌ Error: Root directory not detected.${colors.reset}`);
|
|
96
|
+
console.log(`${colors.yellow}Contentstack Launch requires the 'functions' folder to be at your project root${colors.reset}`);
|
|
97
|
+
console.log(`${colors.yellow}(the same directory as your package.json).${colors.reset}\n`);
|
|
98
|
+
console.log(`Please ${colors.bright}cd${colors.reset} into your project root and try again.`);
|
|
99
|
+
process.exit(1);
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
try {
|
|
@@ -114,11 +119,7 @@ async function init() {
|
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
// Final Summary Messages based on the state
|
|
117
|
-
if (
|
|
118
|
-
console.log(`\n${colors.yellow}⚠️ Setup detected in a subdirectory!${colors.reset}`);
|
|
119
|
-
console.log(`The 'functions' folder exists here, but Contentstack Launch`);
|
|
120
|
-
console.log(`requires it to be at the ${colors.bright}project root${colors.reset} (next to package.json).`);
|
|
121
|
-
} else if (actionsTaken === 0) {
|
|
122
|
+
if (actionsTaken === 0) {
|
|
122
123
|
console.log(`\n${colors.bright}${colors.blue}🏁 Everything is already set up!${colors.reset}`);
|
|
123
124
|
console.log(`No changes were made to your existing files.`);
|
|
124
125
|
} else {
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,6 @@ export * from "./redirect/redirect.js";
|
|
|
4
4
|
export * from "./auth/basic-auth.js";
|
|
5
5
|
export * from "./security/ip-access.js";
|
|
6
6
|
export * from "./security/block-bots.js";
|
|
7
|
+
export * from "./security/block-default-domains.js";
|
|
7
8
|
export * from "./geo/geo-headers.js";
|
|
8
9
|
export * from "./nextjs/rsc.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blocks access to the site when accessed via default Contentstack Launch domains.
|
|
3
|
+
* This is useful for ensuring users only access the site via your custom domain.
|
|
4
|
+
*/
|
|
5
|
+
export function blockDefaultDomains(request, options = {}) {
|
|
6
|
+
const domain = options.domainToBlock || 'contentstackapps.com';
|
|
7
|
+
const url = new URL(request.url);
|
|
8
|
+
if (url.hostname.includes(domain)) {
|
|
9
|
+
return new Response('Forbidden: Access via default domain is restricted.', {
|
|
10
|
+
status: 403,
|
|
11
|
+
statusText: 'Forbidden'
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
# 🚀 Edge Utils
|
|
1
|
+
# 🚀 Edge Utils for Contentstack Launch
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@launch/edge-utils)
|
|
3
|
+
[](https://www.npmjs.com/package/@aryanbansal-launch/edge-utils)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](#platform-support)
|
|
6
5
|
|
|
7
|
-
A lightweight,
|
|
6
|
+
A lightweight, high-performance toolkit specifically designed for **Contentstack Launch Edge Functions**. Speed up your development with production-ready utilities for security, authentication, routing, and Next.js compatibility—all optimized to run at the edge.
|
|
8
7
|
|
|
9
8
|
---
|
|
10
9
|
|
|
@@ -13,39 +12,35 @@ A lightweight, developer-friendly toolkit for **Edge Computing**. Speed up your
|
|
|
13
12
|
- 🛡️ **Security First**: Block AI crawlers and manage IP access with ease.
|
|
14
13
|
- 🔐 **Edge Auth**: Implement Basic Auth directly at the edge for specific hostnames.
|
|
15
14
|
- 📍 **Geo-Aware**: Easily extract location data from request headers.
|
|
16
|
-
- ⚛️ **Next.js Ready**: Built-in fixes for RSC header issues on
|
|
15
|
+
- ⚛️ **Next.js Ready**: Built-in fixes for RSC header issues on Launch proxies.
|
|
17
16
|
- 🔀 **Smart Routing**: Declarative redirects based on path and method.
|
|
18
17
|
- ⚡ **Zero Dependencies**: Lightweight and optimized for edge runtime limits.
|
|
19
18
|
|
|
20
19
|
---
|
|
21
20
|
|
|
22
|
-
##
|
|
21
|
+
## ⚡ Quick Start (Recommended)
|
|
23
22
|
|
|
23
|
+
Set up your entire edge environment in seconds with our automated CLI tool.
|
|
24
|
+
|
|
25
|
+
### 1. Install
|
|
24
26
|
```bash
|
|
25
|
-
npm install @launch/edge-utils
|
|
27
|
+
npm install @aryanbansal-launch/edge-utils
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
## ⚡ Quick Start (Automatic Setup)
|
|
31
|
-
|
|
32
|
-
If you are using **Contentstack Launch**, you can automatically set up your edge function directory and boilerplate handler with a single command:
|
|
33
|
-
|
|
30
|
+
### 2. Initialize
|
|
31
|
+
Run this command from your **project root**:
|
|
34
32
|
```bash
|
|
35
33
|
npx launch-init
|
|
36
34
|
```
|
|
37
|
-
|
|
38
|
-
This command will:
|
|
39
|
-
1. Create a `functions/` directory in your project root.
|
|
40
|
-
2. Generate a `[proxy].edge.js` file with a production-ready boilerplate.
|
|
35
|
+
This will automatically create the `functions/` directory and a boilerplate `[proxy].edge.js` handler for you.
|
|
41
36
|
|
|
42
37
|
---
|
|
43
38
|
|
|
44
39
|
## 🛠️ Usage Example
|
|
45
40
|
|
|
46
|
-
|
|
41
|
+
Once initialized, your `functions/[proxy].edge.js` will look like a powerful middleware chain:
|
|
47
42
|
|
|
48
|
-
```
|
|
43
|
+
```javascript
|
|
49
44
|
import {
|
|
50
45
|
jsonResponse,
|
|
51
46
|
passThrough,
|
|
@@ -53,14 +48,19 @@ import {
|
|
|
53
48
|
protectWithBasicAuth,
|
|
54
49
|
ipAccessControl,
|
|
55
50
|
blockAICrawlers,
|
|
51
|
+
blockDefaultDomains,
|
|
56
52
|
getGeoHeaders,
|
|
57
53
|
handleNextJS_RSC
|
|
58
|
-
} from "@launch/edge-utils";
|
|
54
|
+
} from "@aryanbansal-launch/edge-utils";
|
|
59
55
|
|
|
60
|
-
export default async function handler(request
|
|
61
|
-
// 1.
|
|
56
|
+
export default async function handler(request, context) {
|
|
57
|
+
// 1. 🛡️ Block access via default Launch domains
|
|
58
|
+
const defaultDomainResponse = blockDefaultDomains(request);
|
|
59
|
+
if (defaultDomainResponse) return defaultDomainResponse;
|
|
60
|
+
|
|
61
|
+
// 2. ⚛️ Fix Next.js RSC issues for specific paths
|
|
62
62
|
const rscResponse = await handleNextJS_RSC(request, {
|
|
63
|
-
affectedPaths: ["/
|
|
63
|
+
affectedPaths: ["/shop", "/about"]
|
|
64
64
|
});
|
|
65
65
|
if (rscResponse) return rscResponse;
|
|
66
66
|
|
|
@@ -68,11 +68,11 @@ export default async function handler(request: Request) {
|
|
|
68
68
|
const botResponse = blockAICrawlers(request);
|
|
69
69
|
if (botResponse) return botResponse;
|
|
70
70
|
|
|
71
|
-
//
|
|
71
|
+
// 3. 🧱 IP Whitelisting
|
|
72
72
|
const ipResponse = ipAccessControl(request, { allow: ["203.0.113.10"] });
|
|
73
73
|
if (ipResponse) return ipResponse;
|
|
74
74
|
|
|
75
|
-
//
|
|
75
|
+
// 4. 🔐 Domain-specific Basic Auth (e.g., for staging)
|
|
76
76
|
const authResponse = await protectWithBasicAuth(request, {
|
|
77
77
|
hostnameIncludes: "staging.myapp.com",
|
|
78
78
|
username: "admin",
|
|
@@ -80,7 +80,7 @@ export default async function handler(request: Request) {
|
|
|
80
80
|
});
|
|
81
81
|
if (authResponse && authResponse.status === 401) return authResponse;
|
|
82
82
|
|
|
83
|
-
//
|
|
83
|
+
// 5. 🔀 SEO-friendly Redirects
|
|
84
84
|
const redirectResponse = redirectIfMatch(request, {
|
|
85
85
|
path: "/legacy-url",
|
|
86
86
|
to: "/modern-url",
|
|
@@ -88,16 +88,9 @@ export default async function handler(request: Request) {
|
|
|
88
88
|
});
|
|
89
89
|
if (redirectResponse) return redirectResponse;
|
|
90
90
|
|
|
91
|
-
//
|
|
91
|
+
// 6. 📍 Geo-Location Access
|
|
92
92
|
const geo = getGeoHeaders(request);
|
|
93
|
-
|
|
94
|
-
console.log(`User from ${geo.city}, ${geo.region}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 6. 📤 Custom JSON Responses
|
|
98
|
-
if (new URL(request.url).pathname === "/api/health") {
|
|
99
|
-
return jsonResponse({ status: "healthy", region: geo.region });
|
|
100
|
-
}
|
|
93
|
+
console.log(`Request from ${geo.city}, ${geo.country}`);
|
|
101
94
|
|
|
102
95
|
// 7. 🚀 Pass through to origin
|
|
103
96
|
return passThrough(request);
|
|
@@ -109,78 +102,24 @@ export default async function handler(request: Request) {
|
|
|
109
102
|
## 📖 API Reference
|
|
110
103
|
|
|
111
104
|
### 🛡️ Security
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
- **Parameters**: `request: Request`, `bots?: string[]` (optional list to override defaults)
|
|
116
|
-
- **Returns**: `Response` (403) or `null`.
|
|
117
|
-
|
|
118
|
-
#### `ipAccessControl`
|
|
119
|
-
Simple firewall for your edge function.
|
|
120
|
-
- **Options**:
|
|
121
|
-
- `allow`: Array of IPs allowed to access.
|
|
122
|
-
- `deny`: Array of IPs to block.
|
|
123
|
-
- **Returns**: `Response` (403) or `null`.
|
|
105
|
+
- **`blockAICrawlers(request, bots?)`**: Blocks common AI crawlers.
|
|
106
|
+
- **`blockDefaultDomains(request, { domainToBlock? })`**: Blocks access via default Contentstack Launch domains (e.g., `contentstackapps.com`).
|
|
107
|
+
- **`ipAccessControl(request, { allow?, deny? })`**: Simple IP-based firewall.
|
|
124
108
|
|
|
125
109
|
### 🔐 Authentication
|
|
126
|
-
|
|
127
|
-
#### `protectWithBasicAuth`
|
|
128
|
-
Prompt for credentials based on the hostname.
|
|
129
|
-
- **Options**:
|
|
130
|
-
- `hostnameIncludes`: Match substring in hostname (e.g., ".dev").
|
|
131
|
-
- `username`: Required username.
|
|
132
|
-
- `password`: Required password.
|
|
133
|
-
- `realm`: Optional realm name for the auth prompt.
|
|
134
|
-
- **Returns**: `Promise<Response>` or `null`.
|
|
110
|
+
- **`protectWithBasicAuth(request, options)`**: Prompt for credentials based on hostname.
|
|
135
111
|
|
|
136
112
|
### 🔀 Redirection
|
|
137
|
-
|
|
138
|
-
#### `redirectIfMatch`
|
|
139
|
-
Perform redirects directly at the edge to reduce latency.
|
|
140
|
-
- **Options**:
|
|
141
|
-
- `path`: The path to match.
|
|
142
|
-
- `method`: HTTP method to match (optional, defaults to any).
|
|
143
|
-
- `to`: Target path or URL.
|
|
144
|
-
- `status`: HTTP status code (default: 301).
|
|
145
|
-
- **Returns**: `Response` (Redirect) or `null`.
|
|
113
|
+
- **`redirectIfMatch(request, options)`**: Perform SEO-friendly redirects at the edge.
|
|
146
114
|
|
|
147
115
|
### 📍 Geo Location
|
|
148
|
-
|
|
149
|
-
#### `getGeoHeaders`
|
|
150
|
-
Extracts geo-information provided by edge platform headers.
|
|
151
|
-
- **Returns**: Object with `country`, `region`, `city`, `latitude`, `longitude`.
|
|
116
|
+
- **`getGeoHeaders(request)`**: Returns an object with `country`, `region`, `city`, `latitude`, `longitude`.
|
|
152
117
|
|
|
153
118
|
### ⚛️ Next.js
|
|
154
|
-
|
|
155
|
-
#### `handleNextJS_RSC`
|
|
156
|
-
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.
|
|
157
|
-
- **Options**:
|
|
158
|
-
- `affectedPaths`: Array of pathnames (e.g., `['/shop', '/about']`) where this fix should apply.
|
|
159
|
-
- **Returns**: `Promise<Response>` (the re-fetched request without RSC header) or `null`.
|
|
119
|
+
- **`handleNextJS_RSC(request, { affectedPaths })`**: Resolves RSC header issues on Contentstack Launch.
|
|
160
120
|
|
|
161
121
|
---
|
|
162
122
|
|
|
163
123
|
## 🌐 Platform Support
|
|
164
124
|
|
|
165
|
-
|
|
166
|
-
- [Contentstack Launch](https://www.contentstack.com/docs/developers/launch)
|
|
167
|
-
- [Vercel Edge Functions](https://vercel.com/docs/concepts/functions/edge-functions)
|
|
168
|
-
- [Cloudflare Workers](https://workers.cloudflare.com/)
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## 🤝 Contributing
|
|
173
|
-
|
|
174
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
175
|
-
|
|
176
|
-
1. Fork the Project
|
|
177
|
-
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
178
|
-
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
|
179
|
-
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
|
180
|
-
5. Open a Pull Request
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## 📄 License
|
|
185
|
-
|
|
186
|
-
Distributed under the MIT License. See `LICENSE` for more information.
|
|
125
|
+
This library is exclusively optimized for **[Contentstack Launch](https://www.contentstack.com/docs/developers/launch)**.
|