edge-functions 2.2.0-stage.2 → 2.2.0-stage.3

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/.prettierignore CHANGED
@@ -2,4 +2,5 @@ examples/*
2
2
  jsdoc/*
3
3
  yarn.lock
4
4
  CHANGELOG.md
5
- docker-compose.yml
5
+ docker-compose.yml
6
+ CODEOWNERS
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [2.2.0-stage.3](https://github.com/aziontech/vulcan/compare/v2.2.0-stage.2...v2.2.0-stage.3) (2023-12-13)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * loop in fs polyfill when readdirSync and path / ([4c1e2d0](https://github.com/aziontech/vulcan/commit/4c1e2d0195eaf04bf86ce4995ac41c9e2de5c596))
7
+ * loop in fs polyfill when readdirSync and path / ([#197](https://github.com/aziontech/vulcan/issues/197)) ([bd56c75](https://github.com/aziontech/vulcan/commit/bd56c75d8e48dd8af7792710aa51f68d7f737521))
8
+
1
9
  ## [2.2.0-stage.2](https://github.com/aziontech/vulcan/compare/v2.2.0-stage.1...v2.2.0-stage.2) (2023-12-06)
2
10
 
3
11
 
package/CODEOWNERS CHANGED
@@ -1,2 +1 @@
1
- * @jotanarciso
2
- * @MagnunAVFAzion
1
+ * @jotanarciso @MagnunAVFAzion @enicio @jcbsfilho @tiagokrebs
@@ -294,7 +294,7 @@ function getOptions(options, defaultOptions = kEmptyObject) {
294
294
  /**
295
295
  * Closes the file descriptor.
296
296
  * @param {number} fd
297
- * @param {(err?: Error) => any} [callback]
297
+ * @param {function(Error): any} [callback]
298
298
  * @returns {void}
299
299
  */
