edge-functions 1.0.0

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.
Files changed (230) hide show
  1. package/.babelrc +13 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.json +42 -0
  4. package/.github/workflows/major.yml +28 -0
  5. package/.github/workflows/minor.yml +31 -0
  6. package/.vscode/settings.json +20 -0
  7. package/CODEOWNERS +2 -0
  8. package/CODE_OF_CONDUCT.md +73 -0
  9. package/CONTRIBUTING.md +90 -0
  10. package/LICENSE.md +21 -0
  11. package/README.md +60 -0
  12. package/aliases.js +13 -0
  13. package/docs/overview.md +36 -0
  14. package/docs/presets.md +115 -0
  15. package/examples/angular-static/.editorconfig +16 -0
  16. package/examples/angular-static/README.md +27 -0
  17. package/examples/angular-static/angular.json +98 -0
  18. package/examples/angular-static/package.json +39 -0
  19. package/examples/angular-static/src/app/app-routing.module.ts +10 -0
  20. package/examples/angular-static/src/app/app.component.css +0 -0
  21. package/examples/angular-static/src/app/app.component.html +484 -0
  22. package/examples/angular-static/src/app/app.component.spec.ts +29 -0
  23. package/examples/angular-static/src/app/app.component.ts +10 -0
  24. package/examples/angular-static/src/app/app.module.ts +18 -0
  25. package/examples/angular-static/src/assets/.gitkeep +0 -0
  26. package/examples/angular-static/src/favicon.ico +0 -0
  27. package/examples/angular-static/src/index.html +13 -0
  28. package/examples/angular-static/src/main.ts +7 -0
  29. package/examples/angular-static/src/styles.css +1 -0
  30. package/examples/angular-static/tsconfig.app.json +14 -0
  31. package/examples/angular-static/tsconfig.json +33 -0
  32. package/examples/angular-static/tsconfig.spec.json +14 -0
  33. package/examples/astro-static/README.md +55 -0
  34. package/examples/astro-static/astro.config.mjs +5 -0
  35. package/examples/astro-static/package.json +15 -0
  36. package/examples/astro-static/public/favicon.svg +9 -0
  37. package/examples/astro-static/src/components/Card.astro +63 -0
  38. package/examples/astro-static/src/env.d.ts +1 -0
  39. package/examples/astro-static/src/layouts/Layout.astro +36 -0
  40. package/examples/astro-static/src/pages/edge/index.astro +55 -0
  41. package/examples/astro-static/src/pages/index.astro +81 -0
  42. package/examples/astro-static/tsconfig.json +3 -0
  43. package/examples/hexo-static/.github/dependabot.yml +7 -0
  44. package/examples/hexo-static/_config.landscape.yml +0 -0
  45. package/examples/hexo-static/_config.yml +105 -0
  46. package/examples/hexo-static/package.json +26 -0
  47. package/examples/hexo-static/scaffolds/draft.md +4 -0
  48. package/examples/hexo-static/scaffolds/page.md +4 -0
  49. package/examples/hexo-static/scaffolds/post.md +5 -0
  50. package/examples/hexo-static/source/_posts/hello-world.md +38 -0
  51. package/examples/hexo-static/source/_posts/other-page.md +62 -0
  52. package/examples/hexo-static/themes/.gitkeep +0 -0
  53. package/examples/hexo-static/yarn.lock +1625 -0
  54. package/examples/next-12-static/.babelrc +3 -0
  55. package/examples/next-12-static/README.md +21 -0
  56. package/examples/next-12-static/components/post.jsx +27 -0
  57. package/examples/next-12-static/package-lock.json +6191 -0
  58. package/examples/next-12-static/package.json +18 -0
  59. package/examples/next-12-static/pages/index.jsx +34 -0
  60. package/examples/next-12-static/pages/post/[id].jsx +63 -0
  61. package/examples/next-static/README.md +34 -0
  62. package/examples/next-static/jsconfig.json +7 -0
  63. package/examples/next-static/next.config.js +9 -0
  64. package/examples/next-static/package.json +16 -0
  65. package/examples/next-static/public/next.svg +1 -0
  66. package/examples/next-static/public/vercel.svg +1 -0
  67. package/examples/next-static/src/app/blog/[slug]/page.js +27 -0
  68. package/examples/next-static/src/app/favicon.ico +0 -0
  69. package/examples/next-static/src/app/globals.css +107 -0
  70. package/examples/next-static/src/app/layout.js +17 -0
  71. package/examples/next-static/src/app/misty-mountains/moria/page.js +15 -0
  72. package/examples/next-static/src/app/page.js +96 -0
  73. package/examples/next-static/src/app/page.module.css +229 -0
  74. package/examples/next-static/yarn.lock +199 -0
  75. package/examples/react-static/README.md +70 -0
  76. package/examples/react-static/package.json +38 -0
  77. package/examples/react-static/public/favicon.ico +0 -0
  78. package/examples/react-static/public/index.html +43 -0
  79. package/examples/react-static/public/logo192.png +0 -0
  80. package/examples/react-static/public/logo512.png +0 -0
  81. package/examples/react-static/public/manifest.json +25 -0
  82. package/examples/react-static/public/robots.txt +3 -0
  83. package/examples/react-static/src/App.css +38 -0
  84. package/examples/react-static/src/App.js +25 -0
  85. package/examples/react-static/src/App.test.js +8 -0
  86. package/examples/react-static/src/index.css +13 -0
  87. package/examples/react-static/src/index.js +17 -0
  88. package/examples/react-static/src/logo.svg +1 -0
  89. package/examples/react-static/src/reportWebVitals.js +13 -0
  90. package/examples/react-static/src/setupTests.js +5 -0
  91. package/examples/simple-js-esm/main.js +14 -0
  92. package/examples/simple-js-esm/messages.js +7 -0
  93. package/examples/simple-js-node/main.js +18 -0
  94. package/examples/vue-static/README.md +24 -0
  95. package/examples/vue-static/babel.config.js +5 -0
  96. package/examples/vue-static/jsconfig.json +19 -0
  97. package/examples/vue-static/package.json +45 -0
  98. package/examples/vue-static/public/favicon.ico +0 -0
  99. package/examples/vue-static/public/index.html +17 -0
  100. package/examples/vue-static/src/App.vue +16 -0
  101. package/examples/vue-static/src/assets/logo.png +0 -0
  102. package/examples/vue-static/src/components/HelloWorld.vue +58 -0
  103. package/examples/vue-static/src/main.js +28 -0
  104. package/examples/vue-static/src/views/Home.vue +14 -0
  105. package/examples/vue-static/vue.config.js +4 -0
  106. package/jest.config.js +6 -0
  107. package/jsconfig.json +40 -0
  108. package/jsdoc.json +52 -0
  109. package/lib/build/bundlers/index.js +4 -0
  110. package/lib/build/bundlers/webpack/index.js +40 -0
  111. package/lib/build/bundlers/webpack/webpack.config.js +38 -0
  112. package/lib/build/dispatcher/dispatcher.js +211 -0
  113. package/lib/build/dispatcher/dispatcher.test.js +0 -0
  114. package/lib/build/dispatcher/index.js +3 -0
  115. package/lib/build/polyfills/index.js +0 -0
  116. package/lib/constants/azion-edges.constants.js +98 -0
  117. package/lib/constants/index.js +5 -0
  118. package/lib/constants/messages/build.messages.js +23 -0
  119. package/lib/constants/messages/env.messages.js +38 -0
  120. package/lib/constants/messages/global.messages.js +19 -0
  121. package/lib/constants/messages/index.js +10 -0
  122. package/lib/constants/messages/platform.messages.js +84 -0
  123. package/lib/constants/runtime-apis.constants.js +118 -0
  124. package/lib/env/index.js +5 -0
  125. package/lib/env/runtime.env.js +69 -0
  126. package/lib/env/server.env.js +90 -0
  127. package/lib/env/vulcan.env.js +93 -0
  128. package/lib/main.js +260 -0
  129. package/lib/notations/namespaces.js +30 -0
  130. package/lib/notations/typedef.js +10 -0
  131. package/lib/platform/actions/application/createApplication.actions.js +33 -0
  132. package/lib/platform/actions/application/enableEdgeFunctions.actions.js +34 -0
  133. package/lib/platform/actions/application/instantiateFunction.actions.js +37 -0
  134. package/lib/platform/actions/application/setFunctionAsDefaultRule.actions.js +33 -0
  135. package/lib/platform/actions/core/auth.actions.js +67 -0
  136. package/lib/platform/actions/core/deploy.actions.js +73 -0
  137. package/lib/platform/actions/core/propagation.actions.js +75 -0
  138. package/lib/platform/actions/core/storage.actions.js +84 -0
  139. package/lib/platform/actions/domain/createDomain.actions.js +42 -0
  140. package/lib/platform/actions/function/createFunction.actions.js +79 -0
  141. package/lib/platform/actions/function/showFunctionLogs.actions.js +149 -0
  142. package/lib/platform/edgehooks/ErrorHTML/ErrorHTML.hooks.js +101 -0
  143. package/lib/platform/edgehooks/ErrorHTML/index.js +3 -0
  144. package/lib/platform/edgehooks/index.js +5 -0
  145. package/lib/platform/edgehooks/mountSPA/index.js +3 -0
  146. package/lib/platform/edgehooks/mountSPA/mountSPA.hooks.js +55 -0
  147. package/lib/platform/edgehooks/mountSPA/mountSPA.hooks.test.js +19 -0
  148. package/lib/platform/edgehooks/mountSSG/index.js +3 -0
  149. package/lib/platform/edgehooks/mountSSG/mountSSG.hooks.js +61 -0
  150. package/lib/platform/edgehooks/mountSSG/mountSSG.hooks.test.js +0 -0
  151. package/lib/platform/index.js +65 -0
  152. package/lib/platform/services/application.service.js +140 -0
  153. package/lib/platform/services/base.service.js +200 -0
  154. package/lib/platform/services/domain.service.js +80 -0
  155. package/lib/platform/services/events.service.js +65 -0
  156. package/lib/platform/services/function.service.js +105 -0
  157. package/lib/platform/services/index.js +8 -0
  158. package/lib/platform/services/storage.service.js +59 -0
  159. package/lib/platform/services/tokens.service.js +55 -0
  160. package/lib/presets/custom/angular/deliver/config.js +12 -0
  161. package/lib/presets/custom/angular/deliver/handler.js +8 -0
  162. package/lib/presets/custom/angular/deliver/prebuild.js +20 -0
  163. package/lib/presets/custom/astro/deliver/config.js +12 -0
  164. package/lib/presets/custom/astro/deliver/handler.js +8 -0
  165. package/lib/presets/custom/astro/deliver/prebuild.js +37 -0
  166. package/lib/presets/custom/hexo/deliver/config.js +12 -0
  167. package/lib/presets/custom/hexo/deliver/handler.js +8 -0
  168. package/lib/presets/custom/hexo/deliver/prebuild.js +37 -0
  169. package/lib/presets/custom/next/deliver/config.js +14 -0
  170. package/lib/presets/custom/next/deliver/handler.js +9 -0
  171. package/lib/presets/custom/next/deliver/prebuild.js +193 -0
  172. package/lib/presets/custom/react/deliver/config.js +12 -0
  173. package/lib/presets/custom/react/deliver/handler.js +8 -0
  174. package/lib/presets/custom/react/deliver/prebuild.js +16 -0
  175. package/lib/presets/custom/vue/deliver/config.js +12 -0
  176. package/lib/presets/custom/vue/deliver/handler.js +8 -0
  177. package/lib/presets/custom/vue/deliver/prebuild.js +20 -0
  178. package/lib/presets/default/html/deliver/config.js +13 -0
  179. package/lib/presets/default/html/deliver/handler.js +9 -0
  180. package/lib/presets/default/html/deliver/prebuild.js +15 -0
  181. package/lib/presets/default/javascript/compute/config.js +13 -0
  182. package/lib/presets/default/javascript/compute/handler.js +5 -0
  183. package/lib/presets/default/javascript/compute/prebuild.js +6 -0
  184. package/lib/presets/default/typescript/compute/config.js +0 -0
  185. package/lib/presets/default/typescript/compute/handler.js +0 -0
  186. package/lib/presets/default/typescript/compute/prebuild.js +0 -0
  187. package/lib/providers/azion/worker.js +12 -0
  188. package/lib/utils/copyDirectory/copyDirectory.utils.js +54 -0
  189. package/lib/utils/copyDirectory/copyDirectory.utils.test.js +43 -0
  190. package/lib/utils/copyDirectory/index.js +3 -0
  191. package/lib/utils/debug/debug.utils.js +36 -0
  192. package/lib/utils/debug/debug.utils.test.js +43 -0
  193. package/lib/utils/debug/index.js +3 -0
  194. package/lib/utils/exec/exec.utils.js +58 -0
  195. package/lib/utils/exec/exec.utils.test.js +84 -0
  196. package/lib/utils/exec/index.js +3 -0
  197. package/lib/utils/feedback/feedback.utils.js +81 -0
  198. package/lib/utils/feedback/feedback.utils.test.js +11 -0
  199. package/lib/utils/feedback/index.js +3 -0
  200. package/lib/utils/generateTimestamp/generateTimestamp.utils.js +25 -0
  201. package/lib/utils/generateTimestamp/generateTimestamp.utils.test.js +10 -0
  202. package/lib/utils/generateTimestamp/index.js +3 -0
  203. package/lib/utils/getAbsoluteLibDirPath/getAbsoluteLibDirPath.utils.js +21 -0
  204. package/lib/utils/getAbsoluteLibDirPath/getAbsoluteLibDirPath.utils.test.js +13 -0
  205. package/lib/utils/getAbsoluteLibDirPath/index.js +3 -0
  206. package/lib/utils/getPackageManager/getPackageManager.utils.js +118 -0
  207. package/lib/utils/getPackageManager/getPackageManager.utils.test.js +35 -0
  208. package/lib/utils/getPackageManager/index.js +3 -0
  209. package/lib/utils/getPackageVersion/getPackageVersion.utils.js +25 -0
  210. package/lib/utils/getPackageVersion/getPackageVersion.utils.test.js +48 -0
  211. package/lib/utils/getPackageVersion/index.js +3 -0
  212. package/lib/utils/getPresetsList/getPresetsList.utils.js +50 -0
  213. package/lib/utils/getPresetsList/getPresetsList.utils.test.js +19 -0
  214. package/lib/utils/getPresetsList/index.js +3 -0
  215. package/lib/utils/getProjectJsonFile/getProjectJsonFile.utils.js +21 -0
  216. package/lib/utils/getProjectJsonFile/getProjectJsonFile.utils.test.js +39 -0
  217. package/lib/utils/getProjectJsonFile/index.js +3 -0
  218. package/lib/utils/getVulcanBuildId/getVulcanBuildId.utils.js +49 -0
  219. package/lib/utils/getVulcanBuildId/getVulcanBuildId.utils.test.js +36 -0
  220. package/lib/utils/getVulcanBuildId/index.js +3 -0
  221. package/lib/utils/index.js +29 -0
  222. package/lib/utils/overrideStaticOutputPath/index.js +3 -0
  223. package/lib/utils/overrideStaticOutputPath/overrideStaticOutputPath.utils.js +47 -0
  224. package/lib/utils/overrideStaticOutputPath/overrideStaticOutputPath.utils.test.js +35 -0
  225. package/lib/utils/readWorkerFile/index.js +3 -0
  226. package/lib/utils/readWorkerFile/readWorkerFile.utils.js +36 -0
  227. package/lib/utils/readWorkerFile/readWorkerFile.utils.test.js +24 -0
  228. package/package.json +99 -0
  229. package/releaserc.json +87 -0
  230. package/tasks/sync-aliases.js +115 -0
