@aryanbansal-launch/edge-utils 0.1.3 → 0.1.5

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.
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+
6
+ const functionsDir = path.join(process.cwd(), 'functions');
7
+ const edgeFile = path.join(functionsDir, '[proxy].edge.js');
8
+
9
+ // Simple ANSI colors for better terminal output
10
+ const colors = {
11
+ reset: '\x1b[0m',
12
+ bright: '\x1b[1m',
13
+ green: '\x1b[32m',
14
+ yellow: '\x1b[33m',
15
+ cyan: '\x1b[36m',
16
+ blue: '\x1b[34m'
17
+ };
18
+
19
+ const template = `
20
+ import {
21
+ jsonResponse,
22
+ passThrough,
23
+ redirectIfMatch,
24
+ protectWithBasicAuth,
25
+ ipAccessControl,
26
+ blockAICrawlers,
27
+ getGeoHeaders,
28
+ handleNextJS_RSC
29
+ } from "@aryanbansal-launch/edge-utils";
30
+
31
+ /**
32
+ * Default Edge Handler for Contentstack Launch
33
+ * This file was automatically generated by @aryanbansal-launch/edge-utils
34
+ */
35
+ export default async function handler(request, context) {
36
+ // 1. ⚛️ Fix Next.js RSC issues for specific paths
37
+ const rscResponse = await handleNextJS_RSC(request, {
38
+ affectedPaths: ["/my-rsc-page", "/another-page"]
39
+ });
40
+ if (rscResponse) return rscResponse;
41
+
42
+ // 2. 🛡️ Block AI bots immediately
43
+ const botResponse = blockAICrawlers(request);
44
+ if (botResponse) return botResponse;
45
+
46
+ // 3. 🧱 IP Whitelisting
47
+ const ipResponse = ipAccessControl(request, { allow: ["203.0.113.10"] });
48
+ if (ipResponse) return ipResponse;
49
+
50
+ // 4. 🔐 Domain-specific Basic Auth
51
+ const authResponse = await protectWithBasicAuth(request, {
52
+ hostnameIncludes: "staging.myapp.com",
53
+ username: "admin",
54
+ password: "securepassword123"
55
+ });
56
+ if (authResponse && authResponse.status === 401) return authResponse;
57
+
58
+ // 5. 🔀 SEO-friendly Redirects
59
+ const redirectResponse = redirectIfMatch(request, {
60
+ path: "/legacy-url",
61
+ to: "/modern-url",
62
+ status: 301
63
+ });
64
+ if (redirectResponse) return redirectResponse;
65
+
66
+ // 6. 📍 Geo-Location Access
67
+ const geo = getGeoHeaders(request);
68
+ if (geo.country === "US") {
69
+ console.log(\`User from \${geo.city}, \${geo.region}\`);
70
+ }
71
+
72
+ // 7. 📤 Custom JSON Responses
73
+ if (new URL(request.url).pathname === "/api/health") {
74
+ return jsonResponse({ status: "healthy", region: geo.region });
75
+ }
76
+
77
+ // 8. 🚀 Pass through to origin
78
+ return passThrough(request);
79
+ }
80
+ `.trim();
81
+
82
+ async function init() {
83
+ console.log(`\n${colors.bright}${colors.cyan}🚀 Edge Utilities: Contentstack Launch Setup${colors.reset}\n`);
84
+
85
+ let actionsTaken = 0;
86
+
87
+ // 1. Root level check: Look for package.json
88
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
89
+ if (!fs.existsSync(packageJsonPath)) {
90
+ console.log(`${colors.red}❌ Error: Root directory not detected.${colors.reset}`);
91
+ console.log(`${colors.yellow}Contentstack Launch requires the 'functions' folder to be at your project root${colors.reset}`);
92
+ console.log(`${colors.yellow}(the same directory as your package.json).${colors.reset}\n`);
93
+ console.log(`Please ${colors.bright}cd${colors.reset} into your project root and try again.`);
94
+ process.exit(1);
95
+ }
96
+
97
+ try {
98
+ // 2. Folder existence check
99
+ if (!fs.existsSync(functionsDir)) {
100
+ fs.mkdirSync(functionsDir, { recursive: true });
101
+ console.log(`${colors.green}✨ New:${colors.reset} Created /functions directory`);
102
+ actionsTaken++;
103
+ } else {
104
+ console.log(`${colors.blue}ℹ️ Existing:${colors.reset} /functions directory already found`);
105
+ }
106
+
107
+ // 3. File existence check (don't overwrite user's work)
108
+ if (!fs.existsSync(edgeFile)) {
109
+ fs.writeFileSync(edgeFile, template + '\n');
110
+ console.log(`${colors.green}✨ New:${colors.reset} Created /functions/[proxy].edge.js`);
111
+ actionsTaken++;
112
+ } else {
113
+ console.log(`${colors.blue}ℹ️ Existing:${colors.reset} /functions/[proxy].edge.js already found`);
114
+ }
115
+
116
+ // Final Summary Messages based on the state
117
+ if (actionsTaken === 0) {
118
+ console.log(`\n${colors.bright}${colors.blue}🏁 Everything is already set up!${colors.reset}`);
119
+ console.log(`No changes were made to your existing files.`);
120
+ } else {
121
+ console.log(`\n${colors.bright}${colors.green}🎉 Setup Complete!${colors.reset}`);
122
+ console.log(`Successfully prepared your edge environment.`);
123
+ }
124
+
125
+ console.log(`\n${colors.bright}Next Steps:${colors.reset}`);
126
+ console.log(`1. Open ${colors.cyan}functions/[proxy].edge.js${colors.reset}`);
127
+ console.log(`2. Customize your redirects, auth, and RSC paths`);
128
+ console.log(`3. Deploy your project to Contentstack Launch\n`);
129
+
130
+ console.log(`${colors.blue}Documentation:${colors.reset} https://github.com/AryanBansal-launch/launch-edge-utils#readme\n`);
131
+ } catch (error) {
132
+ console.error(`\n${colors.red}❌ Error during setup:${colors.reset}`, error.message);
133
+ process.exit(1);
134
+ }
135
+ }
136
+
137
+ init();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aryanbansal-launch/edge-utils",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -12,11 +12,15 @@
12
12
  "url": "https://github.com/AryanBansal-launch/launch-edge-utils/issues"
