@factorialco/fcode-cli 1.1.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 (234) hide show
  1. package/README.md +1021 -0
  2. package/bin/dev +17 -0
  3. package/bin/dev.cmd +3 -0
  4. package/bin/run +7 -0
  5. package/bin/run.cmd +3 -0
  6. package/dist/commands/add.d.ts +6 -0
  7. package/dist/commands/add.js +22 -0
  8. package/dist/commands/clone.d.ts +26 -0
  9. package/dist/commands/clone.js +177 -0
  10. package/dist/commands/dependencies/add.d.ts +9 -0
  11. package/dist/commands/dependencies/add.js +13 -0
  12. package/dist/commands/dependencies/install.d.ts +14 -0
  13. package/dist/commands/dependencies/install.js +44 -0
  14. package/dist/commands/dependencies/pull.d.ts +12 -0
  15. package/dist/commands/dependencies/pull.js +14 -0
  16. package/dist/commands/dependencies/push.d.ts +12 -0
  17. package/dist/commands/dependencies/push.js +14 -0
  18. package/dist/commands/dependencies/reset.d.ts +9 -0
  19. package/dist/commands/dependencies/reset.js +13 -0
  20. package/dist/commands/dependencies/status.d.ts +9 -0
  21. package/dist/commands/dependencies/status.js +13 -0
  22. package/dist/commands/http.d.ts +22 -0
  23. package/dist/commands/http.js +272 -0
  24. package/dist/commands/login.d.ts +18 -0
  25. package/dist/commands/login.js +62 -0
  26. package/dist/commands/logout.d.ts +10 -0
  27. package/dist/commands/logout.js +34 -0
  28. package/dist/commands/modules/add.d.ts +9 -0
  29. package/dist/commands/modules/add.js +13 -0
  30. package/dist/commands/modules/pull.d.ts +13 -0
  31. package/dist/commands/modules/pull.js +43 -0
  32. package/dist/commands/modules/push.d.ts +13 -0
  33. package/dist/commands/modules/push.js +35 -0
  34. package/dist/commands/modules/remove.d.ts +9 -0
  35. package/dist/commands/modules/remove.js +19 -0
  36. package/dist/commands/modules/reset.d.ts +9 -0
  37. package/dist/commands/modules/reset.js +13 -0
  38. package/dist/commands/modules/status.d.ts +11 -0
  39. package/dist/commands/modules/status.js +40 -0
  40. package/dist/commands/pre-debug.d.ts +20 -0
  41. package/dist/commands/pre-debug.js +120 -0
  42. package/dist/commands/process-metadata.d.ts +18 -0
  43. package/dist/commands/process-metadata.js +63 -0
  44. package/dist/commands/processes/add.d.ts +9 -0
  45. package/dist/commands/processes/add.js +13 -0
  46. package/dist/commands/processes/install-dependencies.d.ts +14 -0
  47. package/dist/commands/processes/install-dependencies.js +30 -0
  48. package/dist/commands/processes/pull.d.ts +13 -0
  49. package/dist/commands/processes/pull.js +47 -0
  50. package/dist/commands/processes/push.d.ts +13 -0
  51. package/dist/commands/processes/push.js +39 -0
  52. package/dist/commands/processes/remove.d.ts +9 -0
  53. package/dist/commands/processes/remove.js +19 -0
  54. package/dist/commands/processes/reset.d.ts +9 -0
  55. package/dist/commands/processes/reset.js +13 -0
  56. package/dist/commands/processes/status.d.ts +11 -0
  57. package/dist/commands/processes/status.js +58 -0
  58. package/dist/commands/pull.d.ts +9 -0
  59. package/dist/commands/pull.js +32 -0
  60. package/dist/commands/push.d.ts +9 -0
  61. package/dist/commands/push.js +29 -0
  62. package/dist/commands/remote/add.d.ts +10 -0
  63. package/dist/commands/remote/add.js +32 -0
  64. package/dist/commands/remote/list.d.ts +7 -0
  65. package/dist/commands/remote/list.js +33 -0
  66. package/dist/commands/remote/set.d.ts +10 -0
  67. package/dist/commands/remote/set.js +28 -0
  68. package/dist/commands/reset.d.ts +6 -0
  69. package/dist/commands/reset.js +19 -0
  70. package/dist/commands/run.d.ts +16 -0
  71. package/dist/commands/run.js +56 -0
  72. package/dist/commands/runtime-env.d.ts +12 -0
  73. package/dist/commands/runtime-env.js +42 -0
  74. package/dist/commands/settings/get.d.ts +12 -0
  75. package/dist/commands/settings/get.js +67 -0
  76. package/dist/commands/settings/remove.d.ts +9 -0
  77. package/dist/commands/settings/remove.js +35 -0
  78. package/dist/commands/settings/set.d.ts +10 -0
  79. package/dist/commands/settings/set.js +39 -0
  80. package/dist/commands/setup-debug.d.ts +12 -0
  81. package/dist/commands/setup-debug.js +118 -0
  82. package/dist/commands/status.d.ts +6 -0
  83. package/dist/commands/status.js +22 -0
  84. package/dist/commands/team/pull.d.ts +8 -0
  85. package/dist/commands/team/pull.js +23 -0
  86. package/dist/commands/team/push.d.ts +11 -0
  87. package/dist/commands/team/push.js +41 -0
  88. package/dist/commands/team/status.d.ts +8 -0
  89. package/dist/commands/team/status.js +40 -0
  90. package/dist/commands/test/scaffold.d.ts +9 -0
  91. package/dist/commands/test/scaffold.js +61 -0
  92. package/dist/commands/test.d.ts +13 -0
  93. package/dist/commands/test.js +72 -0
  94. package/dist/commands/variables/add.d.ts +12 -0
  95. package/dist/commands/variables/add.js +21 -0
  96. package/dist/commands/variables/pull.d.ts +12 -0
  97. package/dist/commands/variables/pull.js +14 -0
  98. package/dist/commands/variables/push.d.ts +12 -0
  99. package/dist/commands/variables/push.js +14 -0
  100. package/dist/commands/variables/remove.d.ts +9 -0
  101. package/dist/commands/variables/remove.js +19 -0
  102. package/dist/commands/variables/reset.d.ts +9 -0
  103. package/dist/commands/variables/reset.js +13 -0
  104. package/dist/commands/variables/status.d.ts +12 -0
  105. package/dist/commands/variables/status.js +58 -0
  106. package/dist/config/global.d.ts +15 -0
  107. package/dist/config/global.js +77 -0
  108. package/dist/config/index.d.ts +14 -0
  109. package/dist/config/index.js +44 -0
  110. package/dist/config/remote.d.ts +15 -0
  111. package/dist/config/remote.js +60 -0
  112. package/dist/config/settings.d.ts +16 -0
  113. package/dist/config/settings.js +54 -0
  114. package/dist/config/workspace.d.ts +28 -0
  115. package/dist/config/workspace.js +158 -0
  116. package/dist/core/commands/api-client-command.d.ts +6 -0
  117. package/dist/core/commands/api-client-command.js +30 -0
  118. package/dist/core/commands/base-command.d.ts +11 -0
  119. package/dist/core/commands/base-command.js +61 -0
  120. package/dist/core/commands/dependency-command.d.ts +5 -0
  121. package/dist/core/commands/dependency-command.js +16 -0
  122. package/dist/core/commands/modules-command.d.ts +7 -0
  123. package/dist/core/commands/modules-command.js +18 -0
  124. package/dist/core/commands/processes-command.d.ts +8 -0
  125. package/dist/core/commands/processes-command.js +27 -0
  126. package/dist/core/commands/resource-command.d.ts +24 -0
  127. package/dist/core/commands/resource-command.js +92 -0
  128. package/dist/core/commands/variables-command.d.ts +5 -0
  129. package/dist/core/commands/variables-command.js +16 -0
  130. package/dist/core/dependencies/env-dependencies-command.d.ts +19 -0
  131. package/dist/core/dependencies/env-dependencies-command.js +70 -0
  132. package/dist/core/dependencies/env-process-dependencies-command.d.ts +15 -0
  133. package/dist/core/dependencies/env-process-dependencies-command.js +78 -0
  134. package/dist/core/dependencies/javascript-env-dependencies.d.ts +8 -0
  135. package/dist/core/dependencies/javascript-env-dependencies.js +77 -0
  136. package/dist/core/dependencies/python-env-dependencies.d.ts +9 -0
  137. package/dist/core/dependencies/python-env-dependencies.js +91 -0
  138. package/dist/core/errors.d.ts +55 -0
  139. package/dist/core/errors.js +144 -0
  140. package/dist/core/executors/execution-task.d.ts +29 -0
  141. package/dist/core/executors/execution-task.js +139 -0
  142. package/dist/core/executors/javascript-execution-task.d.ts +13 -0
  143. package/dist/core/executors/javascript-execution-task.js +618 -0
  144. package/dist/core/executors/python/context.d.ts +2 -0
  145. package/dist/core/executors/python/context.js +3 -0
  146. package/dist/core/executors/python/datastore.d.ts +2 -0
  147. package/dist/core/executors/python/datastore.js +4 -0
  148. package/dist/core/executors/python-execution-task.d.ts +11 -0
  149. package/dist/core/executors/python-execution-task.js +173 -0
  150. package/dist/core/executors/source-code-sanitizer.d.ts +4 -0
  151. package/dist/core/executors/source-code-sanitizer.js +45 -0
  152. package/dist/core/resource-merger.d.ts +14 -0
  153. package/dist/core/resource-merger.js +155 -0
  154. package/dist/core/resource-pusher.d.ts +12 -0
  155. package/dist/core/resource-pusher.js +133 -0
  156. package/dist/core/resource-repository.d.ts +8 -0
  157. package/dist/core/resource-repository.js +2 -0
  158. package/dist/core/types.d.ts +290 -0
  159. package/dist/core/types.js +238 -0
  160. package/dist/core/utils/dependencies.d.ts +5 -0
  161. package/dist/core/utils/dependencies.js +82 -0
  162. package/dist/core/utils/variables.d.ts +4 -0
  163. package/dist/core/utils/variables.js +10 -0
  164. package/dist/hooks/init/auth.d.ts +3 -0
  165. package/dist/hooks/init/auth.js +28 -0
  166. package/dist/hooks/init/update.d.ts +3 -0
  167. package/dist/hooks/init/update.js +42 -0
  168. package/dist/index.d.ts +1 -0
  169. package/dist/index.js +4 -0
  170. package/dist/repositories/process.d.ts +36 -0
  171. package/dist/repositories/process.js +339 -0
  172. package/dist/repositories/programming-language-manifest.d.ts +17 -0
  173. package/dist/repositories/programming-language-manifest.js +95 -0
  174. package/dist/repositories/resource.d.ts +29 -0
  175. package/dist/repositories/resource.js +79 -0
  176. package/dist/repositories/script-library.d.ts +28 -0
  177. package/dist/repositories/script-library.js +164 -0
  178. package/dist/repositories/team.d.ts +23 -0
  179. package/dist/repositories/team.js +74 -0
  180. package/dist/repositories/variable.d.ts +26 -0
  181. package/dist/repositories/variable.js +174 -0
  182. package/dist/runtime/executor.js +87 -0
  183. package/dist/runtime/executor.py +81 -0
  184. package/dist/runtime/libs/node/fcode/index.js +1 -0
  185. package/dist/runtime/libs/python/fcode/__init__.py +75 -0
  186. package/dist/runtime/libs/python/fcode/context.py +98 -0
  187. package/dist/runtime/libs/python/fcode/datastore.py +35 -0
  188. package/dist/runtime/libs/python/fcode/schedule.py +45 -0
  189. package/dist/runtime/libs/python/fcode/storage.py +271 -0
  190. package/dist/runtime/libs/python/fcode/variables.py +102 -0
  191. package/dist/services/auth-service.d.ts +12 -0
  192. package/dist/services/auth-service.js +114 -0
  193. package/dist/services/credential-prompter.d.ts +6 -0
  194. package/dist/services/credential-prompter.js +60 -0
  195. package/dist/services/fcode-graphql-client.d.ts +45 -0
  196. package/dist/services/fcode-graphql-client.js +721 -0
  197. package/dist/services/http-server.d.ts +3 -0
  198. package/dist/services/http-server.js +17 -0
  199. package/dist/services/oauth-browser-service.d.ts +9 -0
  200. package/dist/services/oauth-browser-service.js +164 -0
  201. package/dist/services/process-parameters-sampler.d.ts +2 -0
  202. package/dist/services/process-parameters-sampler.js +41 -0
  203. package/dist/services/process-runner.d.ts +22 -0
  204. package/dist/services/process-runner.js +97 -0
  205. package/dist/services/resource-checker.d.ts +8 -0
  206. package/dist/services/resource-checker.js +82 -0
  207. package/dist/services/test-runner.d.ts +17 -0
  208. package/dist/services/test-runner.js +228 -0
  209. package/dist/utils/canonical-hash.d.ts +9 -0
  210. package/dist/utils/canonical-hash.js +30 -0
  211. package/dist/utils/cli/index.d.ts +14 -0
  212. package/dist/utils/cli/index.js +50 -0
  213. package/dist/utils/cli/screen.d.ts +2 -0
  214. package/dist/utils/cli/screen.js +25 -0
  215. package/dist/utils/cli/styled/table.d.ts +23 -0
  216. package/dist/utils/cli/styled/table.js +272 -0
  217. package/dist/utils/colorize-resource.d.ts +1 -0
  218. package/dist/utils/colorize-resource.js +7 -0
  219. package/dist/utils/env-utils.d.ts +2 -0
  220. package/dist/utils/env-utils.js +16 -0
  221. package/dist/utils/file-utils.d.ts +11 -0
  222. package/dist/utils/file-utils.js +33 -0
  223. package/dist/utils/filenamify.d.ts +4 -0
  224. package/dist/utils/filenamify.js +73 -0
  225. package/dist/utils/gitignore.d.ts +17 -0
  226. package/dist/utils/gitignore.js +60 -0
  227. package/dist/utils/promise.d.ts +4 -0
  228. package/dist/utils/promise.js +38 -0
  229. package/dist/utils/url-utils.d.ts +2 -0
  230. package/dist/utils/url-utils.js +10 -0
  231. package/dist/utils/variable-warnings.d.ts +8 -0
  232. package/dist/utils/variable-warnings.js +38 -0
  233. package/oclif.manifest.json +1783 -0
  234. package/package.json +129 -0
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_child_process_1 = require("node:child_process");
4
+ const fs = require("node:fs");
5
+ const path = require("node:path");
6
+ const errors_1 = require("../errors");
7
+ const execution_task_1 = require("./execution-task");
8
+ const workspace_1 = require("../../config/workspace");
9
+ const source_code_sanitizer_1 = require("./source-code-sanitizer");
10
+ class PythonExecutionTask extends execution_task_1.default {
11
+ async run() {
12
+ if (!this._execFilePath) {
13
+ this.prepare();
14
+ }
15
+ const venvPath = (0, workspace_1.baseDependenciesPath)(this._programmingLanguage);
16
+ const forked = (0, node_child_process_1.spawn)(`${venvPath}/bin/python3`, ['-u', path.resolve(this._execFilePath)], {
17
+ stdio: 'inherit',
18
+ env: {},
19
+ });
20
+ return new Promise((resolve, reject) => {
21
+ forked.on('exit', (code) => {
22
+ if (code) {
23
+ reject(new errors_1.ErrorExecutingProcess(`Process exited with code ${code}`));
24
+ }
25
+ else {
26
+ resolve(undefined);
27
+ }
28
+ });
29
+ forked.on('error', (error) => {
30
+ if (error.message.includes('ENOENT')) {
31
+ reject(new errors_1.ErrorExecutingProcess('Seems that python3 virtual environment was not created properly.'));
32
+ }
33
+ else {
34
+ reject(new errors_1.ErrorExecutingProcess(error.message));
35
+ }
36
+ });
37
+ });
38
+ }
39
+ _getJsonLogsSetup() {
40
+ return `
41
+ import logging as _logging
42
+ import datetime as _datetime
43
+ import builtins as _builtins
44
+
45
+ class _JsonFormatter(_logging.Formatter):
46
+ def format(self, record):
47
+ return json.dumps({
48
+ 'timestamp': _datetime.datetime.utcnow().isoformat() + 'Z',
49
+ 'level': record.levelname,
50
+ 'message': record.getMessage()
51
+ })
52
+
53
+ for _h in _logging.root.handlers:
54
+ _h.setFormatter(_JsonFormatter())
55
+
56
+ _orig_print = _builtins.print
57
+ def _json_print(*args, **kwargs):
58
+ msg = ' '.join(str(a) for a in args)
59
+ _orig_print(json.dumps({'timestamp': _datetime.datetime.utcnow().isoformat() + 'Z', 'level': 'INFO', 'message': msg}))
60
+ _builtins.print = _json_print
61
+ `;
62
+ }
63
+ _getReturnBlock(result) {
64
+ if (this._options.jsonLogs) {
65
+ return `
66
+ _orig_print(json.dumps({'timestamp': _datetime.datetime.utcnow().isoformat() + 'Z', 'level': 'INFO', 'message': 'Return', 'data': ${result}}))`;
67
+ }
68
+ return `
69
+ print("Return: ", json.dumps(${result}))`;
70
+ }
71
+ _getMainBlock() {
72
+ const sourceCode = this.getExecutionSourceCode();
73
+ const jsonLogsSetup = this._options.jsonLogs ? this._getJsonLogsSetup() : '';
74
+ if (this._options.hookFilePath) {
75
+ const fn = this._options.hookFunction ?? 'main';
76
+ return `import fcode as _imported_fcode_module
77
+ ${jsonLogsSetup}
78
+ import importlib.util as _importlib_util
79
+ _spec = _importlib_util.spec_from_file_location("hooks", "${this._options.hookFilePath}")
80
+ _hooks_module = _importlib_util.module_from_spec(_spec)
81
+ _spec.loader.exec_module(_hooks_module)
82
+ _hook_fn = getattr(_hooks_module, '${fn}', None)
83
+ if callable(_hook_fn):
84
+ result = _hook_fn()
85
+ if result:
86
+ ${this._getReturnBlock('result').trim()}
87
+ _return_file = os.environ.get('FCODE_RETURN_FILE', '')
88
+ if _return_file:
89
+ with open(_return_file, 'w') as _f:
90
+ json.dump(result, _f)
91
+ `;
92
+ }
93
+ else if ((0, source_code_sanitizer_1.isLegacySourceCode)(sourceCode, 'PYTHON')) {
94
+ return `import fcode as _imported_fcode_module
95
+ ${jsonLogsSetup}
96
+ logger.warning("[Factorial Code executor] Warning: Top-level \`return\` statements are deprecated. Please wrap your code in a \`main()\` function to ensure proper return value handling.")
97
+ def main():
98
+ ${(0, source_code_sanitizer_1.indentScript)(sourceCode)}
99
+
100
+ try:
101
+ result = main()
102
+ if result:
103
+ ${this._getReturnBlock('result').trim()}
104
+ _return_file = os.environ.get('FCODE_RETURN_FILE', '')
105
+ if _return_file:
106
+ with open(_return_file, 'w') as _f:
107
+ json.dump(result, _f)
108
+ except Exception as e:
109
+ print(traceback.format_exc())
110
+ _error_file = os.environ.get('FCODE_ERROR_FILE', '')
111
+ if _error_file:
112
+ with open(_error_file, 'w') as _f:
113
+ json.dump({'message': str(e)}, _f)
114
+ sys.exit(1)
115
+ `;
116
+ }
117
+ else {
118
+ const processPath = ['processes', this._process.slug];
119
+ if (this._options.version != null) {
120
+ processPath.push('versions');
121
+ processPath.push(this._options.version);
122
+ }
123
+ return `import fcode as _imported_fcode_module
124
+ ${jsonLogsSetup}
125
+ process_id = '${this._process.slug}'
126
+ process_path = '${processPath.join('.')}.main'
127
+ sys.path.append(os.getcwd())
128
+ process = __import__(process_path, fromlist=[process_path])
129
+ if (hasattr(process, 'main')):
130
+ result = process.main()
131
+ if result:
132
+ ${this._getReturnBlock('result').trim()}
133
+ _return_file = os.environ.get('FCODE_RETURN_FILE', '')
134
+ if _return_file:
135
+ with open(_return_file, 'w') as _f:
136
+ json.dump(result, _f)
137
+ `;
138
+ }
139
+ }
140
+ async _createExecutionFiles(tempDir) {
141
+ if (!this._options.silent) {
142
+ this._options.jsonLogs
143
+ ? process.stdout.write(JSON.stringify({ timestamp: new Date().toISOString(), level: 'DEBUG', message: `Creating execution files for '${this._process.slug}' in ${tempDir}` }) + '\n')
144
+ : console.debug(`Creating execution files for '${this._process.slug}' in ${tempDir}`);
145
+ }
146
+ const execFilePath = `${tempDir}/${this._process.slug}.py`;
147
+ const libraryFilePath = `${tempDir}/initialize_params.py`;
148
+ fs.writeFileSync(libraryFilePath, JSON.stringify(this.getMetadata()));
149
+ fs.writeFileSync(execFilePath, `
150
+ import sys
151
+ import os
152
+ import json
153
+ import traceback
154
+
155
+ sys.path.append('${path.join(this._dependenciesPath)}')
156
+ sys.path.append('${path.join(__dirname, '..', '..', 'runtime', 'libs', 'python')}')
157
+
158
+ os.environ['FCODE_LOG_LEVEL'] = '${this._options.logLevel}'
159
+ os.environ['FCODE_RUNTIME_METADATA_PATH'] = '${libraryFilePath}'
160
+ os.environ['FCODE_RETURN_FILE'] = '${this.returnValueFilePath}'
161
+ os.environ['FCODE_ERROR_FILE'] = '${this.errorValueFilePath}'
162
+ ${this._options.jsonLogs ? `os.environ['FCODE_JSON_LOGS'] = 'true'` : ''}
163
+ ${Object.entries(this.getVariablesObject()).map(([key, value]) => `if '${key}' not in os.environ: os.environ['${key}'] = ${JSON.stringify(value)}`).join('\n')}
164
+
165
+ ${this._getMainBlock()}
166
+ `);
167
+ return {
168
+ FCODE_EXECUTION_FILE: execFilePath,
169
+ FCODE_TMP_PATH: tempDir,
170
+ };
171
+ }
172
+ }
173
+ exports.default = PythonExecutionTask;
@@ -0,0 +1,4 @@
1
+ import { ProgrammingLanguage } from '../types';
2
+ export declare function removeComments(sourceCode: string, programmingLanguage: ProgrammingLanguage): string;
3
+ export declare function indentScript(str: string, count?: number, indent?: string): string;
4
+ export declare function isLegacySourceCode(sourceCode: string, programmingLanguage: ProgrammingLanguage): boolean;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.removeComments = removeComments;
4
+ exports.indentScript = indentScript;
5
+ exports.isLegacySourceCode = isLegacySourceCode;
6
+ const stripComments = require('strip-comments');
7
+ const MULTILINE_STRING_PATTERN = '(^|[^"])(""".*?"""|\'\'\'.*?\'\'\')';
8
+ function removeComments(sourceCode, programmingLanguage) {
9
+ if (!sourceCode || sourceCode.trim().length === 0) {
10
+ return '';
11
+ }
12
+ return stripComments(sourceCode.trim(), { language: programmingLanguage.toLowerCase(), preserveNewlines: true });
13
+ }
14
+ const cleanMultilineStrings = (sourceCode) => {
15
+ const MULTILINE_STRING_REGEX = new RegExp(MULTILINE_STRING_PATTERN, 'gs');
16
+ return sourceCode.replace(MULTILINE_STRING_REGEX, '$1"<multiline_string>"');
17
+ };
18
+ const unindentMultilineStrings = (sourceCode, indentationString) => {
19
+ const MULTILINE_STRING_REGEX = new RegExp(MULTILINE_STRING_PATTERN, 'gs');
20
+ let result = sourceCode;
21
+ const iterator = sourceCode.matchAll(MULTILINE_STRING_REGEX);
22
+ const matches = Array.from(iterator).reverse();
23
+ for (const match of matches) {
24
+ const unindentedMatch = match[0].replace(new RegExp(`\n${indentationString}`, 'gs'), '\n');
25
+ result = result.substring(0, match.index) + unindentedMatch + result.substring(match.index + match[0].length);
26
+ }
27
+ return result;
28
+ };
29
+ function indentScript(str, count = 4, indent = ' ') {
30
+ const indentationString = indent.repeat(count);
31
+ return unindentMultilineStrings(str.replaceAll(/^/gm, indentationString), indentationString);
32
+ }
33
+ function isLegacySourceCode(sourceCode, programmingLanguage) {
34
+ const code = removeComments(sourceCode, programmingLanguage);
35
+ if (programmingLanguage == 'PYTHON') {
36
+ const cleanCode = cleanMultilineStrings(code);
37
+ const regex = /^return[ \t]/m;
38
+ return regex.test(cleanCode);
39
+ }
40
+ else if (programmingLanguage == 'JAVASCRIPT') {
41
+ const regex = /^module\.exports\s*=+/;
42
+ return !code.split('\n').some((line) => regex.test(line));
43
+ }
44
+ return false;
45
+ }
@@ -0,0 +1,14 @@
1
+ import ResourceRepositoryBase from '../repositories/resource';
2
+ import { MergedResource, Resource, ResourceStatus } from './types';
3
+ export declare const mapResourceStatus: (remoteResource: Resource | undefined, localResource: Resource | undefined, existsMetadata: boolean) => Promise<ResourceStatus>;
4
+ export default class ResourceMerger<R extends Resource> {
5
+ protected _repository: ResourceRepositoryBase<R>;
6
+ constructor(repository: ResourceRepositoryBase<R>);
7
+ protected logChanges(added: Array<R>, updated: Array<R>, overwritten: Array<R>, removed: Array<R>, remoteRemoved: Array<R>): void;
8
+ mergeForPull(remoteResources: Array<R>, localResources: Array<R>, force: boolean): Promise<void>;
9
+ reset(remoteResources: Array<R>, localResources: Array<R>): Promise<void>;
10
+ mergeStatus(remoteResources: Resource[], localResources: Resource[]): Promise<MergedResource[]>;
11
+ private confirmRemoveResourcePromt;
12
+ private confirmOverwriteResourcePrompt;
13
+ private overrideResource;
14
+ }
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapResourceStatus = void 0;
4
+ const color_1 = require("@oclif/color");
5
+ const cli_1 = require("../utils/cli");
6
+ const resource_checker_1 = require("../services/resource-checker");
7
+ const promise_1 = require("../utils/promise");
8
+ const types_1 = require("./types");
9
+ const mapResourceStatus = async (remoteResource, localResource, existsMetadata) => {
10
+ if (!localResource) {
11
+ if (existsMetadata) {
12
+ return types_1.ResourceStatus.LOCAL_REMOVED;
13
+ }
14
+ return types_1.ResourceStatus.REMOTE_NEW;
15
+ }
16
+ const isVariable = localResource instanceof types_1.Variable;
17
+ if (isVariable && !localResource.value && localResource.id) {
18
+ return types_1.ResourceStatus.LOCAL_REMOVED;
19
+ }
20
+ if (isVariable && !localResource.id && remoteResource) {
21
+ return types_1.ResourceStatus.CONFLICT;
22
+ }
23
+ if (!localResource.id) {
24
+ return types_1.ResourceStatus.LOCAL_NEW;
25
+ }
26
+ if (!remoteResource) {
27
+ return types_1.ResourceStatus.REMOTE_REMOVED;
28
+ }
29
+ if (isVariable) {
30
+ if (localResource.isSensitive !== remoteResource.isSensitive) {
31
+ return types_1.ResourceStatus.SENSITIVITY_CHANGED;
32
+ }
33
+ if (localResource.isSensitive) {
34
+ return types_1.ResourceStatus.SENSITIVE;
35
+ }
36
+ }
37
+ if ((await resource_checker_1.default.remoteResourceHasBeenModified(remoteResource, localResource)) &&
38
+ (await resource_checker_1.default.localResourceHasBeenModified(remoteResource, localResource))) {
39
+ return types_1.ResourceStatus.CONFLICT;
40
+ }
41
+ if ((await resource_checker_1.default.remoteResourceHasBeenModified(remoteResource, localResource)) &&
42
+ !(await resource_checker_1.default.localResourceHasBeenModified(remoteResource, localResource))) {
43
+ return types_1.ResourceStatus.OUT_OF_DATE;
44
+ }
45
+ if (await resource_checker_1.default.localResourceHasBeenModified(remoteResource, localResource)) {
46
+ return types_1.ResourceStatus.MODIFIED;
47
+ }
48
+ return types_1.ResourceStatus.UP_TO_DATE;
49
+ };
50
+ exports.mapResourceStatus = mapResourceStatus;
51
+ class ResourceMerger {
52
+ constructor(repository) {
53
+ this._repository = repository;
54
+ }
55
+ // eslint-disable-next-line max-params
56
+ logChanges(added, updated, overwritten, removed, remoteRemoved) {
57
+ if (added.length > 0 || updated.length > 0 || overwritten.length > 0 || removed.length > 0 || remoteRemoved.length > 0) {
58
+ for (const resource of added) {
59
+ cli_1.default.log(`[✨ new]\t\t-> ${resource.slug}`);
60
+ }
61
+ for (const resource of updated) {
62
+ cli_1.default.log(`[âŦ‡ī¸ updated]\t-> ${resource.slug}`);
63
+ }
64
+ for (const resource of overwritten) {
65
+ cli_1.default.log(`[đŸ”Ĩ overwritten]\t-> ${resource.slug}`);
66
+ }
67
+ for (const resource of removed) {
68
+ cli_1.default.log(`[đŸ—‘ī¸ removed]\t-> ${resource.slug}`);
69
+ }
70
+ if (remoteRemoved.length > 0) {
71
+ const removedResourcesAsString = remoteRemoved.map((v) => v.slug).join(', ');
72
+ const warningText = `\nâ„šī¸ ${remoteRemoved.length} ${this._repository._repositoryName} removed from remote: ${removedResourcesAsString}`;
73
+ cli_1.default.log(color_1.default.yellow(warningText));
74
+ }
75
+ }
76
+ else {
77
+ cli_1.default.log(`- ${this._repository._repositoryName} are up to date.\n`);
78
+ }
79
+ }
80
+ async mergeForPull(remoteResources, localResources, force) {
81
+ const resourceRemovedInRemote = localResources.filter((localResource) => localResource.id && !remoteResources.some((remoteResource) => remoteResource.sameAs(localResource)));
82
+ const confirmedRemovedResources = (await (0, promise_1.chainedPromisesWithConfirmation)(resourceRemovedInRemote, this.confirmRemoveResourcePromt.bind(this), force));
83
+ const removed = resourceRemovedInRemote.filter((remoteResource) => confirmedRemovedResources.some((confirmedRemovedResource) => confirmedRemovedResource.sameAs(remoteResource)));
84
+ const remoteRemoved = resourceRemovedInRemote.filter((remoteResource) => !confirmedRemovedResources.some((confirmedRemovedResource) => confirmedRemovedResource.sameAs(remoteResource)));
85
+ let { newRemoteResources, newLocalResources, upToDateResources, conflictResources, modifiedResources, outOfDateResources, remoteRemovedResources, localRemovedResources, } = await resource_checker_1.default.categorizeResources(localResources, remoteResources, types_1.OnConflictResolution.KEEP_REMOTE, (slug) => this._repository.existsMetadata(slug));
86
+ const added = await (0, promise_1.chainedPromisesWithArgs)(newRemoteResources, this._repository.save.bind(this._repository));
87
+ const updated = await (0, promise_1.chainedPromisesWithArgs)(outOfDateResources, this.overrideResource.bind(this));
88
+ const overwritten = force
89
+ ? await (0, promise_1.chainedPromisesWithArgs)(conflictResources, this.overrideResource.bind(this))
90
+ : await (0, promise_1.chainedPromisesWithConfirmationAndArgs)(conflictResources, this.confirmOverwriteResourcePrompt.bind(this), force);
91
+ if (localResources.length > 0 || remoteResources.length > 0) {
92
+ this.logChanges(added, updated, overwritten, removed, remoteRemoved);
93
+ }
94
+ else {
95
+ cli_1.default.log(`🧐 no ${this._repository._repositoryName} found\n`);
96
+ }
97
+ }
98
+ async reset(remoteResources, localResources) {
99
+ let { newRemoteResources, newLocalResources, upToDateResources, conflictResources, modifiedResources: localModifiedResources, outOfDateResources, remoteRemovedResources, localRemovedResources, } = await resource_checker_1.default.categorizeResources(localResources, remoteResources, types_1.OnConflictResolution.KEEP_REMOTE, (slug) => this._repository.existsMetadata(slug));
100
+ const modifiedResources = localModifiedResources.map((localModifiedResource) => remoteResources.find((resource) => localModifiedResource.sameAs(resource)));
101
+ const added = await (0, promise_1.chainedPromisesWithArgs)([...newRemoteResources, ...localRemovedResources], this._repository.save.bind(this._repository));
102
+ const updated = await (0, promise_1.chainedPromisesWithArgs)(outOfDateResources, this.overrideResource.bind(this));
103
+ const overwritten = await (0, promise_1.chainedPromisesWithArgs)([...conflictResources, ...modifiedResources], this.overrideResource.bind(this));
104
+ const removed = await (0, promise_1.chainedPromisesWithArgs)([...remoteRemovedResources, ...newLocalResources], (item) => {
105
+ this._repository.remove(item.slug);
106
+ return Promise.resolve(item);
107
+ });
108
+ const remoteRemoved = [];
109
+ if (localResources.length > 0 || remoteResources.length > 0) {
110
+ this.logChanges(added, updated, overwritten, removed, remoteRemoved);
111
+ }
112
+ else {
113
+ cli_1.default.log(`🧐 no ${this._repository._repositoryName} found\n`);
114
+ }
115
+ }
116
+ async mergeStatus(remoteResources, localResources) {
117
+ const resourceList = [];
118
+ const allResourceSlugs = [...new Set([...localResources, ...remoteResources].map((resource) => resource.slug))];
119
+ for (const resourceSlug of allResourceSlugs) {
120
+ const remoteResource = remoteResources.find((resource) => resource.slug === resourceSlug);
121
+ const localResource = localResources.find((resource) => resource.slug === resourceSlug);
122
+ resourceList.push({
123
+ ...remoteResource,
124
+ ...localResource,
125
+ status: await (0, exports.mapResourceStatus)(remoteResource, localResource, await this._repository.existsMetadata(resourceSlug)),
126
+ sameAs: (remoteResource || localResource).sameAs.bind(localResource || remoteResource),
127
+ });
128
+ }
129
+ return resourceList;
130
+ }
131
+ async confirmRemoveResourcePromt(resource) {
132
+ if (await resource_checker_1.default.localResourceHasBeenModified(undefined, resource)) {
133
+ cli_1.default.log(`đŸ—‘ī¸ '${color_1.default.red(resource.slug)}' has been removed from remote but has changes locally!`);
134
+ if (!(await cli_1.default.confirm('Do you want remove it locally? yes/no'))) {
135
+ return false;
136
+ }
137
+ }
138
+ await this._repository.remove(resource.slug);
139
+ return true;
140
+ }
141
+ async confirmOverwriteResourcePrompt(overwriteResource) {
142
+ cli_1.default.log(`đŸ”Ĩ '${color_1.default.red(overwriteResource.slug)}' has been modified both remote and local!`);
143
+ if (await cli_1.default.confirm('Do you want to override local changes? yes/no')) {
144
+ await this._repository.remove(overwriteResource.slug);
145
+ await this._repository.save(overwriteResource);
146
+ return true;
147
+ }
148
+ return false;
149
+ }
150
+ async overrideResource(resource) {
151
+ await this._repository.remove(resource.slug);
152
+ return this._repository.save(resource);
153
+ }
154
+ }
155
+ exports.default = ResourceMerger;
@@ -0,0 +1,12 @@
1
+ import ResourceRepositoryBase from '../repositories/resource';
2
+ import FcodeGraphQLClient from '../services/fcode-graphql-client';
3
+ import { Resource } from './types';
4
+ export default class ResourcePusher<R extends Resource> {
5
+ private _repository;
6
+ private _fcodeGraphQLClient;
7
+ constructor(repository: ResourceRepositoryBase<R>, fcodeGraphQLClient: FcodeGraphQLClient);
8
+ push(remoteResources: Array<R>, localResources: Array<R>, force: boolean): Promise<void>;
9
+ pushResource(resource: R): Promise<R>;
10
+ removeResource(resource: R): Promise<R>;
11
+ private confirmPushResourcePromt;
12
+ }
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const color_1 = require("@oclif/color");
4
+ const cli_1 = require("../utils/cli");
5
+ const resource_checker_1 = require("../services/resource-checker");
6
+ const promise_1 = require("../utils/promise");
7
+ const types_1 = require("./types");
8
+ const variable_warnings_1 = require("../utils/variable-warnings");
9
+ const global_1 = require("../config/global");
10
+ const settings_1 = require("../config/settings");
11
+ class ResourcePusher {
12
+ constructor(repository, fcodeGraphQLClient) {
13
+ this._repository = repository;
14
+ this._fcodeGraphQLClient = fcodeGraphQLClient;
15
+ }
16
+ async push(remoteResources, localResources, force) {
17
+ cli_1.default.log(`\nđŸŒŠī¸ Pushing ${this._repository._repositoryName}...`);
18
+ for (const resource of localResources.filter((resource) => resource.resolvingTeamSlug)) {
19
+ if (await resource_checker_1.default.localResourceHasBeenModified(undefined, resource)) {
20
+ cli_1.default.log(color_1.default.red(`🚨 '${resource.slug}' is inherited from team '${resource.resolvingTeamSlug}' and is read-only. ` +
21
+ `Run 'fcode ${this._repository._repositoryName}:pull' to restore it before pushing.`));
22
+ }
23
+ }
24
+ localResources = localResources.filter((resource) => !resource.resolvingTeamSlug);
25
+ remoteResources = remoteResources.filter((resource) => !resource.resolvingTeamSlug);
26
+ if (this._repository._repositoryName === 'variables') {
27
+ const localVariables = localResources;
28
+ const remoteVariables = remoteResources;
29
+ const localVariablesFileName = await new global_1.default().getSetting(settings_1.SettingKeys.LOCAL_VARIABLES_FILE_NAME);
30
+ (0, variable_warnings_1.warnSensitivityChanged)((0, variable_warnings_1.sensitivityChangedVariables)(localVariables, remoteVariables).map((variable) => variable.slug));
31
+ (0, variable_warnings_1.warnTamperedSensitive)(localVariables.filter((variable) => (0, variable_warnings_1.isTamperedSensitive)(variable)).map((variable) => variable.slug), localVariablesFileName);
32
+ }
33
+ let { newRemoteResources, newLocalResources, upToDateResources, conflictResources, modifiedResources, outOfDateResources, remoteRemovedResources, localRemovedResources, } = await resource_checker_1.default.categorizeResources(localResources, remoteResources, types_1.OnConflictResolution.KEEP_LOCAL, (slug) => this._repository.existsMetadata(slug));
34
+ const confirmedConflictPusheableResources = await (0, promise_1.chainedPromisesWithConfirmation)(conflictResources, (resource) => {
35
+ return this.confirmPushResourcePromt(resource, types_1.ResourceStatus.CONFLICT);
36
+ }, force);
37
+ const confirmedRemovedPusheableResources = await (0, promise_1.chainedPromisesWithConfirmation)(localRemovedResources, (resource) => {
38
+ return this.confirmPushResourcePromt(resource, types_1.ResourceStatus.LOCAL_REMOVED);
39
+ }, force);
40
+ const pushedResources = await (0, promise_1.chainedPromises)([...newLocalResources, ...modifiedResources, ...confirmedConflictPusheableResources], this.pushResource.bind(this));
41
+ const removedResources = await (0, promise_1.chainedPromises)(confirmedRemovedPusheableResources, this.removeResource.bind(this));
42
+ if ([...pushedResources, ...removedResources].length > 0) {
43
+ // eslint-disable-next-line unicorn/no-array-for-each
44
+ removedResources.forEach((resource) => {
45
+ cli_1.default.log(`[đŸ—‘ī¸ removed] -> ${resource.slug} removed in remote.`);
46
+ });
47
+ pushedResources
48
+ .filter((resource) => !resource.errorPushing)
49
+ .forEach((resource) => {
50
+ if (confirmedConflictPusheableResources.some((confirmedPusheableResource) => confirmedPusheableResource.sameAs(resource))) {
51
+ cli_1.default.log(`[đŸ”Ĩ overwritten] -> ${resource.slug} overwritten to remote.`);
52
+ }
53
+ else {
54
+ cli_1.default.log(`[âŦ†ī¸ uploaded] -> ${resource.slug} uploaded to remote.`);
55
+ }
56
+ });
57
+ }
58
+ else {
59
+ cli_1.default.log(`- No local changes to push.\n`);
60
+ }
61
+ }
62
+ async pushResource(resource) {
63
+ try {
64
+ if (this._repository._repositoryName === 'processes') {
65
+ const updatedProcess = await this._fcodeGraphQLClient.pushProcess(resource);
66
+ return this._repository.save(updatedProcess);
67
+ }
68
+ if (this._repository._repositoryName === 'modules') {
69
+ const updatedModule = await this._fcodeGraphQLClient.pushScriptLibrary(resource);
70
+ return this._repository.save(updatedModule);
71
+ }
72
+ if (this._repository._repositoryName === 'variables') {
73
+ const variable = resource;
74
+ if (variable.id) {
75
+ if (variable.isSensitive) {
76
+ cli_1.default.log(`[⏊ skipped] -> ${variable.slug} is sensitive; its value is immutable and cannot be pushed.`);
77
+ return resource;
78
+ }
79
+ const updatedVariable = await this._fcodeGraphQLClient.updateVariable(variable);
80
+ return this._repository.save(updatedVariable);
81
+ }
82
+ const createdVariable = await this._fcodeGraphQLClient.pushVariable(variable);
83
+ return this._repository.save(createdVariable);
84
+ }
85
+ if (this._repository._repositoryName === 'dependencies') {
86
+ const updatedProgrammingLanguageManifests = await this._fcodeGraphQLClient.pushProgrammingLanguageManifest(resource);
87
+ return this._repository.save(updatedProgrammingLanguageManifests);
88
+ }
89
+ }
90
+ catch (error) {
91
+ if (this._fcodeGraphQLClient.isDuplicatedError(error)) {
92
+ cli_1.default.log(color_1.default.red(`🚨 '${resource.slug}' couldn't be pushed as other ${this._repository._repositoryName} exists in remote with the same attributes`));
93
+ resource.errorPushing = true;
94
+ }
95
+ else if (this._fcodeGraphQLClient.isEmptyValueError(error)) {
96
+ cli_1.default.log(color_1.default.red(`🚨 '${resource.slug}' couldn't be pushed as the value is empty`));
97
+ resource.errorPushing = true;
98
+ }
99
+ else {
100
+ cli_1.default.log(color_1.default.red(`🚨 '${resource.slug}' couldn't be pushed. Error: ${error.message}`));
101
+ resource.errorPushing = true;
102
+ }
103
+ }
104
+ return resource;
105
+ }
106
+ async removeResource(resource) {
107
+ try {
108
+ if (this._repository._repositoryName === 'processes') {
109
+ await this._fcodeGraphQLClient.removeProcess(resource.id);
110
+ }
111
+ if (this._repository._repositoryName === 'modules') {
112
+ await this._fcodeGraphQLClient.removeScriptLibrary(resource.id);
113
+ }
114
+ if (this._repository._repositoryName === 'variables') {
115
+ await this._fcodeGraphQLClient.removeVariable(resource.id);
116
+ }
117
+ this._repository.removeMetadata(resource.slug);
118
+ }
119
+ catch (error) {
120
+ cli_1.default.log(color_1.default.red(`🚨 '${resource.slug}' couldn't be removed. Error: ${error.message}`));
121
+ resource.errorPushing = true;
122
+ }
123
+ return resource;
124
+ }
125
+ async confirmPushResourcePromt(resource, status) {
126
+ cli_1.default.log(`đŸ”Ĩ '${color_1.default.red(resource.slug)}' has been ${status === types_1.ResourceStatus.CONFLICT ? 'modified both remote and local' : 'removed from local'}!`);
127
+ if (await cli_1.default.confirm('Do you want force push to remote? yes/no')) {
128
+ return true;
129
+ }
130
+ return false;
131
+ }
132
+ }
133
+ exports.default = ResourcePusher;
@@ -0,0 +1,8 @@
1
+ import { Resource } from './types';
2
+ export default interface ResourceRepository<R extends Resource> {
3
+ exists(slug: string): Promise<boolean>;
4
+ findAll(): Promise<Array<R>>;
5
+ find(slug: string): Promise<R>;
6
+ remove(slug: string): Promise<void>;
7
+ save(resource: R): Promise<R>;
8
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });