edge-functions 1.0.0 → 1.3.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 (111) hide show
  1. package/.eslintrc.json +60 -15
  2. package/.github/workflows/major.yml +1 -1
  3. package/.github/workflows/minor.yml +1 -2
  4. package/README.md +36 -0
  5. package/aliases.js +1 -1
  6. package/docs/overview.md +4 -3
  7. package/docs/presets.md +18 -6
  8. package/examples/angular-static/package.json +0 -1
  9. package/examples/simple-js-esm/main.js +13 -8
  10. package/examples/simple-js-esm-node/index.js +45 -0
  11. package/examples/simple-ts-esm/main.ts +24 -0
  12. package/examples/simple-ts-esm/messages.ts +11 -0
  13. package/examples/simple-ts-esm/package.json +9 -0
  14. package/examples/simple-ts-esm/tsconfig.json +109 -0
  15. package/examples/simple-ts-esm/yarn.lock +8 -0
  16. package/examples/vue-vite-static/README.md +29 -0
  17. package/examples/vue-vite-static/index.html +13 -0
  18. package/examples/vue-vite-static/package.json +18 -0
  19. package/examples/vue-vite-static/public/favicon.ico +0 -0
  20. package/examples/vue-vite-static/src/App.vue +85 -0
  21. package/examples/vue-vite-static/src/assets/base.css +73 -0
  22. package/examples/vue-vite-static/src/assets/logo.svg +1 -0
  23. package/examples/vue-vite-static/src/assets/main.css +35 -0
  24. package/examples/vue-vite-static/src/components/HelloWorld.vue +44 -0
  25. package/examples/vue-vite-static/src/components/TheWelcome.vue +86 -0
  26. package/examples/vue-vite-static/src/components/WelcomeItem.vue +86 -0
  27. package/examples/vue-vite-static/src/components/icons/IconCommunity.vue +7 -0
  28. package/examples/vue-vite-static/src/components/icons/IconDocumentation.vue +7 -0
  29. package/examples/vue-vite-static/src/components/icons/IconEcosystem.vue +7 -0
  30. package/examples/vue-vite-static/src/components/icons/IconSupport.vue +7 -0
  31. package/examples/vue-vite-static/src/components/icons/IconTooling.vue +19 -0
  32. package/examples/vue-vite-static/src/main.js +11 -0
  33. package/examples/vue-vite-static/src/router/index.js +23 -0
  34. package/examples/vue-vite-static/src/views/AboutView.vue +15 -0
  35. package/examples/vue-vite-static/src/views/HomeView.vue +9 -0
  36. package/examples/vue-vite-static/vite.config.js +16 -0
  37. package/jsconfig.json +1 -1
  38. package/lib/build/bundlers/esbuild/esbuild.config.js +12 -0
  39. package/lib/build/bundlers/esbuild/index.js +46 -0
  40. package/lib/build/bundlers/esbuild/plugins/node-polyfills/index.js +147 -0
  41. package/lib/build/bundlers/esbuild/plugins/node-polyfills/node-polyfills-paths.js +124 -0
  42. package/lib/build/bundlers/index.js +2 -2
  43. package/lib/build/bundlers/webpack/index.js +16 -7
  44. package/lib/build/bundlers/webpack/webpack.config.js +5 -25
  45. package/lib/build/dispatcher/dispatcher.js +116 -66
  46. package/lib/constants/framework-initializer.constants.js +51 -0
  47. package/lib/constants/index.js +4 -1
  48. package/lib/constants/messages/build.messages.js +2 -0
  49. package/lib/constants/messages/env.messages.js +5 -3
  50. package/lib/constants/messages/global.messages.js +4 -1
  51. package/lib/env/polyfills/FetchEvent.polyfills.js +13 -0
  52. package/lib/env/polyfills/fetch.polyfills.js +39 -0
  53. package/lib/env/polyfills/index.js +4 -0
  54. package/lib/env/runtime.env.js +57 -63
  55. package/lib/env/server.env.js +32 -76
  56. package/lib/main.js +234 -90
  57. package/lib/notations/namespaces.js +5 -0
  58. package/lib/platform/actions/core/auth.actions.js +1 -1
  59. package/lib/platform/actions/core/propagation.actions.js +8 -5
  60. package/lib/platform/actions/core/storage.actions.js +1 -1
  61. package/lib/platform/actions/function/showFunctionLogs.actions.js +71 -92
  62. package/lib/platform/edgehooks/debugRequest/debugRequest.hooks.js +28 -0
  63. package/lib/platform/edgehooks/debugRequest/index.js +3 -0
  64. package/lib/platform/edgehooks/index.js +4 -1
  65. package/lib/polyfills/FetchEvent.polyfills.js +13 -0
  66. package/lib/polyfills/fetch.polyfills.js +39 -0
  67. package/lib/polyfills/index.js +4 -0
  68. package/lib/presets/custom/angular/deliver/config.js +2 -4
  69. package/lib/presets/custom/angular/deliver/handler.js +12 -5
  70. package/lib/presets/custom/angular/deliver/prebuild.js +8 -11
  71. package/lib/presets/custom/astro/deliver/config.js +2 -4
  72. package/lib/presets/custom/astro/deliver/handler.js +14 -7
  73. package/lib/presets/custom/astro/deliver/prebuild.js +18 -21
  74. package/lib/presets/custom/hexo/deliver/config.js +2 -4
  75. package/lib/presets/custom/hexo/deliver/handler.js +13 -6
  76. package/lib/presets/custom/hexo/deliver/prebuild.js +18 -21
  77. package/lib/presets/custom/next/deliver/config.js +2 -6
  78. package/lib/presets/custom/next/deliver/handler.js +14 -7
  79. package/lib/presets/custom/next/deliver/prebuild.js +37 -40
  80. package/lib/presets/custom/react/deliver/config.js +2 -4
  81. package/lib/presets/custom/react/deliver/handler.js +10 -3
  82. package/lib/presets/custom/react/deliver/prebuild.js +4 -7
  83. package/lib/presets/custom/vue/deliver/config.js +2 -4
  84. package/lib/presets/custom/vue/deliver/handler.js +10 -3
  85. package/lib/presets/custom/vue/deliver/prebuild.js +66 -10
  86. package/lib/presets/default/html/deliver/config.js +1 -4
  87. package/lib/presets/default/html/deliver/handler.js +14 -7
  88. package/lib/presets/default/html/deliver/prebuild.js +3 -2
  89. package/lib/presets/default/javascript/compute/config.js +2 -5
  90. package/lib/presets/default/javascript/compute/handler.js +15 -4
  91. package/lib/presets/default/javascript/compute/prebuild.js +2 -1
  92. package/lib/presets/default/typescript/compute/config.js +10 -0
  93. package/lib/presets/default/typescript/compute/handler.js +16 -0
  94. package/lib/presets/default/typescript/compute/prebuild.js +7 -0
  95. package/lib/providers/azion/worker.js +6 -2
  96. package/lib/utils/exec/exec.utils.js +34 -23
  97. package/lib/utils/feedback/feedback.utils.js +6 -2
  98. package/lib/utils/getAbsoluteLibDirPath/getAbsoluteLibDirPath.utils.js +11 -2
  99. package/lib/utils/getVulcanBuildId/getVulcanBuildId.utils.js +1 -1
  100. package/lib/utils/index.js +4 -2
  101. package/lib/utils/overrideStaticOutputPath/overrideStaticOutputPath.utils.js +2 -2
  102. package/lib/utils/presets/index.js +3 -0
  103. package/lib/utils/presets/presets.utils.js +169 -0
  104. package/lib/utils/spinner/index.js +3 -0
  105. package/lib/utils/spinner/spinner.utils.js +32 -0
  106. package/package.json +17 -4
  107. package/examples/hexo-static/yarn.lock +0 -1625
  108. package/lib/build/polyfills/index.js +0 -0
  109. package/lib/utils/getPresetsList/getPresetsList.utils.js +0 -50
  110. package/lib/utils/getPresetsList/index.js +0 -3
  111. /package/lib/utils/{getPresetsList/getPresetsList.utils.test.js → presets/presets.utils.test.js} +0 -0
