@aryanbansal-launch/edge-utils 0.1.6 β†’ 0.1.8

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,123 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import readline from 'readline';
6
+
7
+ const colors = {
8
+ reset: '\x1b[0m',
9
+ bright: '\x1b[1m',
10
+ green: '\x1b[32m',
11
+ yellow: '\x1b[33m',
12
+ cyan: '\x1b[36m',
13
+ blue: '\x1b[34m',
14
+ red: '\x1b[31m'
15
+ };
16
+
17
+ const rl = readline.createInterface({
18
+ input: process.stdin,
19
+ output: process.stdout
20
+ });
21
+
22
+ const question = (query) =>
23
+ new Promise((resolve) => rl.question(query, resolve));
24
+
25
+ async function run() {
26
+ console.log(`\n${colors.bright}${colors.cyan}πŸš€ Launch Configuration Generator${colors.reset}\n`);
27
+
28
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
29
+ if (!fs.existsSync(packageJsonPath)) {
30
+ console.log(`${colors.red}❌ Error: Root directory not detected.${colors.reset}`);
31
+ process.exit(1);
32
+ }
33
+
34
+ const launchJsonPath = path.join(process.cwd(), 'launch.json');
35
+ let config = {
36
+ redirects: [],
37
+ rewrites: [],
38
+ cache: {
39
+ cachePriming: {
40
+ urls: []
41
+ }
42
+ }
43
+ };
44
+
45
+ if (fs.existsSync(launchJsonPath)) {
46
+ console.log(`${colors.yellow}ℹ️ Existing launch.json found. We will merge your changes.${colors.reset}\n`);
47
+ try {
48
+ const existingConfig = JSON.parse(fs.readFileSync(launchJsonPath, 'utf-8'));
49
+
50
+ // Preserve everything, but ensure our managed fields are initialized as arrays/objects
51
+ config = {
52
+ ...existingConfig,
53
+ redirects: existingConfig.redirects || [],
54
+ rewrites: existingConfig.rewrites || [],
55
+ cache: {
56
+ ...(existingConfig.cache || {}),
57
+ cachePriming: {
58
+ ...(existingConfig.cache?.cachePriming || {}),
59
+ urls: existingConfig.cache?.cachePriming?.urls || []
60
+ }
61
+ }
62
+ };
63
+ } catch (e) {
64
+ console.log(`${colors.red}⚠️ Error parsing existing launch.json. Starting with a fresh config.${colors.reset}\n`);
65
+ }
66
+ }
67
+
68
+ // 1. Redirects
69
+ while (true) {
70
+ const addRedirect = await question(`Do you want to add a Redirect?${config.redirects.length > 0 ? ' another?' : ''} (y/n): `);
71
+ if (addRedirect.toLowerCase() !== 'y') break;
72
+
73
+ const source = await question(' Source path (e.g., /source): ');
74
+ const destination = await question(' Destination path (e.g., /destination): ');
75
+ const code = await question(' Status code (default 308): ');
76
+ config.redirects.push({
77
+ source,
78
+ destination,
79
+ statusCode: parseInt(code) || 308
80
+ });
81
+ console.log(`${colors.green} βœ” Redirect added.${colors.reset}`);
82
+ }
83
+
84
+ // 2. Rewrites
85
+ while (true) {
86
+ const addRewrite = await question(`Do you want to add a Rewrite?${config.rewrites.length > 0 ? ' another?' : ''} (y/n): `);
87
+ if (addRewrite.toLowerCase() !== 'y') break;
88
+
89
+ const source = await question(' Source path (e.g., /api/*): ');
90
+ const destination = await question(' Destination URL: ');
91
+ config.rewrites.push({ source, destination });
92
+ console.log(`${colors.green} βœ” Rewrite added.${colors.reset}`);
93
+ }
94
+
95
+ // 3. Cache Priming
96
+ const addPrime = await question('Do you want to add Cache Priming URLs? (y/n): ');
97
+ if (addPrime.toLowerCase() === 'y') {
98
+ console.log(`${colors.cyan}Note: Only relative paths are supported. No Regex/Wildcards.${colors.reset}`);
99
+ const urls = await question('Enter URLs separated by commas (e.g., /home,/about,/shop): ');
100
+ const urlList = urls.split(',').map(u => u.trim()).filter(u => u);
101
+ config.cache.cachePriming.urls = [...new Set([...(config.cache.cachePriming.urls || []), ...urlList])];
102
+ }
103
+
104
+ // Clean up empty fields before writing
105
+ if (config.redirects && config.redirects.length === 0) delete config.redirects;
106
+ if (config.rewrites && config.rewrites.length === 0) delete config.rewrites;
107
+
108
+ if (config.cache) {
109
+ if (config.cache.cachePriming && config.cache.cachePriming.urls && config.cache.cachePriming.urls.length === 0) {
110
+ delete config.cache.cachePriming;
111
+ }
112
+ if (Object.keys(config.cache).length === 0) {
113
+ delete config.cache;
114
+ }
115
+ }
116
+
117
+ fs.writeFileSync(launchJsonPath, JSON.stringify(config, null, 2));
118
+ console.log(`\n${colors.green}βœ… Successfully updated launch.json!${colors.reset}\n`);
119
+
120
+ rl.close();
121
+ }
122
+
123
+ run();
package/dist/index.js CHANGED
@@ -6,4 +6,5 @@ export * from "./security/ip-access.js";
6
6
  export * from "./security/block-bots.js";
