edge-functions 2.5.0 → 2.6.0-stage.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/CHANGELOG.md +14 -0
- package/README.md +11 -7
- package/docs/nextjs.md +66 -0
- package/docs/overview.md +8 -1
- package/docs/presets.md +2 -0
- package/lib/build/bundlers/polyfills/polyfills-manager.js +5 -1
- package/lib/env/polyfills/azion/network-list/context/index.js +3 -0
- package/lib/env/polyfills/azion/network-list/context/network-list.context.js +232 -0
- package/lib/env/polyfills/azion/network-list/context/network-list.context.test.js +44 -0
- package/lib/env/polyfills/azion/network-list/index.js +3 -0
- package/lib/env/polyfills/azion/network-list/network-list.polyfills.js +16 -0
- package/lib/env/polyfills/index.js +2 -0
- package/lib/env/runtime.env.js +4 -0
- package/lib/presets/custom/next/deliver/prebuild.js +14 -41
- package/lib/presets/custom/next/utils.next.js +66 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [2.6.0-stage.2](https://github.com/aziontech/vulcan/compare/v2.6.0-stage.1...v2.6.0-stage.2) (2024-03-06)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* nextjs config file read ([#261](https://github.com/aziontech/vulcan/issues/261)) ([39dbe3e](https://github.com/aziontech/vulcan/commit/39dbe3e840aba42802c442b240ad35551261d2db))
|
|
7
|
+
|
|
8
|
+
## [2.6.0-stage.1](https://github.com/aziontech/vulcan/compare/v2.5.0...v2.6.0-stage.1) (2024-03-05)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add support to network list api ([#260](https://github.com/aziontech/vulcan/issues/260)) ([79c20be](https://github.com/aziontech/vulcan/commit/79c20be283c32829a9eb2a1db2b0996bb3246d83))
|
|
14
|
+
|
|
1
15
|
## [2.5.0](https://github.com/aziontech/vulcan/compare/v2.4.0...v2.5.0) (2024-02-28)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ Here's a detailed breakdown of the configuration properties available in `vulcan
|
|
|
91
91
|
|
|
92
92
|
**Type:** String
|
|
93
93
|
|
|
94
|
-
**Description:**
|
|
94
|
+
**Description:**
|
|
95
95
|
This represents the primary entry point for your application, where the building process begins.
|
|
96
96
|
|
|
97
97
|
**Note:** `Entry` will be ignored for jamstack solutions.
|
|
@@ -100,28 +100,28 @@ This represents the primary entry point for your application, where the building
|
|
|
100
100
|
|
|
101
101
|
**Type:** String ('esbuild' or 'webpack')
|
|
102
102
|
|
|
103
|
-
**Description:**
|
|
103
|
+
**Description:**
|
|
104
104
|
Defines which build tool to use. The available options are `esbuild` and `webpack`.
|
|
105
105
|
|
|
106
106
|
### UseNodePolyfills
|
|
107
107
|
|
|
108
108
|
**Type:** Boolean
|
|
109
109
|
|
|
110
|
-
**Description:**
|
|
110
|
+
**Description:**
|
|
111
111
|
Determines whether Node.js polyfills should be applied. This is useful for projects that leverage specific Node.js functionality but target environments without these built-in features. The use of useNodePolyfills is ignored when used in mode `deliver` presets, as Node.js features must be resolved at build time by the framework process itself.
|
|
112
112
|
|
|
113
113
|
### UseOwnWorker
|
|
114
114
|
|
|
115
115
|
**Type:** Boolean
|
|
116
116
|
|
|
117
|
-
**Description:**
|
|
117
|
+
**Description:**
|
|
118
118
|
This flag indicates that the constructed code inserts its own worker expression, such as `addEventListener("fetch")` or similar, without the need to inject a provider.
|
|
119
119
|
|
|
120
120
|
### Preset
|
|
121
121
|
|
|
122
122
|
**Type:** Object
|
|
123
123
|
|
|
124
|
-
**Description:**
|
|
124
|
+
**Description:**
|
|
125
125
|
Provides preset-specific configurations.
|
|
126
126
|
|
|
127
127
|
- **Name (Type: String):** Refers to the preset name, e.g., "vue" or "next".
|
|
@@ -131,7 +131,7 @@ Provides preset-specific configurations.
|
|
|
131
131
|
|
|
132
132
|
**Type:** Object
|
|
133
133
|
|
|
134
|
-
**Description:**
|
|
134
|
+
**Description:**
|
|
135
135
|
Configurations related to the in-memory filesystem.
|
|
136
136
|
|
|
137
137
|
- **InjectionDirs (Type: Array of Strings):** Directories to be injected into memory for runtime access via the fs API.
|
|
@@ -142,7 +142,7 @@ Configurations related to the in-memory filesystem.
|
|
|
142
142
|
|
|
143
143
|
**Type:** Object
|
|
144
144
|
|
|
145
|
-
**Description:**
|
|
145
|
+
**Description:**
|
|
146
146
|
Allows you to extend the capabilities of the chosen bundler (either `webpack` or `esbuild`) with custom plugins or configurations.
|
|
147
147
|
|
|
148
148
|
- **Plugins (Type: Object):** Add your custom plugins for your chosen bundler here.
|
|
@@ -177,8 +177,12 @@ module.exports = {
|
|
|
177
177
|
|
|
178
178
|
- [Overview](docs/overview.md)
|
|
179
179
|
- [Presets](docs/presets.md)
|
|
180
|
+
- [Nextjs](docs/nextjs.md)
|
|
180
181
|
- [Rust/Wasm example](examples/rust-wasm-yew-ssr/)
|
|
181
182
|
- [Emscripten/Wasm example](examples/emscripten-wasm/)
|
|
183
|
+
- [Env vars example](examples/simple-js-env-vars)
|
|
184
|
+
- [Storage example](examples/simple-js-esm-storage)
|
|
185
|
+
- [Firewall example](examples/simple-js-firewall-event)
|
|
182
186
|
|
|
183
187
|
## Wasm Notes
|
|
184
188
|
|
package/docs/nextjs.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Nextjs Support
|
|
2
|
+
|
|
3
|
+
Vulcan supports Nextjs in compute and deliver modes.
|
|
4
|
+
|
|
5
|
+
## Deliver
|
|
6
|
+
|
|
7
|
+
Static site delivered by edge without a function.
|
|
8
|
+
Check static examples in [Nextjs examples dir](/examples//next/) for more details.
|
|
9
|
+
|
|
10
|
+
### References
|
|
11
|
+
|
|
12
|
+
- [Pages Router - Static Export](https://nextjs.org/docs/pages/building-your-application/deploying/static-exports)
|
|
13
|
+
- [App Router - Static Export](https://nextjs.org/docs/app/building-your-application/deploying/static-exports)
|
|
14
|
+
|
|
15
|
+
## Compute
|
|
16
|
+
|
|
17
|
+
In compute mode the nextjs handler uses a routing system (based on vercel multiple steps routing) to handle the request.
|
|
18
|
+
|
|
19
|
+
After a route match in the routing system takes one of these actions:
|
|
20
|
+
|
|
21
|
+
- deliver a static;
|
|
22
|
+
- make a request override;
|
|
23
|
+
- call a builded edge module;
|
|
24
|
+
- call a node custom server;
|
|
25
|
+
|
|
26
|
+
This solution was created based on fastly ([next-compute-js v1](https://github.com/fastly/next-compute-js)) and cloudflare ([next-on-pages](https://github.com/cloudflare/next-on-pages)) Nextjs solutions.
|
|
27
|
+
|
|
28
|
+
Check edge or node examples in [Nextjs examples dir](/examples//next/) for more details.
|
|
29
|
+
|
|
30
|
+
### Supported Features
|
|
31
|
+
|
|
32
|
+
| Runtime | Versions | Format/Router | Feature |
|
|
33
|
+
| ------- | ---------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
34
|
+
| Edge | 12.2.x, 12.3.x | Pages Router | Static Pages |
|
|
35
|
+
| | | | SSR |
|
|
36
|
+
| | | | SSG |
|
|
37
|
+
| | | | Edge API Routes |
|
|
38
|
+
| | | | Dynamic Routes |
|
|
39
|
+
| | | | Middleware (rewrite, redirect, continue to response, set request header, throw error, set response header, set response cookie) |
|
|
40
|
+
| | | | Next configs (rewrite before files, rewrite after files, rewrite fallback, redirects, header definition) |
|
|
41
|
+
| | | | i18n routing |
|
|
42
|
+
| Edge | 13.0.x, 13.1.x, 13.2.x, 13.3.x, 13.4.x, 13.5.x | Pages Router | Static Pages |
|
|
43
|
+
| | | | SSR |
|
|
44
|
+
| | | | SSG |
|
|
45
|
+
| | | | Edge API Routes |
|
|
46
|
+
| | | | Dynamic Routes |
|
|
47
|
+
| | | | Middleware (rewrite, redirect, continue to response, set request header, throw error, return response, set response header, set response cookie) |
|
|
48
|
+
| | | | Next configs (rewrite before files, rewrite after files, rewrite fallback, redirects, header definition) |
|
|
49
|
+
| | | | i18n routing |
|
|
50
|
+
| | | | Custom Errors |
|
|
51
|
+
| Edge | 13.0.x, 13.1.x, 13.2.x, 13.3.x, 13.4.x, 13.5.x | App Router | App router (basic structure, routing, layouts) |
|
|
52
|
+
| | | | Server Components |
|
|
53
|
+
| | | | Route Handlers |
|
|
54
|
+
| | | | Dynamic Routes |
|
|
55
|
+
| | | | Middleware (rewrite, redirect, continue to response, set request header, throw error, return response, set response header, set response cookie) |
|
|
56
|
+
| | | | Next configs (rewrite before files, rewrite after files, redirects, header definition) |
|
|
57
|
+
| | | | Internationalization |
|
|
58
|
+
| | | | Custom Errors (error.js and not-found.js) |
|
|
59
|
+
| Node | 12.3.x | Pages Router | Static Pages |
|
|
60
|
+
| | | | SSR |
|
|
61
|
+
| | | | SSG |
|
|
62
|
+
| | | | API Routes |
|
|
63
|
+
| | | | Dynamic Routes |
|
|
64
|
+
| | | | Next configs (rewrite before files, rewrite after files, rewrite fallback, redirects, header definition) |
|
|
65
|
+
| | | | i18n routing |
|
|
66
|
+
| | | | Custom Errors |
|
package/docs/overview.md
CHANGED
|
@@ -8,7 +8,8 @@ flowchart LR
|
|
|
8
8
|
A[Trigger] -->|args| B(Dispatcher)
|
|
9
9
|
B --> C(Prebuild)
|
|
10
10
|
C -->|args| D(Common Build)
|
|
11
|
-
D --> E
|
|
11
|
+
D --> E(Postbuild)
|
|
12
|
+
E --> F[Artifacts]
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
### Trigger
|
|
@@ -35,6 +36,10 @@ Polyfills can be used to generate the worker(s) file(s).
|
|
|
35
36
|
|
|
36
37
|
Some configs can be passed to the builder but if user tries to override `azion worker configs` this passed configs will be ignored.
|
|
37
38
|
|
|
39
|
+
### Post Build
|
|
40
|
+
|
|
41
|
+
Optional step to run post build actions after common build (bundlers action).
|
|
42
|
+
|
|
38
43
|
### Artifacts
|
|
39
44
|
|
|
40
45
|
The **'.edge'** folder will be generated representing the edge locally. Files generated to run on the infrastructure:
|
|
@@ -42,3 +47,5 @@ The **'.edge'** folder will be generated representing the edge locally. Files ge
|
|
|
42
47
|
- JS worker(s) => '.edge/workers.js';
|
|
43
48
|
- Assets => '.edge/storage/\*';
|
|
44
49
|
- Environment variables => '.edge/.env'.
|
|
50
|
+
|
|
51
|
+
The **'.vulcan'** file also will be generated. This file contains build infos that can be used by local env or other tools.
|
package/docs/presets.md
CHANGED
|
@@ -37,6 +37,8 @@ Each preset is made up of three primary files: `config.js`, `prebuild.js`, and `
|
|
|
37
37
|
- mountSSG: This function takes the request and sets up routes according to the SSG structure.
|
|
38
38
|
- ErrorHTML: This edgehook provides a return of an HTML template showing the error and the description passed as a parameter. You can pass the captured error as the third parameter, and it will be displayed on the screen (it's a good way to debug).
|
|
39
39
|
|
|
40
|
+
4. `postbuild.js`: this file is optional. Here you can run actions after the common build done by bundlers.
|
|
41
|
+
|
|
40
42
|
# How to add a new preset
|
|
41
43
|
|
|
42
44
|
Here's a step-by-step guide on how to add a new preset in Vulcan:
|
|
@@ -161,9 +161,13 @@ class PolyfillsManager {
|
|
|
161
161
|
|
|
162
162
|
// globalThis.Azion
|
|
163
163
|
this.setExternal(
|
|
164
|
-
'Azion',
|
|
164
|
+
'Azion.env',
|
|
165
165
|
`${externalPolyfillsPath}/azion/env-vars/env-vars.polyfills.js`,
|
|
166
166
|
);
|
|
167
|
+
this.setExternal(
|
|
168
|
+
'Azion.networkList',
|
|
169
|
+
`${externalPolyfillsPath}/azion/network-list/network-list.polyfills.js`,
|
|
170
|
+
);
|
|
167
171
|
|
|
168
172
|
return {
|
|
169
173
|
libs: this.libs,
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import ipLib from 'ip';
|
|
2
|
+
import nodePath from 'node:path';
|
|
3
|
+
import { readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This class is a VM context (NETWORK_LIST_CONTEXT) to handle with network list
|
|
11
|
+
* @class NetworkListContext
|
|
12
|
+
* @description Class to manage the network list
|
|
13
|
+
*/
|
|
14
|
+
class NetworkListContext {
|
|
15
|
+
/**
|
|
16
|
+
* Cache Dynamic Import flag - If true, the file will be reloaded every time default: true
|
|
17
|
+
*/
|
|
18
|
+
#cacheDynamicImport;
|
|
19
|
+
|
|
20
|
+
#networkList = [];
|
|
21
|
+
|
|
22
|
+
#workDir = '.edge';
|
|
23
|
+
|
|
24
|
+
#configFile = 'azion.config.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Creates an instance of NetworkListContext.
|
|
28
|
+
* @param {boolean} [cacheDynamicImport=true] - Cache Dynamic Import flag - If false, the file will be reloaded every time default: true
|
|
29
|
+
*/
|
|
30
|
+
constructor(cacheDynamicImport = true) {
|
|
31
|
+
this.#cacheDynamicImport = cacheDynamicImport;
|
|
32
|
+
this.#init();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if the network list contains the value
|
|
37
|
+
* @param {string} networkListId - The network list id
|
|
38
|
+
* @param {string} value - The value to check
|
|
39
|
+
* @returns {boolean} - Return true if the network list contains the value
|
|
40
|
+
* @memberof NetworkListContext
|
|
41
|
+
*/
|
|
42
|
+
contains(networkListId, value) {
|
|
43
|
+
const network = this.#networkList.find(
|
|
44
|
+
(networkItem) =>
|
|
45
|
+
parseInt(networkItem.id, 10) === parseInt(networkListId, 10),
|
|
46
|
+
);
|
|
47
|
+
return this.#containsType(network, value);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#containsType(network, value) {
|
|
51
|
+
switch (network?.listType) {
|
|
52
|
+
case 'ip_cidr':
|
|
53
|
+
return this.#networkCIDR(value, network);
|
|
54
|
+
case 'asn':
|
|
55
|
+
return this.#networkAsn(value, network);
|
|
56
|
+
case 'countries':
|
|
57
|
+
return this.#networkCountries(value, network);
|
|
58
|
+
default:
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// eslint-disable-next-line class-methods-use-this
|
|
64
|
+
#networkCIDR(ipAddress, network) {
|
|
65
|
+
const listContent = network?.listContent;
|
|
66
|
+
if (!listContent || listContent.length === 0) return false;
|
|
67
|
+
return listContent.some((currentIp) => {
|
|
68
|
+
if (currentIp.includes('/')) {
|
|
69
|
+
return ipLib.cidrSubnet(currentIp).contains(ipAddress);
|
|
70
|
+
}
|
|
71
|
+
return currentIp === ipAddress;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// eslint-disable-next-line class-methods-use-this
|
|
76
|
+
#networkAsn(asn, network) {
|
|
77
|
+
const listContent = network?.listContent;
|
|
78
|
+
if (!listContent || listContent.length === 0) return false;
|
|
79
|
+
return listContent.some((currentAsn) => {
|
|
80
|
+
return parseInt(currentAsn, 10) === parseInt(asn, 10);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// eslint-disable-next-line class-methods-use-this
|
|
85
|
+
#networkCountries(country, network) {
|
|
86
|
+
const listContent = network?.listContent;
|
|
87
|
+
if (!listContent || listContent.length === 0) return false;
|
|
88
|
+
return listContent.some((currentCountry) => {
|
|
89
|
+
return currentCountry === country;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line class-methods-use-this
|
|
94
|
+
async #init() {
|
|
95
|
+
try {
|
|
96
|
+
const config = await this.#loadConfigFile();
|
|
97
|
+
this.#networkList = config.networkList;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
this.#networkList = [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// eslint-disable-next-line class-methods-use-this
|
|
104
|
+
async #loadConfigFile() {
|
|
105
|
+
const { configFilePath, rootPath } = this.#getConfigFilePath();
|
|
106
|
+
|
|
107
|
+
const {
|
|
108
|
+
type: typeImport,
|
|
109
|
+
changed,
|
|
110
|
+
currentConfigPath,
|
|
111
|
+
matchPaths,
|
|
112
|
+
} = this.#checkFileImportType(configFilePath);
|
|
113
|
+
|
|
114
|
+
let config;
|
|
115
|
+
if (typeImport === 'esm') {
|
|
116
|
+
config = await this.#importEsmModule(
|
|
117
|
+
rootPath,
|
|
118
|
+
currentConfigPath,
|
|
119
|
+
changed,
|
|
120
|
+
);
|
|
121
|
+
} else {
|
|
122
|
+
config = await this.#importCjsModule(
|
|
123
|
+
rootPath,
|
|
124
|
+
currentConfigPath,
|
|
125
|
+
changed,
|
|
126
|
+
matchPaths,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return config?.default || config;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// eslint-disable-next-line class-methods-use-this
|
|
134
|
+
#getConfigFilePath() {
|
|
135
|
+
const projectRoot = process.cwd();
|
|
136
|
+
const isWindows = process.platform === 'win32';
|
|
137
|
+
const rootPath = isWindows
|
|
138
|
+
? fileURLToPath(new URL(`file:///${nodePath.resolve(projectRoot, '.')}`))
|
|
139
|
+
: nodePath.resolve(projectRoot, '.');
|
|
140
|
+
return {
|
|
141
|
+
configFilePath: nodePath.resolve(rootPath, this.#configFile),
|
|
142
|
+
rootPath,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// eslint-disable-next-line class-methods-use-this
|
|
147
|
+
async #importEsmModule(rootPath, originalConfigPath, changed) {
|
|
148
|
+
let pathCache = originalConfigPath;
|
|
149
|
+
if (!this.#cacheDynamicImport) {
|
|
150
|
+
pathCache = `${originalConfigPath}?u=${Date.now()}`;
|
|
151
|
+
}
|
|
152
|
+
const config = (await import(pathCache)).default;
|
|
153
|
+
if (changed) {
|
|
154
|
+
rmSync(originalConfigPath);
|
|
155
|
+
}
|
|
156
|
+
return config;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// eslint-disable-next-line class-methods-use-this
|
|
160
|
+
async #importCjsModule(rootPath, configFilePath, changed, matchPaths) {
|
|
161
|
+
if (!this.#cacheDynamicImport) {
|
|
162
|
+
delete require.cache[configFilePath];
|
|
163
|
+
if (changed && matchPaths?.length > 0) {
|
|
164
|
+
matchPaths.forEach((match) => {
|
|
165
|
+
delete require.cache[nodePath.resolve(rootPath, match)];
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return new Promise((resolve) => {
|
|
170
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
171
|
+
resolve(require(configFilePath));
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// eslint-disable-next-line class-methods-use-this
|
|
176
|
+
#checkFileImportType(originalConfigPath) {
|
|
177
|
+
const file = readFileSync(originalConfigPath, 'utf8');
|
|
178
|
+
if (file?.includes('export default')) {
|
|
179
|
+
const { changed, currentConfigPath } = this.#changeEsmImports(
|
|
180
|
+
originalConfigPath,
|
|
181
|
+
file,
|
|
182
|
+
);
|
|
183
|
+
return { type: 'esm', changed, currentConfigPath };
|
|
184
|
+
}
|
|
185
|
+
const { changed, matchPaths } = this.#changeCjsImports(file);
|
|
186
|
+
return {
|
|
187
|
+
type: 'cjs',
|
|
188
|
+
changed,
|
|
189
|
+
currentConfigPath: originalConfigPath,
|
|
190
|
+
matchPaths,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// eslint-disable-next-line class-methods-use-this
|
|
195
|
+
#changeEsmImports(originalConfigPath, file) {
|
|
196
|
+
const regex = /import\s+(.*)\s+from\s+['"]\.(.*)['"]/g;
|
|
197
|
+
let changed = false;
|
|
198
|
+
let fileUpdated = file;
|
|
199
|
+
if (file.match(regex)) {
|
|
200
|
+
changed = true;
|
|
201
|
+
fileUpdated = file.replace(
|
|
202
|
+
regex,
|
|
203
|
+
`import $1 from "..$2?u=${Date.now()}"`,
|
|
204
|
+
);
|
|
205
|
+
const tmpFile = this.#configFile.replace('.js', '.temp.js');
|
|
206
|
+
const tmpConfigPath = nodePath.join(
|
|
207
|
+
process.cwd(),
|
|
208
|
+
this.#workDir,
|
|
209
|
+
tmpFile,
|
|
210
|
+
);
|
|
211
|
+
writeFileSync(tmpConfigPath, fileUpdated, 'utf8');
|
|
212
|
+
return { changed, currentConfigPath: tmpConfigPath };
|
|
213
|
+
}
|
|
214
|
+
return { changed, currentConfigPath: originalConfigPath };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// eslint-disable-next-line class-methods-use-this
|
|
218
|
+
#changeCjsImports(file) {
|
|
219
|
+
let changed = false;
|
|
220
|
+
const regex = /require\(['"]([^'"]+)['"]\)/g;
|
|
221
|
+
const matchPaths = [];
|
|
222
|
+
let match = regex.exec(file);
|
|
223
|
+
while (match !== null) {
|
|
224
|
+
changed = true;
|
|
225
|
+
matchPaths.push(match[1]);
|
|
226
|
+
match = regex.exec(file);
|
|
227
|
+
}
|
|
228
|
+
return { changed, matchPaths };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export default NetworkListContext;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { it } from '@jest/globals';
|
|
2
|
+
import mockFs from 'mock-fs';
|
|
3
|
+
import NetworkListContext from './network-list.context.js';
|
|
4
|
+
|
|
5
|
+
describe('Network List Context', () => {
|
|
6
|
+
let networkListContext;
|
|
7
|
+
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
// eslint-disable-next-line jest/no-standalone-expect
|
|
10
|
+
const typeImport = expect.getState().currentTestName.includes('commonjs')
|
|
11
|
+
? 'module.exports ='
|
|
12
|
+
: 'export default';
|
|
13
|
+
const code = `${typeImport} {
|
|
14
|
+
networkList: [
|
|
15
|
+
{ id: 1, listType: "ip_cidr", listContent: ["10.0.0.1"] },
|
|
16
|
+
{ id: 2, listType: "asn", listContent: [123, 456, 789]},
|
|
17
|
+
{ id: 3, listType: "countries", listContent: ["United States", "Brazil"]}
|
|
18
|
+
]};`;
|
|
19
|
+
mockFs({
|
|
20
|
+
'azion.config.js': code,
|
|
21
|
+
});
|
|
22
|
+
networkListContext = new NetworkListContext();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
mockFs.restore();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should contain the valid ip in the network list and list type is ip_cidr', async () => {
|
|
30
|
+
expect(networkListContext.contains(1, '10.0.0.1')).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should contain the valid asn in the network list and list type is asn', async () => {
|
|
34
|
+
expect(networkListContext.contains(2, 123)).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should contain the valid country in the network list and list type is countries', async () => {
|
|
38
|
+
expect(networkListContext.contains(3, 'United States')).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should type import be commonjs and contain the valid ip in the network list and list type is ip_cidr', async () => {
|
|
42
|
+
expect(networkListContext.contains(1, '10.0.0.1')).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
globalThis.Azion = globalThis.Azion || {};
|
|
3
|
+
|
|
4
|
+
globalThis.Azion.networkList = {};
|
|
5
|
+
// unique context for each instance
|
|
6
|
+
const instanceNetworkList = new NETWORK_LIST_CONTEXT(false);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param {string} network_list_id - The network list id
|
|
11
|
+
* @param {string} value - The value to check
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
globalThis.Azion.networkList.contains = (network_list_id, value) => {
|
|
15
|
+
return instanceNetworkList.contains(network_list_id, value);
|
|
16
|
+
};
|
|
@@ -3,6 +3,7 @@ import FetchEventContext from './azion/fetch-event/index.js';
|
|
|
3
3
|
import { AsyncHooksContext } from './async-hooks/index.js';
|
|
4
4
|
import { StorageContext } from './azion/storage/index.js';
|
|
5
5
|
import EnvVarsContext from './azion/env-vars/index.js';
|
|
6
|
+
import NetworkListContext from './azion/network-list/index.js';
|
|
6
7
|
|
|
7
8
|
export {
|
|
8
9
|
fetchContext,
|
|
@@ -10,4 +11,5 @@ export {
|
|
|
10
11
|
AsyncHooksContext,
|
|
11
12
|
StorageContext,
|
|
12
13
|
EnvVarsContext,
|
|
14
|
+
NetworkListContext,
|
|
13
15
|
};
|
package/lib/env/runtime.env.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
AsyncHooksContext,
|
|
7
7
|
StorageContext,
|
|
8
8
|
EnvVarsContext,
|
|
9
|
+
NetworkListContext,
|
|
9
10
|
} from './polyfills/index.js';
|
|
10
11
|
import FirewallEventContext from './polyfills/azion/firewall-event/index.js';
|
|
11
12
|
|
|
@@ -77,6 +78,9 @@ function runtime(code, isFirewallEvent = false) {
|
|
|
77
78
|
// EnvVars Context
|
|
78
79
|
context.ENV_VARS_CONTEXT = EnvVarsContext;
|
|
79
80
|
|
|
81
|
+
// Network List Context
|
|
82
|
+
context.NETWORK_LIST_CONTEXT = NetworkListContext;
|
|
83
|
+
|
|
80
84
|
return context;
|
|
81
85
|
};
|
|
82
86
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { readdir, stat, mkdir, rm, copyFile } from 'fs/promises';
|
|
2
|
+
import { join, extname, basename, dirname } from 'path';
|
|
1
3
|
import { gte, coerce, valid } from 'semver';
|
|
4
|
+
|
|
2
5
|
import {
|
|
3
6
|
exec,
|
|
4
7
|
feedback,
|
|
@@ -7,9 +10,7 @@ import {
|
|
|
7
10
|
copyDirectory,
|
|
8
11
|
Manifest,
|
|
9
12
|
} from '#utils';
|
|
10
|
-
|
|
11
|
-
import { readdir, stat, mkdir, readFile, rm, copyFile } from 'fs/promises';
|
|
12
|
-
import { join, extname, basename, dirname, resolve } from 'path';
|
|
13
|
+
import { getNextConfig, readManifestFile } from '../utils.next.js';
|
|
13
14
|
|
|
14
15
|
const packageManager = await getPackageManager();
|
|
15
16
|
|
|
@@ -84,18 +85,6 @@ async function moveFiles(directory) {
|
|
|
84
85
|
);
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
/**
|
|
88
|
-
* Get a project manifest generated after build
|
|
89
|
-
* @param {string} file - file name
|
|
90
|
-
* @returns {object} - the manifest
|
|
91
|
-
*/
|
|
92
|
-
async function readManifestFile(file) {
|
|
93
|
-
const manifestPath = resolve(process.cwd(), '.next', file);
|
|
94
|
-
const manifest = await readFile(manifestPath, 'utf-8');
|
|
95
|
-
|
|
96
|
-
return JSON.parse(manifest);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
88
|
/**
|
|
100
89
|
* Fix Nextjs routing problems using a default storage
|
|
101
90
|
*/
|
|
@@ -151,6 +140,7 @@ async function fixAppDirRoutes() {
|
|
|
151
140
|
* In this version the static site had significant changes in how to use and how to build.
|
|
152
141
|
* doc reference:
|
|
153
142
|
* https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
|
|
143
|
+
* https://nextjs.org/docs/app/building-your-application/deploying/static-exports
|
|
154
144
|
* @param {string} version - Next.js version ("latest" or in "x.x.x" format).
|
|
155
145
|
* @returns {boolean} - a boolean indicating if is a recent version or not.
|
|
156
146
|
* @example
|
|
@@ -179,30 +169,15 @@ function isANewerVersion(version) {
|
|
|
179
169
|
return false;
|
|
180
170
|
}
|
|
181
171
|
|
|
182
|
-
/**
|
|
183
|
-
* Get next config file
|
|
184
|
-
* @returns {object} - next config as a JSON
|
|
185
|
-
*/
|
|
186
|
-
async function getNextConfig() {
|
|
187
|
-
try {
|
|
188
|
-
const configPath = join(process.cwd(), 'next.config.js');
|
|
189
|
-
const configModule = await import(configPath);
|
|
190
|
-
|
|
191
|
-
return configModule.default;
|
|
192
|
-
} catch (error) {
|
|
193
|
-
throw Error('Error reading next config file:', error);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
172
|
/**
|
|
198
173
|
* Validates if static site mode is enabled in next config.
|
|
199
174
|
* @param {object} nextConfig - the config as JSON object.
|
|
200
175
|
*/
|
|
201
176
|
function validateStaticSiteMode(nextConfig) {
|
|
202
|
-
if (!nextConfig.output || nextConfig.output !== 'export') {
|
|
177
|
+
if (!nextConfig || !nextConfig.output || nextConfig.output !== 'export') {
|
|
203
178
|
const errorMessage = `Static site mode not enabled in project config.
|
|
204
|
-
You must add 'output: "export"' in your 'next.config.js' file if you are trying to build a Next.js v >= 13.3 static project.
|
|
205
|
-
For more details go to https://nextjs.org/docs/pages/building-your-application/deploying/static-exports \n`;
|
|
179
|
+
You must add 'output: "export"' in your 'next.config.js' or 'next.config.mjs' file if you are trying to build a Next.js v >= 13.3 static project.
|
|
180
|
+
For more details go to https://nextjs.org/docs/pages/building-your-application/deploying/static-exports or https://nextjs.org/docs/app/building-your-application/deploying/static-exports \n`;
|
|
206
181
|
throw Error(errorMessage);
|
|
207
182
|
}
|
|
208
183
|
}
|
|
@@ -218,6 +193,7 @@ async function prebuild() {
|
|
|
218
193
|
|
|
219
194
|
const staticsOutputDir = '.edge/storage';
|
|
220
195
|
|
|
196
|
+
// new build format
|
|
221
197
|
if (isANewerVersion(nextVersion)) {
|
|
222
198
|
const nextConfig = await getNextConfig();
|
|
223
199
|
|
|
@@ -225,14 +201,9 @@ async function prebuild() {
|
|
|
225
201
|
|
|
226
202
|
// check if an output path is specified in config file
|
|
227
203
|
let outDir = 'out';
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
(match) => match,
|
|
232
|
-
)[0];
|
|
233
|
-
if (attributeMatch) {
|
|
234
|
-
// get the specified value in config
|
|
235
|
-
outDir = attributeMatch[1].trim().replace(/["']/g, '');
|
|
204
|
+
|
|
205
|
+
if (nextConfig.distDir) {
|
|
206
|
+
outDir = nextConfig.distDir;
|
|
236
207
|
}
|
|
237
208
|
|
|
238
209
|
await exec(`${packageManager} run build`, `Next ${nextVersion}`, true);
|
|
@@ -245,6 +216,8 @@ async function prebuild() {
|
|
|
245
216
|
await moveFiles(`${process.cwd()}/${staticsOutputDir}`);
|
|
246
217
|
await fixAppDirRoutes();
|
|
247
218
|
} else {
|
|
219
|
+
// old build format
|
|
220
|
+
|
|
248
221
|
await exec(`${packageManager} run build`, `Next ${nextVersion}`, true);
|
|
249
222
|
|
|
250
223
|
await exec(
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { readFile, access, constants } from 'fs/promises';
|
|
2
|
+
import { join, resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
import { feedback } from '#utils';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get a project manifest generated after build
|
|
8
|
+
* @param {string} file - file name
|
|
9
|
+
* @returns {object} - the manifest
|
|
10
|
+
*/
|
|
11
|
+
async function readManifestFile(file) {
|
|
12
|
+
const manifestPath = resolve(process.cwd(), '.next', file);
|
|
13
|
+
const manifest = await readFile(manifestPath, 'utf-8');
|
|
14
|
+
|
|
15
|
+
return JSON.parse(manifest);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if a file exists
|
|
20
|
+
* @param {string} path - file path
|
|
21
|
+
* @returns {boolean} indicates if exists or not
|
|
22
|
+
*/
|
|
23
|
+
async function fileExists(path) {
|
|
24
|
+
try {
|
|
25
|
+
await access(path, constants.F_OK);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
// file does not exists
|
|
28
|
+
if (err.code === 'ENOENT') {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
// other error cases
|
|
32
|
+
feedback.prebuild.error('Error reading file:', err);
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get next config file
|
|
41
|
+
* @returns {object} - next config as a JSON
|
|
42
|
+
*/
|
|
43
|
+
async function getNextConfig() {
|
|
44
|
+
try {
|
|
45
|
+
let configFile = null;
|
|
46
|
+
const jsConfigExists = await fileExists('next.config.js');
|
|
47
|
+
const mjsConfigExists = await fileExists('next.config.mjs');
|
|
48
|
+
if (jsConfigExists) {
|
|
49
|
+
configFile = 'next.config.js';
|
|
50
|
+
} else if (mjsConfigExists) {
|
|
51
|
+
configFile = 'next.config.mjs';
|
|
52
|
+
} else {
|
|
53
|
+
feedback.prebuild.info('Nextjs config file does not exists!');
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const configPath = join(process.cwd(), configFile);
|
|
58
|
+
const configModule = await import(configPath);
|
|
59
|
+
|
|
60
|
+
return configModule.default;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
throw Error('Error reading next config file:', error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { readManifestFile, getNextConfig };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edge-functions",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.6.0-stage.2",
|
|
5
5
|
"description": "Tool to launch and build JavaScript/Frameworks. This tool automates polyfills for Edge Computing and assists in creating Workers, notably for the Azion platform.",
|
|
6
6
|
"main": "lib/main.js",
|
|
7
7
|
"bin": {
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"https-browserify": "^1.0.0",
|
|
69
69
|
"inquirer": "^9.2.7",
|
|
70
70
|
"install": "^0.13.0",
|
|
71
|
+
"ip": "^2.0.1",
|
|
71
72
|
"lodash": "^4.17.21",
|
|
72
73
|
"lodash.merge": "^4.6.2",
|
|
73
74
|
"log-update": "^5.0.1",
|