package/lib/main.js CHANGED
@@ -1,15 +1,23 @@
1
1
  #! /usr/bin/env node
2
- import path from 'path';
2
+ import { join, resolve } from 'path';
3
+ import { readFileSync } from 'fs';
3
4
  import { Command } from 'commander';
4
5
  import { createPromptModule } from 'inquirer';
5
6
  import { satisfies } from 'semver';
6
- import { feedback, debug } from '#utils';
7
- import { Messages } from '#constants';
7
+ import {
8
+ feedback, debug, getAbsoluteLibDirPath,
9
+ } from '#utils';
10
+ import { Messages, FrameworkInitializer } from '#constants';
8
11
 
9
12
  const MIN_NODE_VERSION = '18.0.0';
10
13
 
14
+ const vulcanLibPath = getAbsoluteLibDirPath();
15
+ const vulcanRootPath = resolve(vulcanLibPath, '..');
16
+ const vulcanPackageJSON = JSON.parse(readFileSync(`${vulcanRootPath}/package.json`, 'utf8'));
17
+ const vulcanVersion = vulcanPackageJSON.version;
18
+
11
19
  const debugEnabled = process.env.DEBUG === 'true';
12
- const version = process.env.npm_package_version;
20
+
13
21
  const program = new Command();
14
22
  const prompt = createPromptModule();