300
300
  function close(fd, callback = defaultCloseCallback) {
@@ -316,12 +316,9 @@ function closeSync(fd) {
316
316
  /**
317
317
  * Asynchronously opens a file.
318
318
  * @param {string | Buffer | URL} path
319
- * @param {string | number} [flags]
320
- * @param {string | number} [mode]
321
- * @param {(
322
- * err?: Error,
323
- * fd?: number
324
- * ) => any} callback
319
+ * @param {string | number} [flags='r']
320
+ * @param {string | number} [mode=0o666]
321
+ * @param {function(Error, number): any} [callback]
325
322
  * @returns {void}
326
323
  */
327
324
  function open(path, flags, mode, callback) {
@@ -382,11 +379,9 @@ function openSync(path, flags, mode) {
382
379
  /**
383
380
  * Asynchronously gets the stats of a file.
384
381
  * @param {string | Buffer | URL} path
385
- * @param {{ bigint?: boolean; }} [options]
386
- * @param {(
387
- * err?: Error,
388
- * stats?: any
389
- * ) => any} callback
382
+ * @param {Object} [options] - Options object.
383
+ * @param {boolean} [options.bigint=false]
384
+ * @param {function(Error, any): any} [callback]
390
385
  * @returns {void}
391
386
  */
392
387
  async function stat(path, options = { bigint: false }, callback) {
@@ -426,14 +421,13 @@ async function stat(path, options = { bigint: false }, callback) {
426
421
  * Synchronously retrieves the `fs.Stats`
427
422
  * for the `path`.
428
423
  * @param {string | Buffer | URL} path
429
- * @param {{
430
- * bigint?: boolean;
431
- * throwIfNoEntry?: boolean;
432
- * }} [options]
424
+ * @param {Object} [options]
425
+ * @param {boolean} [options.bigint]
426
+ * @param {boolean} [options.throwIfNoEntry]
433
427
  * @returns {any}
434
428
  */
435
429
  function statSync(path, options) {
436
- // checks final /
430
+ // checks final
437
431
  path = getValidatedPath(path);
438
432
 
439
433
  // Synchronous method to get file information
@@ -463,15 +457,11 @@ function statSync(path, options) {
463
457
  /**
464
458
  * Asynchronously reads the entire contents of a file.
465
459
  * @param {string | Buffer | URL | number} path
466
- * @param {{
467
- * encoding?: string | null;
468
- * flag?: string;
469
- * signal?: AbortSignal;
470
- * } | string} [options]
471
- * @param {(
472
- * err?: Error,
473
- * data?: string | Buffer
474
- * ) => any} callback
460
+ * @param {Object | string} [options] - Options object or encoding string.
461
+ * @param {string} [options.encoding] - The file encoding.
462
+ * @param {string} [options.flag] - The flag.
463
+ * @param {AbortSignal} [options.signal] - The signal.
464
+ * @param {function(Error, (string | Buffer))} callback - Callback function.
475
465
  * @returns {void}
476
466
  */
477
467
  function readFile(path, options, callback) {
@@ -507,10 +497,9 @@ function readFile(path, options, callback) {
507
497
  /**
508
498
  * Synchronously reads the entire contents of a file.
509
499
  * @param {string | Buffer | URL | number} path
510
- * @param {{
511
- * encoding?: string | null;
512
- * flag?: string;
513
- * }} [options]
500
+ * @param {Object | string} [options] - Options object or encoding string.
501
+ * @param {string} [options.encoding] - The file encoding.
502
+ * @param {string} [options.flag] - The flag.
514
503
  * @returns {string | Buffer}
515
504
  */
516
505
  function readFileSync(path, options) {
@@ -537,13 +526,10 @@ function readFileSync(path, options) {
537
526
 
538
527
  /**
539
528
  * Synchronously reads the contents of a directory.
540
- * @param {string | Buffer | URL} path
541
- * @param {string | {
542
- * encoding?: string;
543
- * withFileTypes?: boolean;
544
- * recursive?: boolean;
545
- * }} [options]
546
- * @returns {string | Buffer[] | Dirent[]}
529
+ * @param {string | Object} [options] - Options object or encoding string.
530
+ * @param {string} [options.encoding] - The encoding.
531
+ * @param {boolean} [options.withFileTypes] - Whether to include file types.
532
+ * @param {boolean} [options.recursive] - Whether to include subdirectories.
547
533
  */
548
534
  function readdirSync(path, options) {
549
535
  path = getValidatedPath(path);
@@ -563,14 +549,20 @@ function readdirSync(path, options) {
563
549
  const matchedElements = filesInfos.paths.filter(
564
550
  (dir) => dir.startsWith(path) && path !== dir,
565
551
  );
566
- const elementsInDir = [
567
- ...new Set(
568
- matchedElements.map(
569
- (element) => element.replace(`${path}/`, '').split('/')[0],
552
+ let elementsInDir;
553
+ if (path === '/') {
554
+ elementsInDir = matchedElements.filter(
555
+ (element) => !element.substring(1).includes('/'),
556
+ );
557
+ } else {
558
+ elementsInDir = [
559
+ ...new Set(
560
+ matchedElements.map(
561
+ (element) => element.replace(`${path}/`, '').split('/')[0],
562
+ ),
570
563
  ),
571
- ),
572
- ];
573
-
564
+ ];
565
+ }
574
566
  // generate the list of elements in dir (strings or Dirents)
575
567
  if (options.withFileTypes) {
576
568
  result = elementsInDir.map((element) => {
@@ -617,5 +609,4 @@ export {
617
609
  readdirSync,
618
610
  promises,
619
611
  };
620
-
621
612
  /* eslint-enable */
@@ -27,39 +27,49 @@ async function directoryExists(dirPath) {
27
27
  * Then, it will upload static files and deploy the application.
28
28
  * Finally, it watches for the propagation of the deployment.
29
29
  * @returns {Promise<void>} - A promise that resolves when the deployment is complete.
30
+ * @param {string} functionID - Function ID will be updated.
30
31
  * @example
31
32
  *
32
33
  * deployCommand();
33
34
  */
34
- async function deployCommand() {
35
- const { core } = await import('#platform');
35
+ async function deployCommand(functionID) {
36
+ const { core, functions } = await import('#platform');
36
37
  const { getVulcanBuildId } = await import('#utils');
37
38
 
38
39
  const versionId = getVulcanBuildId();
39
40
  const staticsPath = join(process.cwd(), '/.edge/storage');
40
41
 
41
- const answers = await prompt([
42
- {
43
- type: 'input',
44
- name: 'applicationName',
45
- message:
46
- 'Enter the name of the application (optional, leave empty for random name):',
47
- },
48
- {
49
- type: 'input',
50
- name: 'functionName',
51
- message:
52
- 'Enter the name of the function (optional, leave empty for random name):',
53
- },
54
- ]);
42
+ if (functionID) {
43
+ if (await directoryExists(staticsPath)) {
44
+ await core.actions.uploadStatics(versionId, staticsPath);
45
+ }
46
+ await functions.actions.updateFunction(functionID);
47
+ }
48
+ if (!functionID) {
49
+ const answers = await prompt([
50
+ {
51
+ type: 'input',
52
+ name: 'applicationName',
53
+ message:
54
+ 'Enter the name of the application (optional, leave empty for random name):',
55
+ },
56
+ {
57
+ type: 'input',
58
+ name: 'functionName',
59
+ message:
60
+ 'Enter the name of the function (optional, leave empty for random name):',
61
+ },
62
+ ]);
63
+
64
+ const { applicationName, functionName } = answers;
55
65
 
56
- const { applicationName, functionName } = answers;
66
+ if (await directoryExists(staticsPath)) {
67
+ await core.actions.uploadStatics(versionId, staticsPath);
68
+ }
57
69
 
58
- if (await directoryExists(staticsPath)) {
59
- await core.actions.uploadStatics(versionId, staticsPath);
70
+ const domain = await core.actions.deploy(applicationName, functionName);
71
+ core.actions.watchPropagation(domain);
60
72
  }
61
- const domain = await core.actions.deploy(applicationName, functionName);
62
- core.actions.watchPropagation(domain);
63
73
  }
64
74
 
65
75
  export default deployCommand;
package/lib/main.js CHANGED
@@ -151,9 +151,10 @@ function startVulcanProgram() {
151
151
  program
152
152
  .command('deploy')
153
153
  .description('Create and deploy an application with a function')
154
- .action(async () => {
154
+ .option('--functionID <id>', 'Function ID to deploy')
155
+ .action(async (options) => {
155
156
  const { deployCommand } = await import('#commands');
156
- await deployCommand();
157
+ await deployCommand(options?.functionID);
157
158
  });
158
159
  }
159
160
 
@@ -0,0 +1,83 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ import { debug } from '#utils';
5
+ import { Platform } from '#namespaces';
6
+ import FunctionService from '../../services/function.service.js';
7
+
8
+ /**
9
+ * @function
10
+ * @memberof Platform
11
+ * Get the initiator type based on the code signature.
12
+ * @param {string} code - The code for the function.
13
+ * @returns {string} The initiator type.
14
+ */
15
+ function getInitiatorType(code) {
16
+ const fetchSignature = "addEventListener('fetch'";
17
+ const firewallSignature = "addEventListener('firewall'";
18
+
19
+ if (code.includes(fetchSignature)) {
20
+ return 'edge_application';
21
+ }
22
+ if (code.includes(firewallSignature)) {
23
+ return 'edge_firewall';
24
+ }
25
+ return 'edge_application';
26
+ }
27
+
28
+ /**
29
+ * @function
30
+ * @memberof Platform
31
+ * @description Updates an existing function (worker) for the edge application.
32
+ * It reads the function code and arguments from the respective '.edge/worker.js'
33
+ * and '.edge/args.json' files, and infers the initiator type based on the function's code.
34
+ * @param {string} [functionId] - The id of the function to be updated.
35
+ * If not provided, the id is determined from the function's code.
36
+ * @returns {Promise<object>} A promise that resolves with the details of the updated
37
+ * function. The object returned contains details such as the function ID, name, code,
38
+ * language, initiator type, arguments, and activation status.
39
+ * @throws {Error} If the function update fails, possibly due to invalid code or
40
+ * function arguments, or due to a problem with the FunctionService.
41
+ * @example
42
+ * try {
43
+ * const functionDetails = await updateFunction('myFunctionId');
44
+ * console.log(`Function updated with ID: ${functionDetails.id}`);
45
+ * } catch (error) {
46
+ * console.error('Failed to update function:', error);
47
+ * }
48
+ */
49
+ async function updateFunction(functionId) {
50
+ try {
51
+ // get code
52
+ const workerFilePath = path.join(process.cwd(), '.edge/worker.js');
53
+ const code = fs.readFileSync(workerFilePath, 'utf8');
54
+
55
+ // get json_args
56
+ const argsFilePath = path.join(process.cwd(), '.edge/args.json');
57
+ let jsonArgs = {};
58
+ if (fs.existsSync(argsFilePath)) {
59
+ const argsData = fs.readFileSync(argsFilePath, 'utf8');
60
+ jsonArgs = JSON.parse(argsData);
61
+ }
62
+
63
+ // get initiator_type
64
+ const initiatorType = getInitiatorType(code);
65
+
66
+ const payload = {
67
+ code,
68
+ initiator_type: initiatorType,
69
+ json_args: jsonArgs,
70
+ active: true,
71
+ };
72
+
73
+ const response = await (
74
+ await FunctionService.update(functionId, payload)
75
+ ).json();
76
+ return response.results;
77
+ } catch (error) {
78
+ debug.error(error);
79
+ throw error;
80
+ }
81
+ }
82
+
83
+ export default updateFunction;
@@ -14,6 +14,7 @@ import setFunctionAsDefaultRule from './actions/application/setFunctionAsDefault
14
14
 
15
15
  // function
16
16
  import showFunctionLogs from './actions/function/showFunctionLogs.actions.js';
17
+ import updateFunction from './actions/function/updateFunction.actions.js';
17
18
 
18
19
  /**
19
20
  * Object containing core-related actions.
@@ -58,9 +59,10 @@ const domains = {
58
59
  * @typedef {object} FunctionsObject
59
60
  * @property {object} actions - Object containing Azion function actions.
60
61
  * @property {showFunctionLogs} actions.showFunctionLogs - Action to perform Azion function logs.
62
+ * @property {updateFunction} actions.updateFunction - Action to update Azion function.
61
63
  */
62
64
  const functions = {
63
- actions: { showFunctionLogs },
65
+ actions: { showFunctionLogs, updateFunction },
64
66
  };
65
67
 
66
68
  export { core, applications, domains, functions };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "edge-functions",
3
3
  "type": "module",
4
- "version": "2.2.0-stage.2",
4
+ "version": "2.2.0-stage.3",
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": {