13
13
  },
14
14
  "main": "dist/index.js",
15
+ "bin": {
16
+ "launch-init": "./bin/launch-init.js"
17
+ },
15
18
  "exports": {
16
19
  ".": "./dist/index.js"
17
20
  },
18
21
  "files": [
19
- "dist"
22
+ "dist",
23
+ "bin"
20
24
  ],
21
25
  "scripts": {
22
26
  "build": "tsc"
package/readme.md CHANGED
@@ -1,10 +1,9 @@
1
- # 🚀 Edge Utils
1
+ # 🚀 Edge Utils for Contentstack Launch
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@launch/edge-utils.svg)](https://www.npmjs.com/package/@launch/edge-utils)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Platform Support](https://img.shields.io/badge/Platform-Edge-blueviolet.svg)](#platform-support)
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
+ 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,25 +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 edge proxies.
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
- ## 📦 Installation
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
 
30
+ ### 2. Initialize
31
+ Run this command from your **project root**:
32
+ ```bash
33
+ npx launch-init
34
+ ```
35
+ This will automatically create the `functions/` directory and a boilerplate `[proxy].edge.js` handler for you.
36
+
28
37
  ---
29
38
 
30
39
  ## 🛠️ Usage Example
31
40
 
32
- Transform your edge handler into a powerful middleware chain:
41
+ Once initialized, your `functions/[proxy].edge.js` will look like a powerful middleware chain:
33
42
 
34
- ```typescript
43
+ ```javascript
35
44
  import {
36
45
  jsonResponse,
37
46
  passThrough,
@@ -41,12 +50,12 @@ import {
41
50
  blockAICrawlers,
42
51
  getGeoHeaders,
43
52
  handleNextJS_RSC
44
- } from "@launch/edge-utils";
53
+ } from "@aryanbansal-launch/edge-utils";
45
54
 
46
- export default async function handler(request: Request) {
55
+ export default async function handler(request, context) {
47
56
  // 1. ⚛️ Fix Next.js RSC issues for specific paths
48
57
  const rscResponse = await handleNextJS_RSC(request, {
49
- affectedPaths: ["/my-rsc-page", "/another-page"]
58
+ affectedPaths: ["/shop", "/about"]
50
59
  });
51
60
  if (rscResponse) return rscResponse;
52
61
 
@@ -54,11 +63,11 @@ export default async function handler(request: Request) {
54
63
  const botResponse = blockAICrawlers(request);
55
64
  if (botResponse) return botResponse;
56
65
 
57
- // 2. 🧱 IP Whitelisting
66
+ // 3. 🧱 IP Whitelisting
58
67
  const ipResponse = ipAccessControl(request, { allow: ["203.0.113.10"] });
59
68
  if (ipResponse) return ipResponse;
60
69
 
61
- // 3. 🔐 Domain-specific Basic Auth
70
+ // 4. 🔐 Domain-specific Basic Auth (e.g., for staging)
62
71
  const authResponse = await protectWithBasicAuth(request, {
63
72
  hostnameIncludes: "staging.myapp.com",
64
73
  username: "admin",
@@ -66,7 +75,7 @@ export default async function handler(request: Request) {
66
75
  });
67
76
  if (authResponse && authResponse.status === 401) return authResponse;
68
77
 
69
- // 4. 🔀 SEO-friendly Redirects
78
+ // 5. 🔀 SEO-friendly Redirects
70
79
  const redirectResponse = redirectIfMatch(request, {
71
80
  path: "/legacy-url",
72
81
  to: "/modern-url",
@@ -74,16 +83,9 @@ export default async function handler(request: Request) {
74
83
  });
75
84
  if (redirectResponse) return redirectResponse;
76
85
 
77
- // 5. 📍 Geo-Location Access
86
+ // 6. 📍 Geo-Location Access
78
87
  const geo = getGeoHeaders(request);
79
- if (geo.country === "US") {
80
- console.log(`User from ${geo.city}, ${geo.region}`);
81
- }
82
-
83
- // 6. 📤 Custom JSON Responses
84
- if (new URL(request.url).pathname === "/api/health") {
85
- return jsonResponse({ status: "healthy", region: geo.region });
86
- }
88
+ console.log(`Request from ${geo.city}, ${geo.country}`);
87
89
 
88
90
  // 7. 🚀 Pass through to origin
89
91
  return passThrough(request);
@@ -95,78 +97,23 @@ export default async function handler(request: Request) {
95
97
  ## 📖 API Reference
96
98
 
97
99
  ### 🛡️ 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`.
100
+ - **`blockAICrawlers(request, bots?)`**: Blocks common AI crawlers.
101
+ - **`ipAccessControl(request, { allow?, deny? })`**: Simple IP-based firewall.
110
102
 
111
103
  ### 🔐 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`.
104
+ - **`protectWithBasicAuth(request, options)`**: Prompt for credentials based on hostname.
121
105
 
122
106
  ### 🔀 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`.
107
+ - **`redirectIfMatch(request, options)`**: Perform SEO-friendly redirects at the edge.
132
108
 
133
109
  ### 📍 Geo Location
134
-
135
- #### `getGeoHeaders`
136
- Extracts geo-information provided by edge platform headers.
137
- - **Returns**: Object with `country`, `region`, `city`, `latitude`, `longitude`.
110
+ - **`getGeoHeaders(request)`**: Returns an object with `country`, `region`, `city`, `latitude`, `longitude`.
138
111
 
139
112
  ### ⚛️ Next.js
140
-
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`.
113
+ - **`handleNextJS_RSC(request, { affectedPaths })`**: Resolves RSC header issues on Contentstack Launch.
146
114
 
147
115
  ---
148
116
 
149
117
  ## 🌐 Platform Support
150
118
 
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/)
155
-
156
- ---
157
-
158
- ## 🤝 Contributing
159
-
160
- Contributions are welcome! Please feel free to submit a Pull Request.
161
-
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
167
-
168
- ---
169
-
170
- ## 📄 License
171
-
172
- Distributed under the MIT License. See `LICENSE` for more information.
119
+ This library is exclusively optimized for **[Contentstack Launch](https://www.contentstack.com/docs/developers/launch)**.