@aryanbansal-launch/edge-utils 0.1.5 β 0.1.7
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-config.js +123 -0
- package/bin/launch-init.js +6 -1
- package/dist/index.js +2 -0
- package/dist/launch/config.js +14 -0
- package/dist/security/block-default-domains.js +11 -0
- package/package.json +3 -2
- package/readme.md +15 -2
|
@@ -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/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
|
});
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,7 @@ 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";
|
|
9
|
+
export * from "./launch/config.js";
|
|
8
10
|
export * from "./nextjs/rsc.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a standard launch.json configuration object.
|
|
3
|
+
*/
|
|
4
|
+
export function generateLaunchConfig(options) {
|
|
5
|
+
return {
|
|
6
|
+
redirects: options.redirects || [],
|
|
7
|
+
rewrites: options.rewrites || [],
|
|
8
|
+
cache: {
|
|
9
|
+
cachePriming: {
|
|
10
|
+
urls: options.cache?.cachePriming?.urls || []
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function blockDefaultDomains(request, options = {}) {
|
|
2
|
+
const domain = options.domainToBlock || 'contentstackapps.com';
|
|
3
|
+
const url = new URL(request.url);
|
|
4
|
+
if (url.hostname.includes(domain)) {
|
|
5
|
+
return new Response('Forbidden: Access via default domain is restricted.', {
|
|
6
|
+
status: 403,
|
|
7
|
+
statusText: 'Forbidden'
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aryanbansal-launch/edge-utils",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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
|
-
[](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
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, Rewrites, and Cache Priming) interactively:
|
|
40
|
+
```bash
|
|
41
|
+
npx launch-config
|
|
42
|
+
```
|
|
43
|
+
|
|
37
44
|
---
|
|
38
45
|
|
|
39
46
|
## π οΈ Usage Example
|
|
@@ -48,12 +55,17 @@ import {
|
|
|
48
55
|
protectWithBasicAuth,
|
|
49
56
|
ipAccessControl,
|
|
50
57
|
blockAICrawlers,
|
|
58
|
+
blockDefaultDomains,
|
|
51
59
|
getGeoHeaders,
|
|
52
60
|
handleNextJS_RSC
|
|
53
61
|
} from "@aryanbansal-launch/edge-utils";
|
|
54
62
|
|
|
55
63
|
export default async function handler(request, context) {
|
|
56
|
-
// 1.
|
|
64
|
+
// 1. π‘οΈ Block access via default Launch domains
|
|
65
|
+
const defaultDomainResponse = blockDefaultDomains(request);
|
|
66
|
+
if (defaultDomainResponse) return defaultDomainResponse;
|
|
67
|
+
|
|
68
|
+
// 2. βοΈ Fix Next.js RSC issues for specific paths
|
|
57
69
|
const rscResponse = await handleNextJS_RSC(request, {
|
|
58
70
|
affectedPaths: ["/shop", "/about"]
|
|
59
71
|
});
|
|
@@ -98,6 +110,7 @@ export default async function handler(request, context) {
|
|
|
98
110
|
|
|
99
111
|
### π‘οΈ Security
|
|
100
112
|
- **`blockAICrawlers(request, bots?)`**: Blocks common AI crawlers.
|
|
113
|
+
- **`blockDefaultDomains(request, { domainToBlock? })`**: Blocks access via default Contentstack Launch domains (e.g., `contentstackapps.com`).
|
|
101
114
|
- **`ipAccessControl(request, { allow?, deny? })`**: Simple IP-based firewall.
|
|
102
115
|
|
|
103
116
|
### π Authentication
|