@codenotch/codenotch.cli 1.0.33 → 1.0.34

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 (97) hide show
  1. package/dist/commands/index.js +2 -0
  2. package/dist/commands/index.js.map +1 -1
  3. package/dist/commands/validation/ValidationUtils.d.ts +28 -0
  4. package/dist/commands/validation/ValidationUtils.js +182 -0
  5. package/dist/commands/validation/ValidationUtils.js.map +1 -0
  6. package/dist/commands/validation/files/AppManifestValidator.d.ts +6 -0
  7. package/dist/commands/validation/files/AppManifestValidator.js +93 -0
  8. package/dist/commands/validation/files/AppManifestValidator.js.map +1 -0
  9. package/dist/commands/validation/files/BpmnValidator.d.ts +5 -0
  10. package/dist/commands/validation/files/BpmnValidator.js +32 -0
  11. package/dist/commands/validation/files/BpmnValidator.js.map +1 -0
  12. package/dist/commands/validation/files/DbSchemaValidator.d.ts +5 -0
  13. package/dist/commands/validation/files/DbSchemaValidator.js +16 -0
  14. package/dist/commands/validation/files/DbSchemaValidator.js.map +1 -0
  15. package/dist/commands/validation/files/DocumentValidator.d.ts +5 -0
  16. package/dist/commands/validation/files/DocumentValidator.js +12 -0
  17. package/dist/commands/validation/files/DocumentValidator.js.map +1 -0
  18. package/dist/commands/validation/files/I18nValidator.d.ts +5 -0
  19. package/dist/commands/validation/files/I18nValidator.js +13 -0
  20. package/dist/commands/validation/files/I18nValidator.js.map +1 -0
  21. package/dist/commands/validation/files/JsonValidator.d.ts +5 -0
  22. package/dist/commands/validation/files/JsonValidator.js +13 -0
  23. package/dist/commands/validation/files/JsonValidator.js.map +1 -0
  24. package/dist/commands/validation/files/ManifestCacheValidator.d.ts +5 -0
  25. package/dist/commands/validation/files/ManifestCacheValidator.js +121 -0
  26. package/dist/commands/validation/files/ManifestCacheValidator.js.map +1 -0
  27. package/dist/commands/validation/files/ManifestValidator.d.ts +5 -0
  28. package/dist/commands/validation/files/ManifestValidator.js +14 -0
  29. package/dist/commands/validation/files/ManifestValidator.js.map +1 -0
  30. package/dist/commands/validation/files/ScriptValidator.d.ts +12 -0
  31. package/dist/commands/validation/files/ScriptValidator.js +128 -0
  32. package/dist/commands/validation/files/ScriptValidator.js.map +1 -0
  33. package/dist/commands/validation/files/TableValidator.d.ts +5 -0
  34. package/dist/commands/validation/files/TableValidator.js +19 -0
  35. package/dist/commands/validation/files/TableValidator.js.map +1 -0
  36. package/dist/commands/validation/validate.d.ts +2 -0
  37. package/dist/commands/validation/validate.js +245 -0
  38. package/dist/commands/validation/validate.js.map +1 -0
  39. package/dist/utils/BuildUtils.d.ts +22 -4
  40. package/dist/utils/BuildUtils.js +45 -16
  41. package/dist/utils/BuildUtils.js.map +1 -1
  42. package/dist/utils/FileUtils.d.ts +13 -0
  43. package/dist/utils/FileUtils.js +30 -0
  44. package/dist/utils/FileUtils.js.map +1 -1
  45. package/dist/utils/I18nUtils.js +0 -2
  46. package/dist/utils/I18nUtils.js.map +1 -1
  47. package/package.json +14 -6
  48. package/.github/copilot-instructions.md +0 -3
  49. package/bin/cli.js +0 -3
  50. package/docs/ARCHITECTURE.md +0 -179
  51. package/docs/TODO.md +0 -18
  52. package/src/bin/cli.ts +0 -4
  53. package/src/commands/index.ts +0 -13
  54. package/src/commands/project/compile.ts +0 -20
  55. package/src/commands/project/deploy.ts +0 -11
  56. package/src/commands/project/index.ts +0 -28
  57. package/src/commands/project/inspect.ts +0 -12
  58. package/src/commands/project/new.ts +0 -11
  59. package/src/commands/project/open.ts +0 -21
  60. package/src/commands/project/package.ts +0 -20
  61. package/src/commands/project/refresh.ts +0 -13
  62. package/src/commands/project/remove.ts +0 -12
  63. package/src/commands/project/shared.ts +0 -9
  64. package/src/commands/project/start.ts +0 -24
  65. package/src/commands/project/test.ts +0 -15
  66. package/src/commands/react/build.ts +0 -12
  67. package/src/commands/react/index.ts +0 -12
  68. package/src/commands/react/serve.ts +0 -13
  69. package/src/commands/runtime/clear.ts +0 -11
  70. package/src/commands/runtime/index.ts +0 -24
  71. package/src/commands/runtime/info.ts +0 -47
  72. package/src/commands/runtime/latest.ts +0 -18
  73. package/src/commands/runtime/portal.ts +0 -20
  74. package/src/commands/runtime/start.ts +0 -16
  75. package/src/commands/runtime/stop.ts +0 -13
  76. package/src/commands/runtime/update.ts +0 -12
  77. package/src/commands/system/index.ts +0 -9
  78. package/src/commands/system/info.ts +0 -23
  79. package/src/commands/system/read.ts +0 -25
  80. package/src/index.ts +0 -22
  81. package/src/models/Codenotch.ts +0 -361
  82. package/src/models/DbModels.ts +0 -428
  83. package/src/models/OpenApiModels.ts +0 -145
  84. package/src/utils/BuildUtils.ts +0 -739
  85. package/src/utils/ConsoleUtils.ts +0 -22
  86. package/src/utils/FileUtils.ts +0 -266
  87. package/src/utils/GitHubAuthUtils.ts +0 -83
  88. package/src/utils/I18nUtils.ts +0 -58
  89. package/src/utils/OpenAPIUtils.ts +0 -285
  90. package/src/utils/OsUtils.ts +0 -32
  91. package/src/utils/ProjectCompilationUtils.ts +0 -1002
  92. package/src/utils/ProjectUtils.ts +0 -151
  93. package/src/utils/RuntimeUtils.ts +0 -743
  94. package/src/utils/ServeUtils.ts +0 -175
  95. package/src/utils/ShellUtils.ts +0 -26
  96. package/src/utils/StringUtils.ts +0 -80
  97. package/tsconfig.json +0 -35