7
7
  export * from "./security/block-default-domains.js";
8
8
  export * from "./geo/geo-headers.js";
9
+ export * from "./launch/config.js";
9
10
  export * from "./nextjs/rsc.js";
@@ -0,0 +1,11 @@
1
+ export function generateLaunchConfig(options) {
2
+ return {
3
+ redirects: options.redirects || [],
4
+ rewrites: options.rewrites || [],
5
+ cache: {
6
+ cachePriming: {
7
+ urls: options.cache?.cachePriming?.urls || []
8
+ }
9
+ }
10
+ };
11
+ }
@@ -1,7 +1,3 @@
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
1
  export function blockDefaultDomains(request, options = {}) {
6
2
  const domain = options.domainToBlock || 'contentstackapps.com';
7
3
  const url = new URL(request.url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aryanbansal-launch/edge-utils",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -13,7 +13,8 @@
13
13
  },
14
14
  "main": "dist/index.js",
15
15
  "bin": {
16
- "launch-init": "./bin/launch-init.js"
16
+ "launch-init": "./bin/launch-init.js",
17
+ "launch-config": "./bin/launch-config.js"
17
18
  },
18
19
  "exports": {
19
20
  ".": "./dist/index.js"
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # πŸš€ Edge Utils for Contentstack Launch
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@launch/edge-utils.svg)](https://www.npmjs.com/package/@aryanbansal-launch/edge-utils)
3
+ [![npm version](https://img.shields.io/npm/v/@aryanbansal-launch/edge-utils.svg)](https://www.npmjs.com/package/@aryanbansal-launch/edge-utils)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
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.
@@ -13,6 +13,7 @@ A lightweight, high-performance toolkit specifically designed for **Contentstack
13
13
  - πŸ” **Edge Auth**: Implement Basic Auth directly at the edge for specific hostnames.
14
14
  - πŸ“ **Geo-Aware**: Easily extract location data from request headers.
15
15
  - βš›οΈ **Next.js Ready**: Built-in fixes for RSC header issues on Launch proxies.
16
+ - πŸš€ **Cache Priming**: Easily manage cache warming URLs via CLI.
16
17
  - πŸ”€ **Smart Routing**: Declarative redirects based on path and method.
17
18
  - ⚑ **Zero Dependencies**: Lightweight and optimized for edge runtime limits.
18
19
 
@@ -34,6 +35,12 @@ npx launch-init
34
35
  ```
35
36
  This will automatically create the `functions/` directory and a boilerplate `[proxy].edge.js` handler for you.
36
37
 
38
+ ### 3. Configure (Optional)
39
+ Manage your `launch.json` ([Redirects](https://www.contentstack.com/docs/developers/launch/edge-url-redirects), [Rewrites](https://www.contentstack.com/docs/developers/launch/edge-url-rewrites), and [Cache Priming](https://www.contentstack.com/docs/developers/launch/cache-priming)) interactively:
40
+ ```bash
41
+ npx launch-config
42
+ ```
43
+
37
44
  ---
38
45
 
39
46
  ## πŸ› οΈ Usage Example
@@ -64,15 +71,15 @@ export default async function handler(request, context) {
64
71
  });
65
72
  if (rscResponse) return rscResponse;
66
73
 
67
- // 2. πŸ›‘οΈ Block AI bots immediately
74
+ // 3. πŸ€– Block AI bots immediately
68
75
  const botResponse = blockAICrawlers(request);
69
76
  if (botResponse) return botResponse;
70
77
 
71
- // 3. 🧱 IP Whitelisting
78
+ // 4. 🧱 IP Whitelisting
72
79
  const ipResponse = ipAccessControl(request, { allow: ["203.0.113.10"] });
73
80
  if (ipResponse) return ipResponse;
74
81
 
75
- // 4. πŸ” Domain-specific Basic Auth (e.g., for staging)
82
+ // 5. πŸ” Domain-specific Basic Auth (e.g., for staging)
76
83
  const authResponse = await protectWithBasicAuth(request, {
77
84
  hostnameIncludes: "staging.myapp.com",
78
85
  username: "admin",
@@ -80,7 +87,7 @@ export default async function handler(request, context) {
80
87
  });
81
88
  if (authResponse && authResponse.status === 401) return authResponse;
82
89
 
83
- // 5. πŸ”€ SEO-friendly Redirects
90
+ // 6. πŸ”€ SEO-friendly Redirects
84
91
  const redirectResponse = redirectIfMatch(request, {
85
92
  path: "/legacy-url",
86
93
  to: "/modern-url",
@@ -88,11 +95,11 @@ export default async function handler(request, context) {
88
95
  });
89
96
  if (redirectResponse) return redirectResponse;
90
97
 
91
- // 6. πŸ“ Geo-Location Access
98
+ // 7. πŸ“ Geo-Location Access
92
99
  const geo = getGeoHeaders(request);
93
100
  console.log(`Request from ${geo.city}, ${geo.country}`);
94
101
 
95
- // 7. πŸš€ Pass through to origin
102
+ // 8. πŸš€ Pass through to origin
96
103
  return passThrough(request);
97
104
  }
98
105
  ```
@@ -102,9 +109,9 @@ export default async function handler(request, context) {
102
109
  ## πŸ“– API Reference
103
110
 
104
111
  ### πŸ›‘οΈ Security
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.
112
+ - **`blockAICrawlers(request, bots?)`**: Detects and blocks known AI crawlers (GPTBot, ClaudeBot, etc.) based on the User-Agent.
113
+ - **`blockDefaultDomains(request, { domainToBlock? })`**: Prevents users from accessing your site via the default `*.contentstackapps.com` domains, forcing them to use your custom domain.
114
+ - **`ipAccessControl(request, { allow?, deny? })`**: A simple firewall to whitelist or blacklist specific IP addresses at the edge.
108
115
 
109
116
  ### πŸ” Authentication
110
117
  - **`protectWithBasicAuth(request, options)`**: Prompt for credentials based on hostname.
@@ -123,3 +130,9 @@ export default async function handler(request, context) {
123
130
  ## 🌐 Platform Support
124
131
 
125
132
  This library is exclusively optimized for **[Contentstack Launch](https://www.contentstack.com/docs/developers/launch)**.
133
+
134
+ ---
135
+
136
+ ## πŸ“„ License
137
+
138
+ Distributed under the MIT License. See `LICENSE` for more information.