@@ -0,0 +1,84 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import mime from 'mime-types';
4
+ import { promisify } from 'util';
5
+ import { feedback, debug } from '#utils';
6
+ import { Messages } from '#constants';
7
+ import StorageService from '../../services/storage.service.js';
8
+
9
+ /**
10
+ * @function
11
+ * @memberof platform
12
+ * @name uploadStatics
13
+ * @description Uploads files to the storage. This function recursively navigates
14
+ * through the directory structure from the specified base path and uploads all the files
15
+ * it encounters. It counts the successful uploads and provides feedback on each file upload status.
16
+ * @param {string} versionId - The ID of the version to which the files are to be uploaded.
17
+ * @param {string} basePath - The base path from where files to upload are located.
18
+ * @param {string} [currentPath] - The current path being processed, used for recursive calls.
19
+ * Defaults to an empty string.
20
+ * @returns {Promise<number>} A promise that resolves with the total number of files
21
+ * uploaded successfully.
22
+ * @throws Will throw an error if there's a problem reading files or directories,
23
+ * or uploading a file.
24
+ * @example
25
+ * try {
26
+ * const totalUploaded = await uploadStatics('version123', './path/to/files');
27
+ * console.log(`Total files uploaded: ${totalUploaded}`);
28
+ * } catch (error) {
29
+ * console.error(error);
30
+ * }
31
+ */
32
+ async function uploadStatics(versionId, basePath, currentPath = '') {
33
+ let successUploadCount = 0;
34
+ /**
35
+ * Uploads files recursively to the storage.
36
+ * @param {string} filePath - The file path.
37
+ * @returns {Promise<void>} A Promise that resolves when the file is uploaded.
38
+ */
39
+ async function uploadFiles(filePath) {
40
+ const fullFilePath = path.join(basePath, filePath);
41
+ const fileStat = await promisify(fs.stat)(fullFilePath);
42
+
43
+ if (fileStat.isFile()) {
44
+ const fileContent = await promisify(fs.readFile)(fullFilePath, 'utf8');
45
+ const mimeType = mime.lookup(fullFilePath);
46
+ const staticPath = path.join(currentPath, filePath);
47
+
48
+ try {
49
+ const response = await StorageService.upload(versionId, fileContent, staticPath, mimeType);
50
+ if (response.statusText === 'OK') {
51
+ successUploadCount += 1;
52
+ feedback.statics.success(
53
+ Messages.platform.storage.success.file_uploaded_success(filePath),
54
+ );
55
+ }
56
+ if (!response.status === 'OK') { throw new Error(response); }
57
+ } catch (error) {
58
+ feedback.statics.error(Messages.platform.storage.errors.file_upload_failed(filePath));
59
+ debug.error(error);
60
+ }
61
+ } else if (fileStat.isDirectory()) {
62
+ const subDirPath = path.join(currentPath, filePath);
63
+ const subDirFiles = await promisify(fs.readdir)(path.join(basePath, subDirPath));
64
+
65
+ await Promise.all(subDirFiles.map((file) => uploadFiles(path.join(subDirPath, file))));
66
+ }
67
+ }
68
+
69
+ try {
70
+ const files = await promisify(fs.readdir)(path.join(basePath, currentPath));
71
+ await Promise.all(files.map((file) => uploadFiles(file)));
72
+ } catch (error) {
73
+ debug.error(error);
74
+ throw error;
75
+ }
76
+
77
+ feedback.statics.success(
78
+ Messages.platform.storage.success.statics_uploaded_finish(successUploadCount),
79
+ );
80
+ return successUploadCount;
81
+ }
82
+
83
+ // eslint-disable-next-line import/prefer-default-export
84
+ export { uploadStatics };
@@ -0,0 +1,42 @@
1
+ import { debug } from '#utils';
2
+ import DomainService from '../../services/domain.service.js';
3
+
4
+ /**
5
+ * @function
6
+ * @memberof platform
7
+ * @name createDomain
8
+ * @description Creates a new domain for a specific application. This operation requires
9
+ * both the name for the new domain and the ID of the application to which it will be linked.
10
+ * @param {string} domainName - The name of the domain to be created.
11
+ * @param {number} applicationId - The ID of the application to which the new domain will be linked.
12
+ * @returns {Promise<object>} A promise that resolves with the created domain's details.
13
+ * The object returned contains details such as the ID, name, associated
14
+ * edge application ID, and other properties.
15
+ * @throws Will throw an error if the domain creation fails.
16
+ * @example
17
+ * try {
18
+ * const domainDetails = await createDomain('example.com', 1234);
19
+ * console.log(`Domain created with ID: ${domainDetails.id}`);
20
+ * } catch (error) {
21
+ * console.error('Failed to create domain:', error);
22
+ * }
23
+ */
24
+ async function createDomain(domainName, applicationId) {
25
+ try {
26
+ const payload = {
27
+ name: domainName,
28
+ cname_access_only: false,
29
+ digital_certificate_id: null,
30
+ edge_application_id: applicationId,
31
+ is_active: true,
32
+ };
33
+
34
+ const response = await (await DomainService.create(payload)).json();
35
+ return response.results;
36
+ } catch (error) {
37
+ debug.error(error);
38
+ throw error;
39
+ }
40
+ }
41
+
42
+ export default createDomain;
@@ -0,0 +1,79 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { debug } from '#utils';
4
+ import FunctionService from '../../services/function.service.js';
5
+
6
+ /**
7
+ * Get the initiator type based on the code signature.
8
+ * @param {string} code - The code for the function.
9
+ * @returns {string} The initiator type.
10
+ */
11
+ function getInitiatorType(code) {
12
+ const fetchSignature = 'addEventListener(\'fetch\'';
13
+ const firewallSignature = 'addEventListener(\'firewall\'';
14
+
15
+ if (code.includes(fetchSignature)) {
16
+ return 'edge_application';
17
+ } if (code.includes(firewallSignature)) {
18
+ return 'edge_firewall';
19
+ }
20
+ return 'edge_application';
21
+ }
22
+
23
+ /**
24
+ * @function
25
+ * @memberof platform
26
+ * @name createFunction
27
+ * @description Creates a new function (worker) for the edge application.
28
+ * It reads the function code and arguments from the respective '.edge/worker.js'
29
+ * and '.edge/args.json' files, and infers the initiator type based on the function's code.
30
+ * @param {string} [functionName] - Optional. The name of the function to be created.
31
+ * If not provided, the name is determined from the function's code.
32
+ * @returns {Promise<object>} A promise that resolves with the details of the created
33
+ * function. The object returned contains details such as the function ID, name, code,
34
+ * language, initiator type, arguments, and activation status.
35
+ * @throws {Error} If the function creation fails, possibly due to invalid code or
36
+ * function arguments, or due to a problem with the FunctionService.
37
+ * @example
38
+ * try {
39
+ * const functionDetails = await createFunction('myFunction');
40
+ * console.log(`Function created with ID: ${functionDetails.id}`);
41
+ * } catch (error) {
42
+ * console.error('Failed to create function:', error);
43
+ * }
44
+ */
45
+ async function createFunction(functionName) {
46
+ try {
47
+ // get code
48
+ const workerFilePath = path.join(process.cwd(), '.edge/worker.js');
49
+ const code = fs.readFileSync(workerFilePath, 'utf8');
50
+
51
+ // get json_args
52
+ const argsFilePath = path.join(process.cwd(), '.edge/args.json');
53
+ let jsonArgs = {};
54
+ if (fs.existsSync(argsFilePath)) {
55
+ const argsData = fs.readFileSync(argsFilePath, 'utf8');
56
+ jsonArgs = JSON.parse(argsData);
57
+ }
58
+
59
+ // get initiator_type
60
+ const initiatorType = getInitiatorType(code);
61
+
62
+ const payload = {
63
+ name: functionName,
64
+ code,
65
+ language: 'javascript',
66
+ initiator_type: initiatorType,
67
+ json_args: jsonArgs,
68
+ active: true,
69
+ };
70
+
71
+ const response = await (await FunctionService.create(payload)).json();
72
+ return response.results;
73
+ } catch (error) {
74
+ debug.error(error);
75
+ throw error;
76
+ }
77
+ }
78
+
79
+ export default createFunction;
@@ -0,0 +1,149 @@
1
+ import { feedback, debug } from '#utils';
2
+ import { Messages } from '#constants';
3
+ import eventsService from '../../services/events.service.js';
4
+
5
+ /**
6
+ *
7
+ * @param {object} log - View logs organized in a box.
8
+ * @param {boolean} [watch=false] - Whether to observe logs in real time.
9
+ */
10
+ function displayLogInBox(log, watch = false) {
11
+ const functionIdLabel = 'Function ID:';
12
+ const dateLabel = 'Date:';
13
+ const eventIdLabel = 'Event ID:';
14
+ const functionIdLine = `${functionIdLabel} ${log.functionId}`;
15
+ const dateLine = `${dateLabel} ${log.date}`;
16
+ const eventIdLine = `${eventIdLabel} ${log.eventId}`;
17
+ const maxLength = Math.max(
18
+ functionIdLine.length,
19
+ dateLine.length,
20
+ eventIdLine.length,
21
+ ...log.logs.map((line) => line.length),
22
+ );
23
+ const horizontalLine = '═'.repeat(maxLength + 2);
24
+
25
+ console.log(`╔${horizontalLine}╗`);
26
+ console.log(`║ ${functionIdLine.padEnd(maxLength)} ║`);
27
+ console.log(`║ ${eventIdLine.padEnd(maxLength)} ║`);
28
+ console.log(`║ ${dateLine.padEnd(maxLength)} ║`);
29
+ console.log(`╠${horizontalLine}╣`);
30
+ log.logs.forEach((line) => {
31
+ console.log(`║ ${line.padEnd(maxLength)} ║`);
32
+ });
33
+ console.log(`╚${horizontalLine}╝`);
34
+
35
+ if (watch) {
36
+ console.log(Messages.platform.logs.info.watch_true);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @function
42
+ * @memberof platform
43
+ * @name showFunctionLogs
44
+ * @description This function retrieves and displays the logs of a specific function
45
+ * or all functions from the edge application. If the watch parameter is set to true,
46
+ * it continuously polls and updates the function logs in real time.
47
+ * @param {string} [functionId] - Optional. The ID of the function for which to retrieve the logs.
48
+ * If not provided, the logs from all functions are retrieved.
49
+ * @param {boolean} [watch=false] - Optional. If set to true, the function logs are observed
50
+ * and updated in real time. Default is false.
51
+ * @returns {void} This function does not return anything. It retrieves and logs the function
52
+ * logs to the console.
53
+ * @throws {Error} If an error occurs while retrieving the logs, it is caught
54
+ * and logged to the console.
55
+ * @example
56
+ * // To observe logs for a specific function in real time:
57
+ * showFunctionLogs('myFunctionId', true);
58
+ *
59
+ * // To get logs from all functions:
60
+ * showFunctionLogs();
61
+ */
62
+ async function showFunctionLogs(functionId, watch = false) {
63
+ let previousConsoleLogId = null;
64
+
65
+ /**
66
+ * Mounts the structure for a console log.
67
+ * @param {object} azionLog - The Azion log event.
68
+ * @param {string} azionLog.id - The ID of the event.
69
+ * @param {string} azionLog.ts - The timestamp of the event.
70
+ * @returns {object} - The log event structure.
71
+ * @property {string} eventId - The ID of the event.
72
+ * @property {string} date - The formatted date of the event.
73
+ * @property {string[]} logs - The array of log messages.
74
+ */
75
+ const mountEventLogStructure = (azionLog) => ({
76
+ eventId: azionLog.id,
77
+ functionId: azionLog.functionId,
78
+ date: new Date(azionLog.ts).toLocaleString(),
79
+ logs: [],
80
+ });
81
+
82
+ /**
83
+ * Internal function for performing polling and retrieving function logs.
84
+ * @returns {void}
85
+ */
86
+ const startPoll = async () => {
87
+ try {
88
+ const response = await eventsService.getFunctionsLogs(functionId);
89
+ const responseJSON = await response.json();
90
+ const { cellsConsoleEvents } = responseJSON.data;
91
+ const requestSuccess = !!cellsConsoleEvents;
92
+
93
+ if (!requestSuccess) {
94
+ throw new Error(JSON.stringify(responseJSON));
95
+ }
96
+ if (requestSuccess) {
97
+ const nextConsoleLog = cellsConsoleEvents.length > 0 ? cellsConsoleEvents[0] : null;
98
+
99
+ if (!nextConsoleLog) {
100
+ feedback.info(Messages.platform.logs.info.no_logs);
101
+ }
102
+ if (nextConsoleLog) {
103
+ if (nextConsoleLog?.id !== previousConsoleLogId) {
104
+ if (watch) {
105
+ const event = mountEventLogStructure(nextConsoleLog);
106
+ // eslint-disable-next-line max-len
107
+ const logsGroupedByRequest = cellsConsoleEvents.filter((item) => item.id === nextConsoleLog.id);
108
+ logsGroupedByRequest.forEach((log) => {
109
+ event.logs.push(log.line);
110
+ });
111
+ displayLogInBox(event, watch);
112
+ previousConsoleLogId = nextConsoleLog.id;
113
+ }
114
+
115
+ if (!watch) {
116
+ const logsGroupedByRequest = Object.values(cellsConsoleEvents
117
+ .reduce((result, event) => {
118
+ const { id, line } = event;
119
+
120
+ if (!result[id]) {
121
+ // eslint-disable-next-line no-param-reassign
122
+ result[id] = mountEventLogStructure(event);
123
+ }
124
+
125
+ result[id].logs.push(line);
126
+
127
+ return result;
128
+ }, {}));
129
+
130
+ logsGroupedByRequest.forEach((log) => {
131
+ displayLogInBox(log, watch);
132
+ });
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ if (watch) {
139
+ setTimeout(startPoll, 500);
140
+ }
141
+ } catch (error) {
142
+ debug.error(error);
143
+ }
144
+ };
145
+
146
+ startPoll();
147
+ }
148
+
149
+ export default showFunctionLogs;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @function formatLog
3
+ * @description Formats the request and error information for logging.
4
+ * @param {Error} error - The error object.
5
+ * @returns {string} The formatted log string.
6
+ */
7
+ function formatLog(error) {
8
+ let formattedLog = '';
9
+
10
+ if (error) {
11
+ const formattedError = error instanceof Error ? error.stack : error;
12
+ formattedLog += `<b>Error:</b>\n${formattedError}`;
13
+ }
14
+
15
+ return formattedLog;
16
+ }
17
+
18
+ /**
19
+ * @function createErrorHTML
20
+ * @description Creates the HTML content for the custom error page.
21
+ * @param {number} errorCode - The error code.
22
+ * @param {string} errorDescription - The error description.
23
+ * @param {Error} [error] - Optional. The debug object containing error object.
24
+ * @returns {string} The HTML content.
25
+ */
26
+ function createErrorHTML(errorCode, errorDescription, error) {
27
+ const formattedLog = formatLog(error);
28
+
29
+ return `<!DOCTYPE html>
30
+ <html lang="en">
31
+ <head>
32
+ <meta charset="UTF-8">
33
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
34
+ <title>Error ${errorCode} - ${errorDescription}</title>
35
+ <style>
36
+ body {
37
+ background-color: black;
38
+ color: rgb(255, 108, 55);
39
+ font-family: Arial, sans-serif;
40
+ text-align: center;
41
+ display: flex;
42
+ flex-direction: column;
43
+ justify-content: center;
44
+ align-items: center;
45
+ height: 100vh;
46
+ margin: 0;
47
+ }
48
+ h1 {
49
+ font-size: 36px;
50
+ margin-bottom: 10px;
51
+ animation: levitate 2s ease-in-out infinite alternate;
52
+ }
53
+ p {
54
+ font-size: 18px;
55
+ margin-bottom: 5px;
56
+ }
57
+ a {
58
+ color: rgb(255, 108, 55);
59
+ text-decoration: none;
60
+ }
61
+
62
+ @keyframes levitate {
63
+ 0% {
64
+ transform: translateY(0);
65
+ }
66
+ 50% {
67
+ transform: translateY(-10px);
68
+ }
69
+ 100% {
70
+ transform: translateY(0);
71
+ }
72
+ }
73
+ </style>
74
+ </head>
75
+ <body>
76
+ <h1>Error ${errorCode}</h1>
77
+ <p>${errorDescription || ''}</p>
78
+ <pre>${formattedLog}</pre>
79
+ </body>
80
+ </html>`;
81
+ }
82
+
83
+ /**
84
+ * @function
85
+ * @memberof edge
86
+ * @name ErrorHTML
87
+ * @description Handles an error by logging the request and error
88
+ * information and returning a custom HTML response.
89
+ * @param {number} code - The error code.
90
+ * @param {string} description - The error description.
91
+ * @param {Error} [error] - Optional. The error object for debugging.
92
+ * If provided, the error will be displayed on the error page.
93
+ * @returns {Response} The custom HTML response.
94
+ */
95
+ function ErrorHTML(code, description, error) {
96
+ const errorHTML = createErrorHTML(code, description, error);
97
+ const headers = { 'Content-Type': 'text/html' };
98
+ return new Response(errorHTML, { status: code, headers });
99
+ }
100
+
101
+ export default ErrorHTML;
@@ -0,0 +1,3 @@
1
+ import ErrorHTML from './ErrorHTML.hooks.js';
2
+
3
+ export default ErrorHTML;
@@ -0,0 +1,5 @@
1
+ import mountSPA from './mountSPA/mountSPA.hooks.js';
2
+ import mountSSG from './mountSSG/mountSSG.hooks.js';
3
+ import ErrorHTML from './ErrorHTML/ErrorHTML.hooks.js';
4
+
5
+ export { mountSPA, mountSSG, ErrorHTML };
@@ -0,0 +1,3 @@
1
+ import mountSPA from './mountSPA.hooks.js';
2
+
3
+ export default mountSPA;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @memberof edge
3
+ * @function
4
+ * @name mountSPA
5
+ * @description
6
+ * The `mountSPA` function is designed to process requests to a Single Page Application (SPA)
7
+ * that's being computed at the edge of a Content Delivery Network (CDN).
8
+ *
9
+ * This function determines if the incoming request is for a static
10
+ * asset or a route within the application,
11
+ * and mounts the appropriate request URL for fetching the required resource from the origin server.
12
+ * @param {string} requestURL - The original URL from the incoming request.
13
+ * @param {string} versionId - The version ID representing the build/assets stored.
14
+ * @returns {Promise<Response>} A promise that resolves to the response from the fetched resource.
15
+ * @example
16
+ * // Handle a request for a homepage
17
+ * // Input: mountSSG('https://example.com/', 'v1');
18
+ * // Output: fetch('file:///v1/index.html');
19
+ * @example
20
+ * // Handle a request for an asset (CSS file)
21
+ * // Input: mountSSG('https://example.com/styles/main.css', 'v1');
22
+ * // Output: fetch('file:///v1/styles/main.css');
23
+ * @example
24
+ * // Handle a request for a specific route
25
+ * // Input: mountSSG('https://example.com/about', 'v1');
26
+ * // Output: fetch('file:///v1/index.html');
27
+ */
28
+ export default function mountSPA(requestURL, versionId) {
29
+ /**
30
+ * The path extracted from the request URL.
31
+ * @type {string}
32
+ */
33
+ const requestPath = new URL(requestURL).pathname;
34
+
35
+ let assetPath;
36
+
37
+ /**
38
+ * Regular expression to match the file extension at the end of a string.
39
+ * Used to determine if the requestPath is an asset or a route.
40
+ * @type {RegExp}
41
+ */
42
+ const fileExtensionRegex = /\.[^.]+$/;
43
+
44
+ if (fileExtensionRegex.test(requestPath)) {
45
+ // If the requestPath has a file extension, it is considered an asset.
46
+ // Concatenate the versionId and requestPath to form the complete asset URL.
47
+ assetPath = new URL(`/${versionId}${requestPath}`, 'file://');
48
+ } else {
49
+ // If the requestPath does not have a file extension, it is treated as a route.
50
+ // Append the versionId and the route "index.html" to form the complete asset URL.
51
+ assetPath = new URL(`/${versionId}/index.html`, 'file://');
52
+ }
53
+
54
+ return fetch(assetPath);
55
+ }
@@ -0,0 +1,19 @@
1
+ import assert from 'assert';
2
+ import { feedback } from '#utils';
3
+ import mountSPA from './index.js';
4
+
5
+ /**
6
+ * Test the mountSPA function.
7
+ */
8
+ function testmountSPA() {
9
+ const requestURL = 'https://example.com/path/to/resource';
10
+ const versionId = 'v1';
11
+ const expectedAssetUrl = 'file:///v1/path/to/resource';
12
+
13
+ const assetUrl = mountSPA(requestURL, versionId);
14
+
15
+ assert.strictEqual(assetUrl.href, expectedAssetUrl);
16
+ feedback.success('mountSPA test passed!');
17
+ }
18
+
19
+ testmountSPA();
@@ -0,0 +1,3 @@
1
+ import mountSSG from './mountSSG.hooks.js';
2
+
3
+ export default mountSSG;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @memberof edge
3
+ * @function mountSSG
4
+ * @name mountSSG
5
+ *@description
6
+ * The `mountSSG` function is designed to handle requests for Static Site Generation (SSG)
7
+ * at the edge, that is, directly within a serverless worker.
8
+ * It processes the incoming request URL and an associated version ID. The function constructs
9
+ * the appropriate asset path based on these parameters, and fetches the corresponding
10
+ * response from the SSG.
11
+ * @param {string} requestURL - The original URL from the event request.
12
+ * @param {string} versionId - The version ID representing the build/assets stored.
13
+ * @returns {Promise<Response>} A promise that resolves to the response from the SSG.
14
+ * @example
15
+ * // Handle a request for a homepage
16
+ * // Input: mountSSG('https://example.com/', 'v1');
17
+ * // Output: fetch('file:///v1/index.html');
18
+ * @example
19
+ * // Handle a request for an asset (CSS file)
20
+ * // Input: mountSSG('https://example.com/styles/main.css', 'v1');
21
+ * // Output: fetch('file:///v1/styles/main.css');
22
+ * @example
23
+ * // Handle a request for a specific route
24
+ * // Input: mountSSG('https://example.com/about', 'v1');
25
+ * // Output: fetch('file:///v1/about/index.html');
26
+ */
27
+ export default function mountSSG(requestURL, versionId) {
28
+ /**
29
+ * The path extracted from the request URL.
30
+ * @type {string}
31
+ */
32
+ const requestPath = new URL(requestURL).pathname;
33
+
34
+ // Check if the requestPath has a trailing slash and remove the trailing slash if it exists
35
+ const hasTrailingSlash = requestPath.endsWith('/');
36
+ const cleanRequestPath = hasTrailingSlash ? requestPath.slice(0, -1) : requestPath;
37
+
38
+ /**
39
+ * Regular expression to match the file extension at the end of a string.
40
+ * Used to determine if the requestPath is an asset or a route.
41
+ * @type {RegExp}
42
+ */
43
+ const fileExtensionRegex = /\.[^.]+$/;
44
+
45
+ let assetPath;
46
+
47
+ if (cleanRequestPath === '/') {
48
+ // If the requestPath is the root path, append the versionId and 'index.html' to the assetPath.
49
+ assetPath = new URL(`/${versionId}/index.html`, 'file://');
50
+ } else if (fileExtensionRegex.test(cleanRequestPath)) {
51
+ // If the requestPath has a file extension, it is considered an asset.
52
+ // Concatenate the versionId and requestPath to form the complete asset URL.
53
+ assetPath = new URL(`/${versionId}${cleanRequestPath}`, 'file://');
54
+ } else {
55
+ // If the requestPath is a route without a file extension,
56
+ // Append the versionId and requestPath to the assetPath.
57
+ assetPath = new URL(`/${versionId}${cleanRequestPath}/index.html`, 'file://');
58
+ }
59
+
60
+ return fetch(assetPath);
61
+ }
@@ -0,0 +1,65 @@
1
+ // core
2
+ import auth from './actions/core/auth.actions.js';
3
+ import deploy from './actions/core/deploy.actions.js';
4
+ import { uploadStatics } from './actions/core/storage.actions.js';
5
+ import { watchPropagation } from './actions/core/propagation.actions.js';
6
+ // domain
7
+ import createDomain from './actions/domain/createDomain.actions.js';
8
+
9
+ // application
10
+ import createApplication from './actions/application/createApplication.actions.js';
11
+ import enableEdgeFunctions from './actions/application/enableEdgeFunctions.actions.js';
12
+ import instantiateFunction from './actions/application/instantiateFunction.actions.js';
13
+ import setFunctionAsDefaultRule from './actions/application/setFunctionAsDefaultRule.actions.js';
14
+
15
+ // function
16
+ import showFunctionLogs from './actions/function/showFunctionLogs.actions.js';
17
+
18
+ /**
19
+ * Object containing core-related actions.
20
+ * @typedef {object} CoreObject
21
+ * @property {object} actions - Object containing Azion core actions.
22
+ * @property {auth} actions.auth - Action for Azion authentication.
23
+ * @property {uploadStatics} actions.uploadStatics - Action for uploading static files.
24
+ * @property {deploy} actions.deploy - Action for deploying application.
25
+ * @property {watchPropagation} actions.watchPropagation - Action for watch Edge Propagation.
26
+ */
27
+ const core = {
28
+ actions: {
29
+ auth, deploy, uploadStatics, watchPropagation,
30
+ },
31
+ };
32
+
33
+ const applications = {
34
+ actions: {
35
+ createApplication,
36
+ enableEdgeFunctions,
37
+ instantiateFunction,
38
+ showFunctionLogs,
39
+ setFunctionAsDefaultRule,
40
+ },
41
+ };
42
+
43
+ /**
44
+ * Object containing domain-related actions.
45
+ * @typedef {object} DomainsObject
46
+ * @property {object} actions - Object containing Azion domain actions.
47
+ * @property {createDomain} actions.createDomain - Action to create a domain.
48
+ */
49
+ const domains = {
50
+ actions: { createDomain },
51
+ };
52
+
53
+ /**
54
+ * Object containing function-related actions.
55
+ * @typedef {object} FunctionsObject
56
+ * @property {object} actions - Object containing Azion function actions.
57
+ * @property {showFunctionLogs} actions.showFunctionLogs - Action to perform Azion function logs.
58
+ */
59
+ const functions = {
60
+ actions: { showFunctionLogs },
61
+ };
62
+
63
+ export {
64
+ core, applications, domains, functions,
65
+ };