@microtronics/studio-cli 0.33.0 → 0.35.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.
package/CHANGELOG.md CHANGED
@@ -1,12 +1,27 @@
1
1
  # Changelog
2
2
 
3
- ## 0.33.0 (2025-09-01)
3
+ ## 0.35.0 (2025-09-12)
4
4
 
5
- ### Features
5
+ ### Bug Fixes
6
6
 
7
- * **cli:** add description to CLI tool usage ([97e8562](https://bitbucket.org/microtronics-core/vscode-studio/commits/97e85625a7c6d11070e2713f3ec3d2c24691854a))
8
- * **cli:** add support for parsing unknown long options ([fa6523c](https://bitbucket.org/microtronics-core/vscode-studio/commits/fa6523caef791d508b31ed5d563452b616e99ae0))
9
- * **cli:** support custom ini file in manifest ([5ae2741](https://bitbucket.org/microtronics-core/vscode-studio/commits/5ae2741ad379258fee95311fd28acc8b3dd1bdb6))
7
+ * **cli:** expected test fixtures ([0edb940](https://bitbucket.org/microtronics-core/vscode-studio/commits/0edb940c79aacb0cc631786f141e3cb28ff5cb78))
8
+ * **cli:** replaced shallow clones with structured clones for API method objects ([f8b3a88](https://bitbucket.org/microtronics-core/vscode-studio/commits/f8b3a88b5a81ad3aa5d422a7f36ea1055068b921))
9
+
10
+ ## 0.34.0 (2025-09-12)
11
+
12
+ ### Features
13
+
14
+ * **cli:** add App Center routing support in API generator ([6586c8b](https://bitbucket.org/microtronics-core/vscode-studio/commits/6586c8bee9a9bdf0a803b5c0e33d04fa22092d8e))
15
+ * **cli:** add support for analytics project type in manifest ([ff0f354](https://bitbucket.org/microtronics-core/vscode-studio/commits/ff0f354f3d95028a889ca87495b24ad36800b1b1))
16
+ * **cli:** enhance path generation for analytics projects ([bb0936a](https://bitbucket.org/microtronics-core/vscode-studio/commits/bb0936afdb385cc67569305c1363617df19b258a))
17
+
18
+ ## 0.33.0 (2025-09-01)
19
+
20
+ ### Features
21
+
22
+ * **cli:** add description to CLI tool usage ([97e8562](https://bitbucket.org/microtronics-core/vscode-studio/commits/97e85625a7c6d11070e2713f3ec3d2c24691854a))
23
+ * **cli:** add support for parsing unknown long options ([fa6523c](https://bitbucket.org/microtronics-core/vscode-studio/commits/fa6523caef791d508b31ed5d563452b616e99ae0))
24
+ * **cli:** support custom ini file in manifest ([5ae2741](https://bitbucket.org/microtronics-core/vscode-studio/commits/5ae2741ad379258fee95311fd28acc8b3dd1bdb6))
10
25
  * **cli:** support manifest overwrites with deep merge ([10b69e2](https://bitbucket.org/microtronics-core/vscode-studio/commits/10b69e29c9426bec08022610b66e85b86cee79c9))
11
26
 
12
27
  ## 0.32.3 (2025-06-11)
@@ -1166,7 +1166,7 @@ export declare namespace DDE {
1166
1166
  * @param ddeHistory
1167
1167
  * @constructor
1168
1168
  */
1169
- const PreCompiler: (libraries: DDE_Core.LibraryFile[], ddeHistory: HistoryJson | null) => YamlPreCompiler.PreCompiler;
1169
+ const PreCompiler: (manifest: Manifest.Manifest, libraries: DDE_Core.LibraryFile[], ddeHistory: HistoryJson | null) => YamlPreCompiler.PreCompiler;
1170
1170
  /**
1171
1171
  * File Generators
1172
1172
  */
@@ -2171,7 +2171,8 @@ export declare namespace Manifest {
2171
2171
  export enum ProjectTypes {
2172
2172
  app = "app",
2173
2173
  library = "library",
2174
- addon = "addon"
2174
+ addon = "addon",
2175
+ analytics = "analytics"
2175
2176
  }
2176
2177
  export interface Manifest {
2177
2178
  version: string;
@@ -2270,6 +2271,11 @@ export declare namespace Manifest {
2270
2271
  * @param manifest
2271
2272
  */
2272
2273
  export function isApp(manifest: Manifest.Manifest): boolean;
2274
+ /**
2275
+ * Check if the current project is an analytics project
2276
+ * @param manifest
2277
+ */
2278
+ export function isAnalytics(manifest: Manifest.Manifest): boolean;
2273
2279
  /**
2274
2280
  * Validates if the properties within
2275
2281
  * @param cwd
@@ -4714,13 +4720,14 @@ declare interface XMLField {
4714
4720
 
4715
4721
  declare namespace YamlPreCompiler {
4716
4722
  class PreCompiler {
4723
+ private manifest;
4717
4724
  private libraryFiles;
4718
4725
  private ddeHistory;
4719
4726
  private readonly fieldNamesPerContainer;
4720
4727
  private readonly usedContainerNames;
4721
4728
  private fileDiagnostics;
4722
4729
  private readonly libraryDDE;
4723
- constructor(libraryFiles: DDE_Core.LibraryFile[], ddeHistory: DDE.HistoryJson | null);
4730
+ constructor(manifest: Manifest.Manifest, libraryFiles: DDE_Core.LibraryFile[], ddeHistory: DDE.HistoryJson | null);
4724
4731
  /**
4725
4732
  * Parse the given library files and return the diagnostics
4726
4733
  */
@@ -4732,10 +4739,12 @@ declare namespace YamlPreCompiler {
4732
4739
  diagnostics: Diagnostics.File[];
4733
4740
  };
4734
4741
  /**
4735
- * Parse a given yaml file
4736
- * @param filePath
4737
- * @param yaml
4738
- * @param libraryName
4742
+ * Parses a YAML file and validates its content to produce a structured JSON object along with diagnostic information.
4743
+ *
4744
+ * @param filePath The URI of the YAML file being parsed.
4745
+ * @param yaml The raw YAML content to parse.
4746
+ * @param libraryName An optional parameter specifying the library name for additional validation.
4747
+ * @return An object containing the parsed DDE JSON and an array of diagnostic messages related to the file.
4739
4748
  */
4740
4749
  parseYamlFile(filePath: URI, yaml: string, libraryName?: string): {
4741
4750
  parsedDDE: DDEJson;
package/out/dde/dde.js CHANGED
@@ -93,9 +93,10 @@ var DDE;
93
93
  * @param fileName
94
94
  */
95
95
  async function compileDDE(cwd, fs, logger, fileName = defaultFiles_1.DefaultFiles.fileNames.mainDDE) {
96
+ const manifest = await manifest_1.Manifest.read(cwd, fs);
96
97
  const validated = await validate(cwd, fs, logger, fileName);
97
98
  const historyJson = await getHistoryJson(cwd, fs, logger);
98
- const preCompiler = new yamlDdePreCompiler_1.YamlPreCompiler.PreCompiler(validated.libraryFiles, historyJson);
99
+ const preCompiler = new yamlDdePreCompiler_1.YamlPreCompiler.PreCompiler(manifest, validated.libraryFiles, historyJson);
99
100
  const libraryDiagnostics = preCompiler.parseLibraries();
100
101
  const librariesHaveError = libraryDiagnostics.find(diagnostics => diagnostics.level === 'error');
101
102
  if (librariesHaveError) {
@@ -137,8 +138,8 @@ var DDE;
137
138
  * @param ddeHistory
138
139
  * @constructor
139
140
  */
140
- DDE.PreCompiler = (libraries, ddeHistory) => {
141
- return new yamlDdePreCompiler_1.YamlPreCompiler.PreCompiler(libraries, ddeHistory);
141
+ DDE.PreCompiler = (manifest, libraries, ddeHistory) => {
142
+ return new yamlDdePreCompiler_1.YamlPreCompiler.PreCompiler(manifest, libraries, ddeHistory);
142
143
  };
143
144
  /**
144
145
  * File Generators
@@ -33,6 +33,7 @@ var fieldTypeDefinitions = coreDefinitions_1.DDE_Core.fieldTypeDefinitions;
33
33
  const manifest_1 = require("../../manifest");
34
34
  const contextIdRef = { $ref: '#/components/parameters/contextId' };
35
35
  const addonIdRef = { $ref: '#/components/parameters/addonId' };
36
+ const appIdRef = { $ref: '#/components/parameters/appId' };
36
37
  const histdataResponseRef = { $ref: '#/components/schemas/histdataResponse' };
37
38
  const histdataRequestRef = { $ref: '#/components/schemas/histdataRequest' };
38
39
  const rapidM2MStampRef = { $ref: '#/components/schemas/rapidM2MStamp' };
@@ -52,6 +53,15 @@ const defaultComponents = {
52
53
  type: 'string'
53
54
  },
54
55
  required: true
56
+ },
57
+ appId: {
58
+ name: 'appUid',
59
+ in: 'path',
60
+ description: 'The app uid',
61
+ schema: {
62
+ type: 'string'
63
+ },
64
+ required: true
55
65
  }
56
66
  },
57
67
  schemas: {
@@ -182,6 +192,7 @@ function generateAPIFromMap(ddeMap, manifest, context = null) {
182
192
  Object.assign(apiDescription.components, structuredClone(defaultAppComponents));
183
193
  }
184
194
  const isAddon = manifest_1.Manifest.isAddon(manifest);
195
+ const isAnalytics = manifest_1.Manifest.isAnalytics(manifest);
185
196
  if (isAddon) {
186
197
  // add addonUid parameter if this is an addon project
187
198
  apiDescription.components.parameters['addonId'] = {
@@ -203,13 +214,17 @@ function generateAPIFromMap(ddeMap, manifest, context = null) {
203
214
  }
204
215
  generateTagDescriptionFromContainer(apiDescription, containerDefinition);
205
216
  generateSchemasFromContainer(apiDescription, containerDefinition, context);
206
- generatePathInformationFromContainer(apiDescription, containerDefinition, isAddon);
217
+ generatePathInformationFromContainer(apiDescription, containerDefinition, isAddon, isAnalytics);
207
218
  // legacy routes
208
- if (!isAddon) {
219
+ if (!isAddon && !isAnalytics) {
209
220
  generateLegacySchemasFromContainer(apiDescription, containerDefinition, context);
210
221
  generateLegacyPathInformationFromContainer(apiDescription, containerDefinition);
211
222
  }
212
223
  }
224
+ apiDescription.tags.push({
225
+ name: 'app-center',
226
+ description: 'App Center related routes'
227
+ });
213
228
  return (0, yaml_1.stringify)(apiDescription);
214
229
  }
215
230
  function generateTagDescriptionFromContainer(apiDescription, containerDefinition) {
@@ -451,23 +466,45 @@ function generateLegacyPathInformationFromContainer(apiDescription, containerDef
451
466
  }
452
467
  apiDescription.paths[apiPath] = apiPathMethods;
453
468
  }
454
- function generatePathInformationFromContainer(apiDescription, containerDefinition, isAddonProject = false) {
469
+ function generatePathInformationFromContainer(apiDescription, containerDefinition, isAddonProject = false, isAnalyticsProject = false) {
455
470
  const isTimeseriesContainer = containerDefinition.containerType === ContainerType.hx;
456
471
  const containerType = isTimeseriesContainer ? 'timeseries' : 'containers';
457
- const pathParameters = [contextIdRef];
458
- let apiPath = `/api/1/sites/{siteUid}/${containerType}/${containerDefinition.title}`;
472
+ let apiPathOptions = [];
473
+ const containerRouteTagName = tagNameFromContainerTitle(containerDefinition.title);
474
+ // analytic projects do not have any site
475
+ if (!isAnalyticsProject) {
476
+ apiPathOptions.push({
477
+ path: `/api/1/sites/{siteUid}/${containerType}/${containerDefinition.title}`,
478
+ params: [contextIdRef],
479
+ tags: [containerRouteTagName]
480
+ });
481
+ }
459
482
  if (isAddonProject) {
460
- apiPath = `/api/1/sites/{siteUid}/addons/{addonUid}/${containerType}/${containerDefinition.title}`;
461
- pathParameters.push(addonIdRef);
483
+ apiPathOptions = [
484
+ {
485
+ path: `/api/1/sites/{siteUid}/addons/{addonUid}/${containerType}/${containerDefinition.title}`,
486
+ params: [addonIdRef],
487
+ tags: [containerRouteTagName]
488
+ }
489
+ ];
490
+ }
491
+ const appCenterPathOptions = {
492
+ path: `/api/1/app-center/apps/{appUid}/${containerType}/${containerDefinition.title}`,
493
+ params: [appIdRef],
494
+ tags: ['app-center']
495
+ };
496
+ if (isAnalyticsProject) {
497
+ appCenterPathOptions.tags.push(containerRouteTagName);
462
498
  }
499
+ apiPathOptions.push(appCenterPathOptions);
463
500
  const apiPathMethods = {
464
501
  get: undefined,
465
502
  put: undefined
466
503
  };
467
504
  const getMethod = {
468
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
505
+ tags: [],
469
506
  summary: `Retrieve the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
470
- parameters: [...pathParameters],
507
+ parameters: [],
471
508
  responses: {
472
509
  '200': {
473
510
  ...successfulOperationResponse,
@@ -498,9 +535,9 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
498
535
  apiPathMethods.get = getMethod;
499
536
  if ([TransferDirection.down, TransferDirection.backendOnly].includes(containerDefinition.transferDirection)) {
500
537
  apiPathMethods.put = {
501
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
538
+ tags: [],
502
539
  summary: `Modify the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
503
- parameters: [...pathParameters],
540
+ parameters: [],
504
541
  requestBody: {
505
542
  content: {
506
543
  'application/json': {
@@ -521,14 +558,20 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
521
558
  }
522
559
  };
523
560
  }
524
- apiDescription.paths[apiPath] = apiPathMethods;
561
+ apiPathOptions.forEach(path => {
562
+ const methods = structuredClone(apiPathMethods);
563
+ Object.values(methods).forEach(value => {
564
+ value?.parameters?.unshift(...path.params);
565
+ value?.tags?.unshift(...path.tags);
566
+ });
567
+ apiDescription.paths[path.path] = methods;
568
+ });
525
569
  }
526
570
  if (containerDefinition.containerType === ContainerType.hx) {
527
571
  apiPathMethods.get = {
528
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
572
+ tags: [],
529
573
  summary: `Get the historical data of "${containerDefinition.title}"`,
530
574
  parameters: [
531
- ...pathParameters,
532
575
  {
533
576
  name: 'json',
534
577
  in: 'query',
@@ -555,16 +598,23 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
555
598
  ...defaultResponseCodes
556
599
  }
557
600
  };
558
- apiDescription.paths[apiPath] = apiPathMethods;
559
- const youngestPath = `${apiPath}/youngest`;
601
+ // only site and addons support timeseries
602
+ const pathOptions = apiPathOptions[0];
603
+ const methods = structuredClone(apiPathMethods);
604
+ Object.values(methods).forEach(value => {
605
+ value?.parameters?.unshift(...pathOptions.params);
606
+ value?.tags?.unshift(...pathOptions.tags);
607
+ });
608
+ apiDescription.paths[pathOptions.path] = methods;
609
+ const youngestPath = `${pathOptions.path}/youngest`;
560
610
  const youngestMethod = {
561
- get: getMethod
611
+ get: structuredClone(methods.get)
562
612
  };
563
613
  youngestMethod.get.summary = `Get the youngest record of "${containerDefinition.title}" container`;
564
614
  apiDescription.paths[youngestPath] = youngestMethod;
565
- const oldestPath = `${apiPath}/oldest`;
615
+ const oldestPath = `${pathOptions.path}/oldest`;
566
616
  const oldestMethod = {
567
- get: getMethod
617
+ get: structuredClone(methods.get)
568
618
  };
569
619
  oldestMethod.get.summary = `Get the oldest record of "${containerDefinition.title}" container`;
570
620
  apiDescription.paths[oldestPath] = oldestMethod;
@@ -18,16 +18,19 @@ var CONFIG_CONTAINER = coreDefinitions_1.DDE_Core.CONFIG_CONTAINER;
18
18
  var TransferDirection = coreDefinitions_1.DDE_Core.TransferDirection;
19
19
  var fieldTypeDefinitions = coreDefinitions_1.DDE_Core.fieldTypeDefinitions;
20
20
  var yamlRangeToLineInformation = helper_1.Helper.yamlRangeToLineInformation;
21
+ const api_1 = require("../api");
21
22
  var YamlPreCompiler;
22
23
  (function (YamlPreCompiler) {
23
24
  class PreCompiler {
25
+ manifest;
24
26
  libraryFiles;
25
27
  ddeHistory;
26
28
  fieldNamesPerContainer = {};
27
29
  usedContainerNames = {};
28
30
  fileDiagnostics = [];
29
31
  libraryDDE = [];
30
- constructor(libraryFiles, ddeHistory) {
32
+ constructor(manifest, libraryFiles, ddeHistory) {
33
+ this.manifest = manifest;
31
34
  this.libraryFiles = libraryFiles;
32
35
  this.ddeHistory = ddeHistory;
33
36
  }
@@ -63,10 +66,12 @@ var YamlPreCompiler;
63
66
  return { ddeJSON: mergedDDE, diagnostics };
64
67
  }
65
68
  /**
66
- * Parse a given yaml file
67
- * @param filePath
68
- * @param yaml
69
- * @param libraryName
69
+ * Parses a YAML file and validates its content to produce a structured JSON object along with diagnostic information.
70
+ *
71
+ * @param filePath The URI of the YAML file being parsed.
72
+ * @param yaml The raw YAML content to parse.
73
+ * @param libraryName An optional parameter specifying the library name for additional validation.
74
+ * @return An object containing the parsed DDE JSON and an array of diagnostic messages related to the file.
70
75
  */
71
76
  parseYamlFile(filePath, yaml, libraryName) {
72
77
  this.fileDiagnostics.length = 0;
@@ -444,6 +449,16 @@ var YamlPreCompiler;
444
449
  ...yamlRangeToLineInformation(lineCounter, container.key.range)
445
450
  });
446
451
  }
452
+ if (api_1.Manifest.isAnalytics(this.manifest) &&
453
+ ![DDEContainerName.relations, DDEContainerName.storage].includes(containerName)) {
454
+ const invalidName = containerName.includes('_') ? containerName.split('_')[1] : containerName;
455
+ this.fileDiagnostics.push({
456
+ file: sourceUri,
457
+ level: 'error',
458
+ message: `"${invalidName}" not allowed within an analytics app. Only "relations" and "storage" are allowed.`,
459
+ ...yamlRangeToLineInformation(lineCounter, container.key.range)
460
+ });
461
+ }
447
462
  }
448
463
  validateContainerFields(yamlDoc, sourceUri, container, lineCounter) {
449
464
  const containerFields = container.value; //(container.value as YAMLMap).get('fields', true);
package/out/manifest.js CHANGED
@@ -16,6 +16,7 @@ const deepmerge_ts_1 = require("deepmerge-ts");
16
16
  const PRODUCT_ID_SUPPORTED = '52v007';
17
17
  const SEMVER_SUPPORTED = '53v001';
18
18
  const ADDON_SUPPORTED = '54v001';
19
+ const ANALYTICS_SUPPORTED = '56v001';
19
20
  // Cache to reduce the disk read requests
20
21
  const manifestCache = {
21
22
  _manifest: null,
@@ -66,6 +67,7 @@ var Manifest;
66
67
  ProjectTypes["app"] = "app";
67
68
  ProjectTypes["library"] = "library";
68
69
  ProjectTypes["addon"] = "addon";
70
+ ProjectTypes["analytics"] = "analytics";
69
71
  })(ProjectTypes = Manifest.ProjectTypes || (Manifest.ProjectTypes = {}));
70
72
  let BloAccessLevel;
71
73
  (function (BloAccessLevel) {
@@ -156,6 +158,14 @@ var Manifest;
156
158
  return manifest.type === ProjectTypes.app;
157
159
  }
158
160
  Manifest.isApp = isApp;
161
+ /**
162
+ * Check if the current project is an analytics project
163
+ * @param manifest
164
+ */
165
+ function isAnalytics(manifest) {
166
+ return manifest.type === ProjectTypes.analytics;
167
+ }
168
+ Manifest.isAnalytics = isAnalytics;
159
169
  /**
160
170
  * Validates if the properties within
161
171
  * @param cwd
@@ -327,7 +337,7 @@ var Manifest;
327
337
  throw new Error('Setting "engines.backend" is invalid');
328
338
  }
329
339
  validateHwFwOrProductId(manifest);
330
- validateAddonSupported(manifest);
340
+ validateAddonOrAnalyticsSupported(manifest);
331
341
  }
332
342
  Manifest.validateEngineSettings = validateEngineSettings;
333
343
  /**
@@ -342,9 +352,10 @@ var Manifest;
342
352
  manifest = await Manifest.read(cwd, fs);
343
353
  }
344
354
  const isAddon = Manifest.isAddon(manifest);
355
+ const isAnalytics = Manifest.isAnalytics(manifest);
345
356
  switch (apmPart) {
346
357
  case apm_1.APM.Part.dlo:
347
- return !!manifest?.dlo?.mainFile && !isAddon;
358
+ return !!manifest?.dlo?.mainFile && !isAddon && !isAnalytics;
348
359
  case apm_1.APM.Part.blo:
349
360
  return !!(manifest.blo?.buildCommand && manifest.blo?.workingPath);
350
361
  case apm_1.APM.Part.pov:
@@ -403,19 +414,20 @@ function validateHwFwOrProductId(manifest) {
403
414
  throw new Error(`"engines.productId" requires "engines.backend" to be "${PRODUCT_ID_SUPPORTED}"`);
404
415
  }
405
416
  }
406
- function validateAddonSupported(manifest) {
407
- if (!Manifest.isAddon(manifest)) {
417
+ function validateAddonOrAnalyticsSupported(manifest) {
418
+ if (!Manifest.isAddon(manifest) && !Manifest.isAnalytics(manifest)) {
408
419
  return;
409
420
  }
410
421
  const { backend } = manifest.engines;
411
- if (!backend || backend < ADDON_SUPPORTED) {
412
- throw new Error(`Project type "addon" requires "engines.backend" to be ">=${ADDON_SUPPORTED}"`);
422
+ const requiredBackend = Manifest.isAddon(manifest) ? ADDON_SUPPORTED : ANALYTICS_SUPPORTED;
423
+ if (!backend || backend < requiredBackend) {
424
+ throw new Error(`Project type "${manifest.type}" requires "engines.backend" to be ">=${requiredBackend}"`);
413
425
  }
414
426
  if (manifest.dlo) {
415
- throw new Error('For project type "addon", "dlo" is not supported!"');
427
+ throw new Error(`For project type "${manifest.type}", "dlo" is not supported!"`);
416
428
  }
417
429
  if (manifest.dpid) {
418
- throw new Error('For project type "addon", "dpid" is not supported!"');
430
+ throw new Error(`For project type "${manifest.type}", "dpid" is not supported!"`);
419
431
  }
420
432
  }
421
433
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microtronics/studio-cli",
3
- "version": "0.33.0",
3
+ "version": "0.35.0",
4
4
  "description": "Microtronics Studio CLI Tool",
5
5
  "main": "./out/api.js",
6
6
  "typings": "./dist/studio-cli.d.ts",