@@ -1,1002 +0,0 @@
1
- import path from "path";
2
- import { ApplicationManifest, IScenarioProjectManifest, IWorkspaceCache, ProjectManifest, ProjectManifestApi, ProjectManifestApplication, ProjectManifestDatastore, ProjectManifestEmail, ProjectManifestExpressComponent, ProjectManifestHtml, ProjectManifestPdf, ProjectManifestProcess, ProjectManifestProcessStartEvent, ProjectManifestReactApplication, ProjectManigestSignal } from "../models/Codenotch";
3
- import AbstractRecordContainer, { TableDefinition, FieldType, FieldTypes } from "../models/DbModels";
4
- import FileUtils from "./FileUtils";
5
- import I18nUtils from "./I18nUtils";
6
- import { OpenApiUtils } from "./OpenAPIUtils";
7
- import ProjectUtils from "./ProjectUtils";
8
- import StringUtils from "./StringUtils";
9
- import { JSONSchema7, JSONSchema7TypeName } from "json-schema";
10
- import { table } from "console";
11
- import { parse } from "js2xmlparser";
12
- import BuildUtils from "./BuildUtils";
13
- import ConsoleUtils from "./ConsoleUtils";
14
- import { IProcess, IWorkflowPool, BpmnToWorkflowDeserializer, IWorkflowItem, WorkflowItemType, WorkflowPoolType, WorkflowPoolPriority, IWorkflowEvent, IWorkflowSignalEventDefinition, IWorkflowStandardEventDefinition, StaticWorkflowDataProvider } from "@codenotch/codenotch.core.workflow";
15
- import { IWorkflowDataProvider } from "@codenotch/codenotch.core.workflow/dist/core/IWorkflowDataProvider";
16
- import { IVariable, VariableType } from "@codenotch/codenotch.core";
17
- import chalk from "chalk";
18
-
19
- // Context used during compilation, can be used to store intermediate data that can be reused during the different compilation steps
20
- export interface CompilationContext {
21
- errors: string[];
22
- warnings: string[];
23
- targetPath: string; // The path of the compiled project, can be used by different steps to read/write compiled files
24
- processes: { [processId: string]: any };
25
- documents: { [documentId: string]: AbstractRecordContainer };
26
- tables: { [tableId: string]: TableDefinition };
27
- manifest?: ProjectManifest;
28
- files: string[]; // List of all project files, can be used to avoid reading the file system multiple times
29
- }
30
-
31
- export interface CompilationResult {
32
- errors: string[];
33
- warnings: string[];
34
- }
35
-
36
- const MANIFEST_FILE_NAME = "manifest.json";
37
- const DB_SCHEMA_FILE_NAME = "db-schema.xml";
38
- const MANIFEST_CACHE_FILE_NAME = "manifest.cache.json";
39
- const OPENAPI_FILE_NAME = "openapi.json";
40
-
41
- export default abstract class ProjectCompilationUtils {
42
-
43
- /**
44
- * Create a new folder that contains a compiled version of the project
45
- * We have to compile the project because some files need to be modified a bit in order to work when deployed
46
- * In-place compilation, the files in the source folder will be modified/deleted and the output will be generated in the same folder
47
- * For example auml files have to include their i18n data
48
- * /!\Warning/!\ This will modify/delete files in the source folder, make sure to know what you are doing !!!
49
- * @param project current project
50
- * @returns the path to the compiled project
51
- */
52
- public static async compileProject(targetPath: string): Promise<CompilationResult> {
53
-
54
- let context: CompilationContext = { targetPath: targetPath, processes: {}, documents: {}, tables: {}, files: [], errors: [], warnings: [] };
55
-
56
- // Rebuild manifest from cache if it exists, otherwise generate it from project files
57
- console.log(chalk.cyan('→') + ' Generating manifest...');
58
- const fullManifest = await this.generateManifest(context);
59
- context.manifest = fullManifest;
60
- ConsoleUtils.check(`Manifest generated (${fullManifest.processes.length} process(es), ${fullManifest.reactApplications?.length ?? 0} React app(s))`);
61
-
62
- console.log(chalk.cyan('→') + ' Generating OpenAPI spec...');
63
- const openApi = await OpenApiUtils.generateOpenApi(context);
64
- if (openApi) ConsoleUtils.check('OpenAPI spec generated');
65
-
66
- console.log(chalk.cyan('→') + ' Generating database schema...');
67
- const dbSchema = await this.generateDbSchema(context);
68
- if (dbSchema && dbSchema.trim() !== '') ConsoleUtils.check('Database schema generated');
69
-
70
- // Compile package versions
71
- //compiledProject.manifest.packages = await this.compileSdkPackages(compiledProject.manifest);
72
- //TODO I don't like the idea of the cli fetching latest versions of each package, maybe they should be fixed ? or resolution happens at deployment ?
73
- // --> the problem is specific to auml components which we'll probably get rid of
74
-
75
- // Compile auml files
76
- //await this.compileAumlFiles(compiledProject.manifestCache, compiledProject);
77
- //TODO aka add i18n into the files, I think we could find a better way to do that, or at least only add relevant i18n
78
- // --> the problem is specific to auml components which we'll probably get rid of
79
-
80
- //TODO i18n for React apps
81
-
82
- // Build React apps if any
83
- const reactApps = fullManifest.reactApplications ?? [];
84
- for(let reactApp of reactApps) {
85
-
86
- try
87
- {
88
- console.log(chalk.cyan('→') + ` Building React app "${reactApp.applicationName}"...`);
89
- const appPath = path.join(targetPath, reactApp.file);
90
- const buildPath = await BuildUtils.buildApp2(appPath);
91
-
92
- // The build result is generated in .codenotch/appname-dist, we need to adapt the manifest to point to the generated index.html file
93
- reactApp.file = StringUtils.toRelativePath(path.join(buildPath, 'index.html'), targetPath);
94
- ConsoleUtils.check(`React app "${reactApp.applicationName}" built`);
95
- }
96
- catch(e)
97
- {
98
- context.errors.push(`Failed to build React application "${reactApp.applicationName}": ${e instanceof Error ? e.message : String(e)}`);
99
- }
100
- }
101
-
102
- // Create/update the files
103
- FileUtils.writeFileSync(path.join(targetPath, MANIFEST_FILE_NAME), JSON.stringify(fullManifest, null, 4));
104
-
105
- const dbSchemaPath = path.join(targetPath, DB_SCHEMA_FILE_NAME);
106
- if (await FileUtils.exists(dbSchemaPath)) {
107
- FileUtils.delete(dbSchemaPath);
108
- }
109
- if(dbSchema && dbSchema.trim() !== '')
110
- FileUtils.writeFileSync(dbSchemaPath, dbSchema);
111
-
112
- const openapiPath = path.join(targetPath, OPENAPI_FILE_NAME);
113
- if (await FileUtils.exists(openapiPath)) {
114
- FileUtils.delete(openapiPath);
115
- }
116
- if(openApi)
117
- FileUtils.writeFileSync(openapiPath, JSON.stringify(openApi, null, 4));
118
-
119
- // Remove manifest cache
120
- FileUtils.delete(path.join(targetPath, MANIFEST_CACHE_FILE_NAME));
121
-
122
- // Remove .npmrc if exists, we don't want to expose it
123
- FileUtils.delete(path.join(targetPath, '.npmrc'));
124
-
125
- if (context.errors.length > 0) {
126
- console.log(chalk.red(`Compilation finished with ${context.errors.length} error(s)`));
127
- } else {
128
- ConsoleUtils.check('Compilation complete');
129
- }
130
-
131
- return {
132
- errors: context.errors,
133
- warnings: context.warnings
134
- };
135
- }
136
-
137
- /**
138
- * Execute the compilation step needed for the tests to execute properly, skipping steps that are not needed for tests (like building React apps)
139
- *
140
- * @param project current project
141
- * @returns the path to the compiled project
142
- */
143
- public static async compileTestProject(targetPath: string): Promise<CompilationResult> {
144
-
145
- let context: CompilationContext = { targetPath: targetPath, processes: {}, documents: {}, tables: {}, files: [], errors: [], warnings: [] };
146
-
147
- // Rebuild manifest from cache if it exists, otherwise generate it from project files
148
- const fullManifest = await this.generateManifest(context);
149
- context.manifest = fullManifest;
150
-
151
- const dbSchema = await this.generateDbSchema(context);
152
-
153
- // Create/update the files
154
- FileUtils.writeFileSync(path.join(targetPath, MANIFEST_FILE_NAME), JSON.stringify(fullManifest, null, 4));
155
- FileUtils.writeFileSync(path.join(targetPath, DB_SCHEMA_FILE_NAME), dbSchema);
156
-
157
- return {
158
- errors: context.errors,
159
- warnings: context.warnings
160
- };
161
- }
162
-
163
- static async generateDbSchema( context: CompilationContext): Promise<string> {
164
-
165
- try
166
- {
167
- if(!context.manifest) {
168
- throw new Error("Manifest is required to generate db schema");
169
- }
170
-
171
- let tablesResult: TableDefinition[] = Object.values(context.tables);
172
-
173
- // Update db schema
174
- let dbSchema = ProjectCompilationUtils.GetXMLFromTableDefinition(tablesResult, context.manifest.version);
175
-
176
- return dbSchema;
177
- }
178
- catch (e) {
179
- context.errors.push(`Failed to generate database schema: ${e instanceof Error ? e.message : String(e)}`);
180
- return '';
181
- }
182
- }
183
-
184
- static GetXMLFromTableDefinition(definitions: TableDefinition[], version: string = "0.0.1"): string {
185
-
186
- let DatabaseSchema: any = {
187
- Tables: {
188
- Table: []
189
- },
190
- "@": { Version: version }
191
- };
192
-
193
- definitions.forEach(definition => {
194
- let definitionModel: any = {
195
- "@": {
196
- Name: definition.Name,
197
- Description: definition.Description
198
- },
199
- Id: {
200
- "@": {
201
- Name: definition.Id.Name,
202
- }
203
- },
204
- CreatedAt: {
205
- "@": {
206
- Name: definition.CreatedAt.Name,
207
- }
208
- },
209
- UpdatedAt: {
210
- "@": {
211
- Name: definition.UpdatedAt.Name,
212
- }
213
- }
214
- }
215
- definition.Fields?.forEach(field => {
216
- let resultfield: any = {
217
- "@": Object.assign({}, field)
218
- }
219
- delete resultfield["@"].Type;
220
-
221
- if (field.Type === FieldTypes.Enum) {
222
- resultfield["Value"] = resultfield["@"].Value;
223
- delete resultfield["@"].Value;
224
- }
225
-
226
- if (Array.isArray(definitionModel[field.Type])) {
227
- definitionModel[field.Type].push(resultfield);
228
- } else if (definitionModel[field.Type]) {
229
- definitionModel[field.Type] = [definitionModel[field.Type]];
230
- definitionModel[field.Type].push(resultfield);
231
- } else {
232
- definitionModel[field.Type] = resultfield
233
- }
234
- })
235
- DatabaseSchema.Tables.Table.push(definitionModel)
236
-
237
- });
238
-
239
- let result = parse("DatabaseSchema", DatabaseSchema, { format: { doubleQuotes: true } })
240
- return result;
241
- }
242
-
243
- /*
244
- private static async compileAumlFiles(manifest: IWorkspaceCache, workspace: IWorkspaceRoot) {
245
-
246
- // Auml files need to include their i18n data
247
- let globali18ns = await I18nUtils.getCompiledGlobali18n(workspace, true);
248
-
249
- await I18nUtils.injectI18nIntoFiles(manifest.applications.map(x => x.file), workspace, globali18ns.values, globali18ns.files);
250
-
251
- if (manifest.expressComponents)
252
- await I18nUtils.injectI18nIntoFiles(manifest.expressComponents.map((x: any) => x.file), workspace, globali18ns.values, globali18ns.files);
253
-
254
- if (manifest.pdfs)
255
- await I18nUtils.injectI18nIntoFiles(manifest.pdfs.map(x => x.file), workspace, globali18ns.values, globali18ns.files);
256
-
257
- if (manifest.htmls)
258
- await I18nUtils.injectI18nIntoFiles(manifest.htmls.map(x => x.file), workspace, globali18ns.values, globali18ns.files);
259
-
260
- if (manifest.emails)
261
- await I18nUtils.injectI18nIntoFiles(manifest.emails.map(x => x.file), workspace, globali18ns.values, globali18ns.files);
262
-
263
-
264
- // Assets can also contain auml files
265
- let assetsPath = workspace + '/assets';
266
- var assetFiles = await ElectronUtil.getFileSystem(assetsPath).getAllFilesFromPath(assetsPath);
267
- var aumlAssetsFiles = assetFiles
268
- .filter(x => x.endsWith(".auml")) // We only want auml files
269
- .map(x => x.replace(workspace.path, '')); // Remove the project path part
270
- await I18nUtils.injectI18nIntoFiles(aumlAssetsFiles, workspace, globali18ns.values, globali18ns.files);
271
- }
272
- */
273
-
274
- /**
275
- * Will lock the version of all packages that use the "latest"
276
- */
277
- /* TODO
278
- private static async compileSdkPackages(manifest: IScenarioProjectManifest): Promise<{ [packageSource: string]: string }> {
279
- let results: {
280
- [packageSource: string]: string;
281
- } = {};
282
-
283
- for (let packageKey of Object.keys(manifest.packages)) {
284
-
285
- let version = manifest.packages[packageKey];
286
- if (version === "latest") {
287
- // We need to lock the version of all packages that use the "latest"
288
- // because otherwise we expose ourselves to a lot of potential problems due to unexpected changes in our dependencies
289
-
290
- // Get the latest version from the table storage
291
- var split = packageKey.split('/');
292
-
293
- let company = "";
294
- let packageName = split[0];
295
-
296
- if (split.length === 2) {
297
- company = split[0].substring(1);
298
- packageName = split[1];
299
- }
300
-
301
- let latestVersion = await PackageUtils.getPackageLatestVersion(packageName, version, company);
302
-
303
- results[packageKey] = latestVersion;
304
- }
305
- else {
306
- results[packageKey] = version;
307
- }
308
- }
309
-
310
- return results;
311
- }
312
- */
313
-
314
- public static getManifestFileType(file: string) {
315
- file = file.toLowerCase();
316
- let fileType: 'table' | 'bpmn' | 'auml' | 'document' | 'script' | 'jsxtsx' | 'i18n' | '' =
317
- file.endsWith('.table') ? 'table' :
318
- file.endsWith('.bpmn') ? 'bpmn' :
319
- file.endsWith('.auml') ? 'auml' :
320
- file.endsWith('.jschema') ? 'document' :
321
- file.endsWith('.tsx') ? 'jsxtsx' :
322
- file.endsWith('.jsx') ? 'jsxtsx' :
323
- file.endsWith('.i18n.csv') ? 'i18n' :
324
- file.endsWith('.js') ? 'script' : '';
325
- return fileType;
326
- }
327
-
328
- /**
329
- * Generate the full project manifest
330
- * Uses the manifest cache if available, otherwise read the project files to generate the full manifest
331
- */
332
- public static async generateManifest(context: CompilationContext): Promise<ProjectManifest> {
333
-
334
- const projectPath = context.targetPath;
335
-
336
- // Obtain the manifest, this part only contains user defined properties, the rest will be generated from files or taken from the cache
337
- let manifest = await FileUtils.readJsonFileAsync<ProjectManifest>(path.join(projectPath, MANIFEST_FILE_NAME));
338
- if(!manifest) {
339
- throw new Error("Cannot find manifest file in project");
340
- }
341
-
342
- // Ensure arrays are defined
343
- manifest.postInstallationProcesses = manifest.postInstallationProcesses ?? [];
344
- manifest.languages = manifest.languages ?? [];
345
- manifest.dependencies = manifest.dependencies ?? [];
346
- manifest.packages = manifest.packages ?? [];
347
- manifest.roles = manifest.roles ?? [];
348
- manifest.reactApplications = manifest.reactApplications ?? [];
349
- manifest.apis = manifest.apis ?? [];
350
- manifest.applications = manifest.applications ?? [];
351
- manifest.emails = manifest.emails ?? [];
352
- manifest.htmls = manifest.htmls ?? [];
353
- manifest.expressComponents = manifest.expressComponents ?? [];
354
- manifest.pdfs = manifest.pdfs ?? [];
355
- manifest.scripts = manifest.scripts ?? [];
356
- manifest.tableDataSources = manifest.tableDataSources ?? [];
357
- manifest.documentDataSources = manifest.documentDataSources ?? [];
358
- manifest.processes = manifest.processes ?? [];
359
-
360
-
361
- // Ensure version is defined
362
- if ((manifest.version ?? '').trim() === '') {
363
- manifest.version = '0.0.1';
364
- }
365
-
366
- // Ensure default roles are existings
367
- if (manifest.roles.find((c: any) => c.name.toLowerCase() === 'dev') === undefined) {
368
- manifest.roles.push({
369
- name: "Dev",
370
- description: 'Role used for developpers.'
371
- });
372
- }
373
- if (manifest.roles.find((c: any) => c.name.toLowerCase() === 'admin') === undefined) {
374
- manifest.roles.push({
375
- name: "Admin",
376
- description: 'Role used for administrators.'
377
- });
378
- }
379
-
380
- // Add default languages if none has been defined yet
381
- if (manifest.languages.length === 0) {
382
- manifest.languages = ["en"]
383
- }
384
-
385
- /* This is more optimized yes, but it lead to problems difficult to debug as it behaves differently depending on the cache, better to have a predictable behaviour
386
- try
387
- {
388
- let manifestCache = context.ignoreCache ? null : await FileUtils.readJsonFileAsync<ProjectManifest>(path.join(projectPath, MANIFEST_CACHE_FILE_NAME));
389
-
390
- // If a cache is present, skip discovering local files, typically inside the ide
391
- if(manifestCache) {
392
-
393
- manifest = {
394
- ...manifest,
395
- ...manifestCache
396
- }
397
-
398
- // We still need to populate the context
399
- //TODO
400
-
401
- return manifest;
402
- }
403
- }
404
- catch {}
405
- */
406
-
407
- // No cache, we need to discover files to generate the full manifest
408
- const projectFiles = FileUtils.getAllFilesFromPath(projectPath, ['node_modules', '.codenotch', '.git', 'dist', 'build', 'out']);
409
- context.files = projectFiles;
410
-
411
- //#region tables
412
- manifest.tableDataSources = [];
413
-
414
- // Read all tables
415
- let tablesFiles = projectFiles.filter(file => file.endsWith(".table"));
416
- for (let i = 0; i < tablesFiles.length; i++) {
417
- let tablefile = tablesFiles[i];
418
- let tableDefinition = (await FileUtils.readJsonFileAsync<TableDefinition>(tablefile))!;
419
- manifest.tableDataSources.push({
420
- file: StringUtils.toRelativePath(tablefile, projectPath),
421
- name: tableDefinition.Name,
422
- });
423
-
424
- context.tables[tableDefinition.Name] = tableDefinition;
425
- }
426
- //#endregion
427
-
428
- //#region documents
429
- manifest.documentDataSources = [];
430
- let datastores = projectFiles.filter(file => file.endsWith(".jschema"));
431
- for (let i = 0; i < datastores.length; i++) {
432
- let datastoreFile = datastores[i];
433
- let document = (await FileUtils.readJsonFileAsync<AbstractRecordContainer>(datastoreFile))!;
434
- manifest.documentDataSources.push({
435
- file: StringUtils.toRelativePath(datastoreFile, projectPath),
436
- name: document.name,
437
- });
438
-
439
- context.documents[document.name] = document;
440
- }
441
- //#endregion
442
-
443
- //#region processes
444
- manifest.processes = [];
445
- manifest.apis = [];
446
-
447
- //TODO handle dependencies too ? for now, it will issue warnings for external calls it cannot verify
448
- let serviceName = manifest.serviceName;
449
- let provider: IWorkflowDataProvider = new StaticWorkflowDataProvider(
450
- [manifest],
451
- serviceName,
452
- Object.values(context.tables).map(t => ({ service: serviceName, model: t })),
453
- Object.values(context.documents).map(d => ({ service: serviceName, model: d }))
454
- );
455
-
456
- let bpmns = projectFiles.filter(file => file.endsWith(".bpmn"));
457
- for (let i = 0; i < bpmns.length; i++) {
458
- let bpmnFile = bpmns[i];
459
- await this.generateManifestBpmn(bpmnFile, projectPath, manifest, provider, context);
460
- }
461
-
462
- // Ensure all processes defined here are really in the project
463
- manifest.postInstallationProcesses = manifest.postInstallationProcesses!.filter((x: any) => manifest!.processes.find((y: any) => y.processName === x.processName) !== undefined);
464
-
465
- //#endregion
466
-
467
- //#region jsxtsx
468
- manifest.reactApplications = [];
469
-
470
- // Each file ending with .jsx or .tsx with a .manifest.json is considered as a React application
471
- const jsxs = projectFiles.filter(file => file.endsWith(".jsx"));
472
- const tsxs = projectFiles.filter(file => file.endsWith(".tsx"));
473
- let jsxtsxs = [...jsxs, ...tsxs];
474
- for (let i = 0; i < jsxtsxs.length; i++) {
475
- await this.generateManifestReactApp(jsxtsxs[i], projectPath, manifest);
476
- }
477
-
478
- //#endregion
479
-
480
- /* TODO see if we need that, shouldn't the react build already contain the translations§
481
- let isNodeApp = await FileUtils.exists(FileUtils.normalize(`${project.path}/package.json`));
482
- //#region i18n
483
-
484
- if (isNodeApp && (updateAll || fileType === 'i18n')) {
485
-
486
- let allI18nValues = await I18nUtils.getAllI18nFiles(project.path);
487
- let codenotchOverridePath = StringUtils.normalizePath(`${project.path}/typings/i18n.d.ts`);
488
- let translations: { key: string; argumentCount: number; }[] = [];
489
- for (let item of allI18nValues) {
490
- let allValues = item.values.map(c => c.value).join(' ');
491
- let argMatchs = [...allValues.matchAll(/({([\d ]*)})/gm)];
492
- let argCount = 0;
493
- for (let i = 0; i < argMatchs.length; i++) {
494
- if (allValues.includes(`{${i}}`)) {
495
- argCount++;
496
- }
497
- else {
498
- break;
499
- }
500
- }
501
- translations.push({
502
- key: item.key,
503
- argumentCount: argCount
504
- });
505
- }
506
- translations = translations.sort((a, b) => a.key.localeCompare(b.key));
507
- let codenotchOverride = TsxCompiler.createCodenotchReactI18NOverride(translations);
508
- await FileUtils.writeFileAsync(codenotchOverridePath, codenotchOverride);
509
-
510
-
511
- TsxCompiler.createFile(codenotchOverridePath, codenotchOverride);
512
- }
513
-
514
- if (processChanged && isNodeApp) {
515
- let codenotchOverridePath = StringUtils.normalizePath(`${project.path}/typings/process.d.ts`);
516
- let codenotchOverride = TsxCompiler.createCodenotchReactProcessOverride(manifestCache.processes);
517
- await IdeServices.Files.writeFileAsync(codenotchOverridePath, codenotchOverride);
518
- TsxCompiler.createFile(codenotchOverridePath, codenotchOverride);
519
- }
520
- */
521
-
522
- //#endregion
523
-
524
- //#region auml
525
- manifest.emails = [];
526
- manifest.pdfs = [];
527
- manifest.applications = [];
528
- manifest.expressComponents = [];
529
- manifest.htmls = [];
530
-
531
- let aumls = projectFiles.filter(file => file.endsWith(".auml"));
532
- for (let i = 0; i < aumls.length; i++) {
533
- let aumlFile = aumls[i];
534
- await this.generateManifestAumlApp(aumlFile, projectPath, manifest);
535
- }
536
-
537
- // Try add a default index
538
- let hasIndexApp = (manifest.index ?? '').trim() !== '';
539
- if (hasIndexApp === false) {
540
- manifest.index = manifest.applications.length > 0 ? manifest.applications[0].applicationName : undefined;
541
- }
542
- else {
543
- let appExists = manifest.applications.find(c => c.applicationName === manifest?.index) !== undefined;
544
- if (appExists === false) {
545
- manifest.index = undefined;
546
- }
547
- }
548
-
549
- //#endregion
550
-
551
- //#region scripts
552
- manifest.scripts = [];
553
- let scripts = projectFiles.filter(file => file.endsWith(".js") &&
554
- file.endsWith(".test.js") === false &&
555
- file.endsWith(".spec.js") === false &&
556
- file.endsWith(".config.js") === false);
557
-
558
- for (let i = 0; i < scripts.length; i++) {
559
- let scriptFile = scripts[i];
560
- manifest.scripts!.push({
561
- file: StringUtils.toRelativePath(scriptFile, projectPath)
562
- });
563
- }
564
- //#endregion
565
-
566
- return manifest;
567
- }
568
-
569
- static async parseBpmn(rawBpmn: string, provider: IWorkflowDataProvider): Promise<IProcess | null> {
570
-
571
- let validator = new BpmnToWorkflowDeserializer();
572
- let result = await validator.parse(rawBpmn, provider);
573
-
574
- //TODO handle warnigns and errors with BpmnValidator
575
- //return result.bpmn;
576
-
577
- return result ?? null;
578
-
579
- }
580
-
581
- private static async generateManifestBpmn(filepath: string, projectPath: string, manifest: ProjectManifest, provider: IWorkflowDataProvider, compilation: CompilationContext) {
582
- try {
583
-
584
- let bpmnRaw = (await FileUtils.readFileAsync(filepath))!;
585
-
586
- let model: IProcess | null = null;
587
- try
588
- {
589
- model = await this.parseBpmn(bpmnRaw, provider);
590
- if(!model) {
591
- compilation.errors.push(`Cannot parse bpmn file ${filepath}`);
592
- return;
593
- }
594
- }
595
- catch(e)
596
- {
597
- compilation.errors.push(`Error while parsing bpmn file ${filepath}: ${(e as Error).message}`);
598
- return;
599
- }
600
-
601
- let processHash = await StringUtils.sha256(bpmnRaw);
602
-
603
- let pools = model.items.filter((e) => e.type === WorkflowItemType.Pool) as IWorkflowPool[];
604
-
605
- // If there is only one process (ie no pools) we can rely on model.processId
606
- if (!pools || pools.length === 0) {
607
- // Default pool
608
- let pool: IWorkflowPool =
609
- {
610
- type: WorkflowItemType.Pool,
611
- orientation: 'horizontal',
612
- id: "defaultPool",
613
- processId: model.processId,
614
- name: model.processId,
615
- lanes: [],
616
- isTransaction: false,
617
- ignoreLogs: false,
618
- minThreads: 1,
619
- maxThreads: -1,
620
- poolType: WorkflowPoolType.Public,
621
- x: 0,
622
- y: 0,
623
- width: 800,
624
- height: 600,
625
- priority: WorkflowPoolPriority.Medium
626
- };
627
-
628
- this.generateManifestProcess(filepath, projectPath, pool, model.items, processHash, manifest);
629
-
630
- compilation.processes[model.processId] = model;
631
- }
632
- else {
633
- // Otherwise, we need to register each process
634
- for (let pool of pools) {
635
- //TODO other way than checking position ?
636
- //let poolElements = model.elements.filter((e: any) => e instanceof BpmnNodeModel && WorkflowUtils.belongsToPoolOrLane(pool, e));
637
- let poolElements = model.items;
638
- this.generateManifestProcess(filepath, projectPath, pool, poolElements, processHash, manifest);
639
-
640
- compilation.processes[pool.processId] = model;
641
- }
642
- }
643
-
644
-
645
- }
646
- catch (e) {
647
- compilation.errors.push(`Error while parsing bpmn file ${filepath}: ${(e as Error).message}`);
648
- }
649
- }
650
-
651
- private static generateManifestProcess(bpmnFilePath: string, projectPath: string, pool: IWorkflowPool, processElements: IWorkflowItem[], processHash: string, manifest: ProjectManifest) {
652
- try {
653
-
654
- // Process and StartEvents
655
- let process = new ProjectManifestProcess();
656
- process.file = StringUtils.toRelativePath(bpmnFilePath, projectPath);
657
-
658
- process.processName = `${pool.processId}`;
659
- let startEvents = processElements.filter(c =>
660
- c.type === WorkflowItemType.StartEvent ||
661
- c.type === WorkflowItemType.SignalStartEvent ||
662
- c.type === WorkflowItemType.ApiStartEvent ||
663
- c.type === WorkflowItemType.WebhookStartEvent ||
664
- c.type === WorkflowItemType.TimerStartEvent ||
665
- c.type === WorkflowItemType.MessageStartEvent ||
666
- c.type === WorkflowItemType.StandardStartEvent);
667
-
668
- let endEvents = processElements.filter(c =>
669
- c.type === WorkflowItemType.EndEvent ||
670
- c.type === WorkflowItemType.ErrorEndEvent ||
671
- c.type === WorkflowItemType.CancelEndEvent ||
672
- c.type === WorkflowItemType.SignalEndEvent ||
673
- c.type === WorkflowItemType.MessageEndEvent ||
674
- c.type === WorkflowItemType.WebhookEndEvent ||
675
- c.type === WorkflowItemType.StandardEndEvent ||
676
- c.type === WorkflowItemType.TerminateEndEvent ||
677
- c.type === WorkflowItemType.EscalationEndEvent ||
678
- c.type === WorkflowItemType.CompensationEndEvent ||
679
- c.type === WorkflowItemType.CallbackEndEvent);
680
-
681
- let signals = processElements.filter(c =>
682
- c.type === WorkflowItemType.SignalStartEvent ||
683
- c.type === WorkflowItemType.SignalEndEvent ||
684
- c.type === WorkflowItemType.ThrowSignalIntermediateEvent ||
685
- c.type === WorkflowItemType.CatchSignalIntermediateEvent);
686
-
687
- startEvents.forEach(startEvent => {
688
- let processStart = new ProjectManifestProcessStartEvent();
689
- processStart.kind = 'not implemented';
690
- switch (startEvent.type) {
691
- case WorkflowItemType.StartEvent: processStart.kind = 'manual'; break;
692
- case WorkflowItemType.SignalStartEvent: processStart.kind = 'signal'; break;
693
- case WorkflowItemType.ApiStartEvent: processStart.kind = 'api'; break;
694
- case WorkflowItemType.WebhookStartEvent: processStart.kind = 'webhook'; break;
695
- case WorkflowItemType.TimerStartEvent: processStart.kind = 'timer'; break;
696
- case WorkflowItemType.MessageStartEvent: processStart.kind = 'message'; break;
697
- case WorkflowItemType.StandardStartEvent: processStart.kind = 'standard'; break;
698
- }
699
- processStart.nodeId = startEvent.id;
700
- processStart.scope = pool.type;
701
-
702
- //TODO This not yet supported
703
- //processStart.roles = WorkflowUtils.getRolesOfStartNode(startEvent as BpmnNodeModel, pool.swimlanes);
704
-
705
- let evtNode = startEvent as IWorkflowEvent;
706
- let eventDefinitions = evtNode.eventDefinitions;
707
-
708
- // Only take the first definition into account
709
- //TODO ?
710
- if (eventDefinitions && eventDefinitions.length > 0) {
711
-
712
- let evtDef = eventDefinitions[0] as IWorkflowStandardEventDefinition;
713
- if (evtNode.outputs && evtNode.outputs.length > 0) {
714
-
715
- let parameters = evtNode.outputs[0].outputVariables;
716
- let input: IVariable =
717
- {
718
- id: 'input',
719
- value: '',
720
- name: 'input',
721
- type: VariableType.object,
722
- objectType: parameters,
723
- isSecret: false
724
- };
725
-
726
- processStart.input = ProjectCompilationUtils.ProcessVariableToJsonSchema(input);
727
- }
728
- else {
729
- processStart.input = {};
730
- }
731
- }
732
-
733
- process.startEvents.push(processStart);
734
- });
735
-
736
- signals.forEach(signal => {
737
-
738
- let processSignal = new ProjectManigestSignal();
739
- processSignal.type = 'not implemented';
740
-
741
- let eventNode = (signal as IWorkflowEvent);
742
-
743
- let signalModel = eventNode.eventDefinitions[0] as IWorkflowSignalEventDefinition;
744
- processSignal.signalId = signalModel.signalId;
745
-
746
- switch (signal.type) {
747
- case WorkflowItemType.SignalStartEvent: processSignal.type = 'start'; break;
748
- case WorkflowItemType.SignalEndEvent: processSignal.type = 'end'; break;
749
- case WorkflowItemType.ThrowSignalIntermediateEvent: processSignal.type = 'throw'; break;
750
- case WorkflowItemType.CatchSignalIntermediateEvent: processSignal.type = 'catch'; break;
751
- }
752
-
753
- process.signals.push(processSignal);
754
- });
755
-
756
- // Create a merged output from all end events of the process
757
- //let mergedOutput: CodeVariable[] = [];
758
-
759
- //TODO not sure we need the end event, we don't really use it and it takes space in the manifest
760
- /*
761
- endEvents.forEach(endEvent => {
762
-
763
- let eventDefinitions = (endEvent as EventNodeModel).eventDefinitions;
764
-
765
- // We are only interested in data definition, other definitions don't change the process output
766
- let dataDef = eventDefinitions.find((x: any) => x instanceof DataEventDefinitionModel) as DataEventDefinitionModel;
767
-
768
- // For api end events...
769
- if (dataDef === undefined) {
770
- dataDef = eventDefinitions.find((x: any) => x instanceof CallbackEventDefinitionModel) as CallbackEventDefinitionModel;
771
- }
772
-
773
- // Add parameters to the merged output
774
- if (dataDef)
775
- mergedOutput = mergedOutput.concat(dataDef.parameters as any);
776
- });
777
-
778
-
779
- // Remove duplicates (variables with the same name)
780
- let seen = new Set();
781
- mergedOutput = mergedOutput.filter(x => seen.has(x.name) ? false : seen.add(x.name));
782
-
783
- let output: CodeVariable = {
784
- name: 'output',
785
- type: VariableType.MAP,
786
- childVariables: mergedOutput,
787
- listElement: null,
788
- isSecret: false,
789
- enum: null
790
- };
791
- process.endEvent = ProjectCompilationUtils.ProcessVariableToJsonSchema(output);
792
- */
793
-
794
- process.priority = pool.priority;
795
- process.minThreads = pool.minThreads;
796
- process.maxThreads = pool.maxThreads;
797
-
798
- // Each process has a unique hash that can be used to optimize the deployment
799
- //@ts-ignore
800
- process.hash = processHash;
801
-
802
- manifest.processes.push(process);
803
-
804
- // Apis
805
- processElements.forEach(e => {
806
-
807
- let eventDefinitions = (e as IWorkflowEvent).eventDefinitions;
808
- if(eventDefinitions)
809
- {
810
- eventDefinitions.forEach((def: any) => {
811
- if(def.route && def.method) {
812
- let api = new ProjectManifestApi();
813
- api.method = def.method;
814
- api.path = def.route
815
- api.bpmn = process.processName;
816
- api.startNode = e.id;
817
- api.accept = def.accept;
818
-
819
- (api as any).whitelist = def.whitelist;
820
-
821
- api.priority = pool.priority;
822
- api.minThreads = pool.minThreads;
823
- api.maxThreads = pool.maxThreads;
824
-
825
- def.parameters.forEach((p: any) => {
826
- // TODO ?
827
- });
828
- manifest.apis.push(api);
829
- }
830
-
831
- });
832
- }
833
- });
834
-
835
-
836
- }
837
- catch (e) {
838
- console.log("Cannot save bpmn", bpmnFilePath, e)
839
- }
840
- }
841
-
842
- public static ProcessVariableToJsonSchema(variable: IVariable): JSONSchema7 {
843
- let schema: JSONSchema7 = {};
844
-
845
- schema.type = ProjectCompilationUtils.ProcessVariableTypeToJsonSchemaTypeName(variable.type);
846
-
847
- if (variable.type === VariableType.object) {
848
- schema.properties = {};
849
- for (let child of variable.objectType ?? []) {
850
- schema.properties[child.name] = ProjectCompilationUtils.ProcessVariableToJsonSchema(child);
851
- }
852
- }
853
- else if (variable.type === VariableType.array) {
854
- if (variable.arrayType)
855
- schema.items = ProjectCompilationUtils.ProcessVariableToJsonSchema(variable.arrayType);
856
- }
857
-
858
- return schema;
859
- }
860
-
861
- public static ProcessVariableTypeToJsonSchemaTypeName(type: VariableType): JSONSchema7TypeName {
862
- switch (type) {
863
- case VariableType.string:
864
- return "string";
865
- case VariableType.int:
866
- return "integer";
867
- case VariableType.float:
868
- return "number";
869
- case VariableType.bool:
870
- return "boolean";
871
- case VariableType.array:
872
- return "array";
873
- case VariableType.object:
874
- return "object";
875
- case VariableType.enum:
876
- return "string";
877
- case VariableType.function:
878
- return "object";
879
- }
880
- }
881
-
882
- private static async generateManifestReactApp(filePath: string, projectPath: string, manifest: ProjectManifest) {
883
- try {
884
- let ext = filePath.split('.').pop()!;
885
- let manifestPath = filePath.replace(`.${ext}`, '.manifest.json');
886
- if (await FileUtils.exists(manifestPath) === false) {
887
- return;
888
- }
889
- let appManifest: ApplicationManifest = JSON.parse((await FileUtils.readFileAsync(manifestPath))!);
890
- let fileName = StringUtils.getFilenameFromUri(filePath);
891
- let reactAppmanifest = new ProjectManifestReactApplication();
892
- reactAppmanifest.file = StringUtils.toRelativePath(filePath, projectPath);
893
- reactAppmanifest.roles = appManifest.auml?.roles ?? [];
894
- reactAppmanifest.scope = appManifest.auml?.scope ?? 'Public';
895
- reactAppmanifest.applicationName = fileName.replace(`.${ext}`, '');
896
- manifest.reactApplications = manifest.reactApplications ?? [];
897
- manifest.reactApplications.push(reactAppmanifest);
898
- }
899
- catch (e) {
900
- console.error("Cannot generateManifestReactApp", filePath, e);
901
- }
902
- }
903
-
904
- private static async generateManifestAumlApp(filePath: string, projectPath: string, manifest: ProjectManifest) {
905
- try {
906
- // Read file
907
-
908
- let auml = await FileUtils.readFileAsync(filePath);
909
- if (!auml) {
910
- return;
911
- }
912
-
913
- let fileName = filePath.includes("/") ? filePath.split("/").pop()! :
914
- filePath.includes("\\") ? filePath.split("\\").pop()! :
915
- filePath;
916
-
917
- // Figure out if this is an application or an express component
918
-
919
- let trimmedAuml = auml.replace(/^\s+|\s+$/g, '');
920
-
921
- if (trimmedAuml.startsWith("<Application")) {
922
-
923
- // Application
924
- let application = new ProjectManifestApplication();
925
- application.file = StringUtils.toRelativePath(filePath, projectPath);
926
- application.roles = []
927
-
928
- let match = auml.match(/<Application[^>]*?Name="(.*?)"/);
929
-
930
- application.applicationName = fileName.replace(".auml", "");
931
- if (match) {
932
-
933
- let appName = match[1];
934
- if (appName) {
935
- application.applicationName = appName;
936
- }
937
- }
938
-
939
- // Roles are now defined manually in manifest editor
940
- // Search for roles in the application
941
- match = auml.match(/<Application[^>]*?Role="(.*?)"/);
942
-
943
- if (match) {
944
- application.roles = [match[1]];
945
- }
946
-
947
- // Search for a scope in the application
948
- match = auml.match(/<Application[^>]*?Scope="(.*?)"/);
949
-
950
- if (match) {
951
-
952
- let scope = match[1];
953
-
954
- if (scope !== "Internal" && scope !== "External")
955
- application.scope = "Public"; // if scope is not recognized, default to public
956
- else
957
- application.scope = scope;
958
- }
959
- else {
960
- application.scope = "Public";
961
- }
962
-
963
- manifest.applications.push(application);
964
- }
965
- else if (trimmedAuml.startsWith("<Component")) {
966
- let findComponentNameRegexp = /<Component[^>]*?Name="([\S]*?)"/m;
967
-
968
- let match = auml.match(findComponentNameRegexp);
969
-
970
- if (match) {
971
- // Express component
972
- let expressComponent = new ProjectManifestExpressComponent();
973
- expressComponent.componentName = match[1];
974
- expressComponent.file = StringUtils.toRelativePath(filePath, projectPath);
975
- manifest.expressComponents!.push(expressComponent);
976
- }
977
- }
978
- else if (trimmedAuml.startsWith("<PDF")) {
979
- let pdf = new ProjectManifestPdf();
980
- pdf.name = fileName.replace(".auml", "");
981
- pdf.file = StringUtils.toRelativePath(filePath, projectPath);
982
- manifest.pdfs!.push(pdf)
983
- }
984
- else if (trimmedAuml.startsWith("<Email")) {
985
- let email = new ProjectManifestEmail();
986
- email.name = fileName.replace(".auml", "");
987
- email.file = StringUtils.toRelativePath(filePath, projectPath);
988
- manifest.emails!.push(email)
989
- }
990
- else if (trimmedAuml.startsWith("<HTML")) {
991
- let html = new ProjectManifestHtml();
992
- html.name = fileName.replace(".auml", "");
993
- html.file = StringUtils.toRelativePath(filePath, projectPath);
994
- manifest.htmls!.push(html)
995
- }
996
- }
997
- catch (e) {
998
- console.error("Cannot save auml", filePath, e);
999
- }
1000
- }
1001
-
1002
- }