15
23
 
@@ -33,21 +41,231 @@ function validateNodeMinVersion() {
33
41
  * setVulcanEnvironment();
34
42
  */
35
43
  function setVulcanEnvironment() {
36
- const ENV = process.env.AZION_ENV || 'production';
37
- if (!['production', 'stage', 'local'].includes(ENV)) {
44
+ const vulcanContext = {
45
+ env: 'production',
46
+ root: vulcanRootPath,
47
+ package: vulcanPackageJSON,
48
+ debug: debugEnabled,
49
+ version: vulcanVersion,
50
+ };
51
+
52
+ const AZION_ENV = process.env.AZION_ENV || vulcanContext.env;
53
+ if (!['production', 'stage', 'local'].includes(AZION_ENV)) {
38
54
  feedback.error(Messages.env.errors.invalid_environment);
39
55
  process.exit(1);
40
56
  } else {
41
- globalThis.vulcan = { env: ENV };
57
+ vulcanContext.env = AZION_ENV;
42
58
  }
59
+ globalThis.vulcan = vulcanContext;
43
60
  }
44
61
  /**
45
62
  * Starts the command-line interface program.
46
63
  * @example
47
- * startProgram();
64
+ * startVulcanProgram();
48
65
  */
49
66
  function startVulcanProgram() {
50
- program.version(version);
67
+ program.version(vulcanVersion);
68
+
69
+ program
70
+ .command('init')
71
+ .option('--name <project_name>', 'Project name')
72
+ .description('Initialize a new project')
73
+ .action(async (options) => {
74
+ const AVALIABLE_TEMPLATES = Object.keys(FrameworkInitializer);
75
+ let projectName = options.name;
76
+
77
+ const { frameworkChoice } = await prompt([
78
+ {
79
+ type: 'list',
80
+ name: 'frameworkChoice',
81
+ message: 'Choose a template for your project:',
82
+ choices: AVALIABLE_TEMPLATES,
83
+ },
84
+ ]);
85
+
86
+ while (!projectName) {
87
+ // eslint-disable-next-line no-await-in-loop
88
+ const { projectName: inputName } = await prompt([
89
+ {
90
+ type: 'input',
91
+ name: 'projectName',
92
+ message: 'Enter your project name:',
93
+ },
94
+ ]);
95
+
96
+ if (inputName) {
97
+ projectName = inputName;
98
+ }
99
+ if (!inputName) {
100
+ feedback.pending(Messages.info.name_required);
101
+ }
102
+ }
103
+
104
+ const createFrameworkTemplate = FrameworkInitializer[frameworkChoice];
105
+
106
+ if (createFrameworkTemplate) {
107
+ await createFrameworkTemplate(projectName);
108
+ } else {
109
+ feedback.error(Messages.errors.invalid_choice);
110
+ }
111
+ });
112
+
113
+ program
114
+ .command('build')
115
+ .description('Build a project for edge deployment')
116
+ .option(
117
+ '--preset <type>',
118
+ 'Preset of build target (e.g., vue, next, javascript)',
119
+ 'javascript',
120
+ )
121
+ .option(
122
+ '--mode <type>',
123
+ 'Mode of build target (e.g., deliver, compute)',
124
+ 'compute',
125
+ )
126
+ .option(
127
+ '--entry <string>',
128
+ 'Code entrypoint (default: ./main.js)',
129
+ './main.js',
130
+ )
131
+ .option(
132
+ '--useNodePolyfills',
133
+ 'Use node polyfills in build.',
134
+ )
135
+ .action(async ({
136
+ preset, mode, entry, versionId, useNodePolyfills,
137
+ }) => {
138
+ let entryPoint = entry;
139
+
140
+ if (preset === 'javascript') {
141
+ feedback.info('Using main.js as entrypoint by default');
142
+ }
143
+ if (preset === 'typescript') {
144
+ feedback.info('Using main.ts as entrypoint by default');
145
+ entryPoint = './main.ts';
146
+ }
147
+
148
+ const BuildDispatcher = (await import('#build')).default;
149
+ const buildDispatcher = new BuildDispatcher(
150
+ preset,
151
+ mode,
152
+ entryPoint,
153
+ versionId,
154
+ useNodePolyfills,
155
+ );
156
+ await buildDispatcher.run();
157
+ });
158
+
159
+ program
160
+ .command('dev')
161
+ .description('Start local environment')
162
+ .arguments('[file]')
163
+ .option('-p, --port <port>', 'Specify the port', '3000')
164
+ .action(async (file, options) => {
165
+ const { port } = options;
166
+ const parsedPort = parseInt(port, 10);
167
+ const { server } = await import('#env');
168
+ const entryPoint = file || join(process.cwd(), '.edge/worker.js');
169
+ server(entryPoint, parsedPort);
170
+ });
171
+
172
+ program
173
+ .command('presets <type>')
174
+ .description('Create or use defined project presets for Edge')
175
+ .action(async (type) => {
176
+ const { presets } = await import('#utils');
177
+
178
+ let name;
179
+ let mode;
180
+
181
+ switch (type) {
182
+ case 'create':
183
+ // eslint-disable-next-line no-constant-condition
184
+ while (true) {
185
+ // eslint-disable-next-line no-await-in-loop
186
+ const { inputPresetName } = await prompt([
187
+ {
188
+ type: 'input',
189
+ name: 'inputPresetName',
190
+ message: 'Enter the preset name:',
191
+ },
192
+ ]);
193
+
194
+ const presetExists = presets
195
+ .getKeys()
196
+ .map((existingPresetName) => existingPresetName.toLowerCase())
197
+ .includes(inputPresetName.toLowerCase());
198
+
199
+ if (presetExists) {
200
+ feedback.error('A preset with this name already exists.');
201
+ } else if (!inputPresetName) {
202
+ feedback.error('Preset name cannot be empty.');
203
+ } else {
204
+ name = inputPresetName;
205
+ break;
206
+ }
207
+ }
208
+
209
+ // eslint-disable-next-line no-constant-condition
210
+ while (true) {
211
+ // eslint-disable-next-line no-await-in-loop
212
+ const { inputMode } = await prompt([
213
+ {
214
+ type: 'list',
215
+ name: 'inputMode',
216
+ message: 'Choose the mode:',
217
+ choices: ['compute', 'deliver'],
218
+ },
219
+ ]);
220
+
221
+ if (['compute', 'deliver'].includes(inputMode)) {
222
+ mode = inputMode;
223
+ break;
224
+ } else {
225
+ feedback.error('Invalid mode. Choose either "compute" or "deliver".');
226
+ }
227
+ }
228
+
229
+ try {
230
+ presets.set(name, mode);
231
+ feedback.success(`${name}(${mode}) created with success!`);
232
+ feedback.info(`Now open './lib/presets/${name}/${mode}' and work on your preset.`);
233
+ } catch (error) {
234
+ debug.error(error);
235
+ feedback.error(Messages.errors.folder_creation_failed(name));
236
+ }
237
+ break;
238
+
239
+ case 'ls':
240
+ presets.getBeautify().forEach((preset) => feedback.option(preset));
241
+ break;
242
+
243
+ default:
244
+ feedback.error('Invalid argument provided.');
245
+ break;
246
+ }
247
+ });
248
+
249
+ program
250
+ .command('logs <type> [id]')
251
+ .description('Perform operations on function or application logs')
252
+ .option('-w, --watch', 'Show real-time logs')
253
+ .action(async (type, id, options) => {
254
+ const { functions } = await import('#platform');
255
+ const { watch } = options;
256
+
257
+ if (!['function', 'application'].includes(type)) {
258
+ feedback.error(Messages.platform.logs.errors.invalid_log_type);
259
+ return;
260
+ }
261
+
262
+ if (type === 'function') {
263
+ functions.actions.showFunctionLogs(id, watch);
264
+ }
265
+ if (type === 'application') {
266
+ feedback.info(Messages.platform.logs.info.unsupported_log_type);
267
+ }
268
+ });
51
269
 
52
270
  if (debugEnabled) {
53
271
  program
@@ -120,51 +338,17 @@ function startVulcanProgram() {
120
338
  }
121
339
  });
122
340
 
123
- program
124
- .command('run')
125
- .description('Run Edge Function')
126
- .arguments('<file>')
127
- .option('-p, --port <port>', 'Specify the port', '3000')
128
- .action(async (file, options) => {
129
- const { port } = options;
130
- const parsedPort = parseInt(port, 10);
131
- const { server } = await import('#env');
132
- const entryPoint = file || path.join(process.cwd(), '.edge/woker.js');
133
- server(entryPoint, parsedPort);
134
- });
135
-
136
- program
137
- .command('logs <type> [id]')
138
- .description('Perform operations on function or application logs')
139
- .option('-w, --watch', 'Show real-time logs')
140
- .action(async (type, id, options) => {
141
- const { functions } = await import('#platform');
142
- const { watch } = options;
143
-
144
- if (!['function', 'application'].includes(type)) {
145
- feedback.error(Messages.platform.logs.errors.invalid_log_type);
146
- return;
147
- }
148
-
149
- if (type === 'function') {
150
- functions.actions.showFunctionLogs(id, watch);
151
- }
152
- if (type === 'application') {
153
- feedback.info(Messages.platform.logs.info.unsupported_log_type);
154
- }
155
- });
156
-
157
341
  program
158
342
  .command('storage sync')
159
343
  .description(
160
- 'Synchronize local .edge/statics with the Edge Function storage',
344
+ 'Synchronize local .edge/storage with the Edge Function storage',
161
345
  )
162
346
  .action(async () => {
163
347
  const { core } = await import('#platform');
164
348
  const { getVulcanBuildId } = await import('#utils');
165
349
 
166
350
  const versionId = getVulcanBuildId();
167
- const basePath = path.join(process.cwd(), '.edge/statics/');
351
+ const basePath = join(process.cwd(), '.edge/storage/');
168
352
  await core.actions.uploadStatics(versionId, basePath);
169
353
  });
170
354
 
@@ -176,7 +360,7 @@ function startVulcanProgram() {
176
360
  const { getVulcanBuildId } = await import('#utils');
177
361
 
178
362
  const versionId = getVulcanBuildId();
179
- const staticsPath = path.join(process.cwd(), '/.edge/statics');
363
+ const staticsPath = join(process.cwd(), '/.edge/storage');
180
364
 
181
365
  const answers = await prompt([
182
366
  {
@@ -201,49 +385,6 @@ function startVulcanProgram() {
201
385
  });
202
386
  }
203
387
 
204
- program
205
- .command('build')
206
- .description('Build a project for edge deployment')
207
- .option(
208
- '--preset <type>',
209
- 'Preset of build target (e.g., vue, next, javascript)',
210
- 'javascript',
211
- )
212
- .option(
213
- '--mode <type>',
214
- 'Mode of build target (e.g., deliver, compute)',
215
- 'compute',
216
- )
217
- .option(
218
- '--entry <string>',
219
- 'Code entrypoint (default: ./main.js)',
220
- './main.js',
221
- )
222
- .action(async ({
223
- preset, mode, entry, versionId,
224
- }) => {
225
- // TODO: generate versionID in dispatcher (currently generated for webpackconfig)
226
- const BuildDispatcher = (await import('#build')).default;
227
- const buildDispatcher = new BuildDispatcher(
228
- preset,
229
- mode,
230
- entry,
231
- versionId,
232
- );
233
- await buildDispatcher.run();
234
- });
235
-
236
- program
237
- .command('presets <type>')
238
- .description('Create or use defined project presets for Edge.')
239
- .action(async (type) => {
240
- const { getPresetsList } = await import('#utils');
241
- const presets = getPresetsList();
242
-
243
- if (type === 'ls') {
244
- presets.forEach((preset) => feedback.option(preset));
245
- }
246
- });
247
388
  program.parse(process.argv);
248
389
  }
249
390
 
@@ -251,10 +392,13 @@ try {
251
392
  if (validateNodeMinVersion()) {
252
393
  setVulcanEnvironment();
253
394
  startVulcanProgram();
254
- } else {
395
+ }
396
+ if (!validateNodeMinVersion()) {
255
397
  feedback.error(Messages.errors.invalid_node_version(MIN_NODE_VERSION));
398
+ process.exit(1);
256
399
  }
257
400
  } catch (error) {
258
401
  feedback.error(Messages.errors.unknown_error);
259
402
  debug.error(error);
403
+ process.exit(1);
260
404
  }
@@ -28,3 +28,8 @@
28
28
  * @namespace edge
29
29
  * @description Contains imported and computer-ready functions for use on the edge.
30
30
  */
31
+
32
+ /**
33
+ * @namespace polyfills
34
+ * @description Contains polyfills that are used in the local environment and in build.
35
+ */
@@ -45,7 +45,7 @@ async function auth(loginOption, credentials) {
45
45
  }
46
46
  if (responseJSON.token) {
47
47
  await vulcan.createVulcanEnv('API_TOKEN', responseJSON.token);
48
- feedback.success(Messages.platform.auth.success.api_auth_success);
48
+ feedback.success(Messages.platform.auth.success.auth_success);
49
49
  }
50
50
  }
51
51
  if (loginOption === 'token') {
@@ -1,6 +1,8 @@
1
1
  import { AzionEdges, Messages } from '#constants';
2
- import { feedback, debug } from '#utils';
3
- import open from 'open';
2
+ import { feedback, debug, exec } from '#utils';
3
+
4
+ const isWindows = process.platform === 'win32';
5
+
4
6
  /**
5
7
  * @function
6
8
  * @memberof platform
@@ -38,13 +40,14 @@ function watchPropagation(domain) {
38
40
  clearInterval(intervalId);
39
41
 
40
42
  let remainingEdgeIndex = currentEdgeIndex + 1;
41
- const remainingEdgesIntervalId = setInterval(() => {
43
+ const remainingEdgesIntervalId = setInterval(async () => {
42
44
  if (remainingEdgeIndex >= AzionEdges.length) {
43
45
  clearInterval(remainingEdgesIntervalId);
44
46
  f.interactive.complete(
45
47
  Messages.platform.propagation.success.propagation_complete,
46
48
  );
47
- open(`https://${domain}`);
49
+ await exec(`${isWindows ? 'start ' : 'open '} https://${domain} `);
50
+
48
51
  process.exit(0);
49
52
  } else {
50
53
  const remainingEdge = AzionEdges[remainingEdgeIndex];
@@ -72,4 +75,4 @@ function watchPropagation(domain) {
72
75
  }
73
76
 
74
77
  // eslint-disable-next-line import/prefer-default-export
75
- export { watchPropagation };
78
+ export { watchPropagation };
@@ -43,7 +43,7 @@ async function uploadStatics(versionId, basePath, currentPath = '') {
43
43
  if (fileStat.isFile()) {
44
44
  const fileContent = await promisify(fs.readFile)(fullFilePath, 'utf8');
45
45
  const mimeType = mime.lookup(fullFilePath);
46
- const staticPath = path.join(currentPath, filePath);
46
+ const staticPath = path.join(currentPath, filePath).replace(/\\/g, '/');
47
47
 
48
48
  try {
49
49
  const response = await StorageService.upload(versionId, fileContent, staticPath, mimeType);
@@ -5,36 +5,19 @@ import eventsService from '../../services/events.service.js';
5
5
  /**
6
6
  *
7
7
  * @param {object} log - View logs organized in a box.
8
- * @param {boolean} [watch=false] - Whether to observe logs in real time.
9
8
  */
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)} ║`);
9
+ function displayLogInBox(log) {
10
+ log.logLines.forEach((line) => {
11
+ try {
12
+ const trimmedLine = line.trim();
13
+ const parsedJSON = JSON.parse(trimmedLine);
14
+ const formattedJSON = JSON.stringify(parsedJSON, null, 2);
15
+ feedback.logs(log.functionId, log.eventId, log.date.toLocaleString()).debug(`\n${formattedJSON}`);
16
+ } catch (error) {
17
+ console.log(error);
18
+ feedback.logs(`Function ID: ${log.functionId}`, log.eventId, log.date.toLocaleString()).debug(`\n${line}`);
19
+ }
32
20
  });
33
- console.log(`╚${horizontalLine}╝`);
34
-
35
- if (watch) {
36
- console.log(Messages.platform.logs.info.watch_true);
37
- }
38
21
  }
39
22
 
40
23
  /**
@@ -60,90 +43,86 @@ function displayLogInBox(log, watch = false) {
60
43
  * showFunctionLogs();
61
44
  */
62
45
  async function showFunctionLogs(functionId, watch = false) {
63
- let previousConsoleLogId = null;
46
+ let watchStartTime = new Date();
47
+ let logsToDisplay = {};
64
48
 
65
49
  /**
66
50
  * Mounts the structure for a console log.
67
51
  * @param {object} azionLog - The Azion log event.
68
- * @param {string} azionLog.id - The ID of the event.
52
+ * @param {string} azionLog.id - The ID of the event.
69
53
  * @param {string} azionLog.ts - The timestamp of the event.
70
54
  * @returns {object} - The log event structure.
71
55
  * @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.
56
+ * @property {string} functionId - The ID of the function.
57
+ * @property {Date} date - The date object of the event.
58
+ * @property {string[]} logLines - The array of log lines.
74
59
  */
75
- const mountEventLogStructure = (azionLog) => ({
76
- eventId: azionLog.id,
77
- functionId: azionLog.functionId,
78
- date: new Date(azionLog.ts).toLocaleString(),
79
- logs: [],
80
- });
60
+ function mountEventLogStructure(azionLog) {
61
+ return {
62
+ eventId: azionLog.id,
63
+ functionId: azionLog.functionId,
64
+ date: new Date(azionLog.ts),
65
+ logLines: [],
66
+ };
67
+ }
81
68
 
82
- /**
83
- * Internal function for performing polling and retrieving function logs.
84
- * @returns {void}
85
- */
86
- const startPoll = async () => {
87
- try {
69
+ try {
70
+ const getLogs = async () => {
88
71
  const response = await eventsService.getFunctionsLogs(functionId);
89
- const responseJSON = await response.json();
90
- const { cellsConsoleEvents } = responseJSON.data;
91
- const requestSuccess = !!cellsConsoleEvents;
92
72
 
93
- if (!requestSuccess) {
94
- throw new Error(JSON.stringify(responseJSON));
95
- }
96
- if (requestSuccess) {
97
- const nextConsoleLog = cellsConsoleEvents.length > 0 ? cellsConsoleEvents[0] : null;
73
+ if (response.status === 200) {
74
+ const responseJSON = await response.json();
75
+
76
+ const { cellsConsoleEvents } = responseJSON.data;
77
+ const requestSuccess = !!cellsConsoleEvents;
98
78
 
99
- if (!nextConsoleLog) {
100
- feedback.info(Messages.platform.logs.info.no_logs);
79
+ if (!requestSuccess) {
80
+ throw new Error(JSON.stringify(responseJSON));
101
81
  }
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
- }
82
+
83
+ if (watch) {
84
+ if (watch) {
85
+ feedback.watch(Messages.platform.logs.info.watch_true);
134
86
  }
87
+ logsToDisplay = {}; // Clear logsToDisplay on each poll when in watch mode
135
88
  }
89
+
90
+ cellsConsoleEvents.forEach((event) => {
91
+ const { id, line, ts } = event;
92
+ const eventDate = new Date(ts);
93
+
94
+ if (watch && eventDate < watchStartTime) {
95
+ return; // Skip logs that are older than watchStartTime
96
+ }
97
+
98
+ if (!logsToDisplay[id]) {
99
+ logsToDisplay[id] = mountEventLogStructure(event);
100
+ }
101
+
102
+ logsToDisplay[id].logLines.push(line);
103
+ logsToDisplay[id].date = eventDate;
104
+ });
105
+
106
+ Object.values(logsToDisplay).forEach((log) => {
107
+ displayLogInBox(log, watch);
108
+ });
109
+
110
+ watchStartTime = new Date();
136
111
  }
137
112
 
138
113
  if (watch) {
139
- setTimeout(startPoll, 500);
114
+ setTimeout(getLogs, 3000);
140
115
  }
141
- } catch (error) {
142
- debug.error(error);
143
- }
144
- };
116
+ };
145
117
 
146
- startPoll();
118
+ getLogs();
119
+ } catch (error) {
120
+ debug.error(error);
121
+ }
147
122
  }
148
123
 
124
+ process.on('exit', () => {
125
+ feedback.interactive.breakInteractiveChain();
126
+ });
127
+
149
128
  export default showFunctionLogs;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @function
3
+ * @name DebugRequest
4
+ * @memberof edge
5
+ * @description
6
+ * The `DebugRequest` function is used to debug and display the details of an incoming request.
7
+ * It takes the request as a parameter and logs the request URL, method, and headers to the console.
8
+ * @param {FetchEvent} event - The incoming FetchEvent object.
9
+ * @returns {Promise<void>} A promise that resolves once the request details are logged.
10
+ */
11
+ async function DebugRequest(event) {
12
+ const { request } = event;
13
+ const headers = new Headers(request.headers);
14
+
15
+ const requestData = {
16
+ url: request.url,
17
+ method: request.method,
18
+ headers: {},
19
+ };
20
+
21
+ headers.forEach((value, name) => {
22
+ requestData.headers[name] = value;
23
+ });
24
+
25
+ return event.console.log(requestData);
26
+ }
27
+
28
+ export default DebugRequest;
@@ -0,0 +1,3 @@
1
+ import debugRequest from './debugRequest.hooks.js';
2
+
3
+ export default debugRequest;