@microtronics/studio-cli 0.32.3 → 0.34.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,4 +1,21 @@
1
+ # Changelog
2
+
3
+ ## 0.34.0 (2025-09-12)
4
+
5
+ ### Features
6
+
7
+ * **cli:** add App Center routing support in API generator ([6586c8b](https://bitbucket.org/microtronics-core/vscode-studio/commits/6586c8bee9a9bdf0a803b5c0e33d04fa22092d8e))
8
+ * **cli:** add support for analytics project type in manifest ([ff0f354](https://bitbucket.org/microtronics-core/vscode-studio/commits/ff0f354f3d95028a889ca87495b24ad36800b1b1))
9
+ * **cli:** enhance path generation for analytics projects ([bb0936a](https://bitbucket.org/microtronics-core/vscode-studio/commits/bb0936afdb385cc67569305c1363617df19b258a))
1
10
 
11
+ ## 0.33.0 (2025-09-01)
12
+
13
+ ### Features
14
+
15
+ * **cli:** add description to CLI tool usage ([97e8562](https://bitbucket.org/microtronics-core/vscode-studio/commits/97e85625a7c6d11070e2713f3ec3d2c24691854a))
16
+ * **cli:** add support for parsing unknown long options ([fa6523c](https://bitbucket.org/microtronics-core/vscode-studio/commits/fa6523caef791d508b31ed5d563452b616e99ae0))
17
+ * **cli:** support custom ini file in manifest ([5ae2741](https://bitbucket.org/microtronics-core/vscode-studio/commits/5ae2741ad379258fee95311fd28acc8b3dd1bdb6))
18
+ * **cli:** support manifest overwrites with deep merge ([10b69e2](https://bitbucket.org/microtronics-core/vscode-studio/commits/10b69e29c9426bec08022610b66e85b86cee79c9))
2
19
 
3
20
  ## 0.32.3 (2025-06-11)
4
21
 
@@ -185,7 +202,7 @@
185
202
 
186
203
  ## 0.19.1 (2025-02-20)
187
204
 
188
- # Changelog
205
+
189
206
 
190
207
  ## 0.19.0 (2025-02-19)
191
208
 
@@ -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;
@@ -2196,6 +2197,7 @@ export declare namespace Manifest {
2196
2197
  dlo?: {
2197
2198
  mainFile: string;
2198
2199
  compileOptions: string[];
2200
+ iniFile?: string;
2199
2201
  };
2200
2202
  registry?: {
2201
2203
  id?: string;
@@ -2234,6 +2236,8 @@ export declare namespace Manifest {
2234
2236
  embedded = "$embedded",
2235
2237
  pure = "$pure"
2236
2238
  }
2239
+ export function setOverwrites(manifest: Partial<Manifest.Manifest> | null): void;
2240
+ export function clearOverwrites(): void;
2237
2241
  /**
2238
2242
  * Read the studio.json manifest and parse it
2239
2243
  * @param cwd
@@ -2267,6 +2271,11 @@ export declare namespace Manifest {
2267
2271
  * @param manifest
2268
2272
  */
2269
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;
2270
2279
  /**
2271
2280
  * Validates if the properties within
2272
2281
  * @param cwd
@@ -4711,13 +4720,14 @@ declare interface XMLField {
4711
4720
 
4712
4721
  declare namespace YamlPreCompiler {
4713
4722
  class PreCompiler {
4723
+ private manifest;
4714
4724
  private libraryFiles;
4715
4725
  private ddeHistory;
4716
4726
  private readonly fieldNamesPerContainer;
4717
4727
  private readonly usedContainerNames;
4718
4728
  private fileDiagnostics;
4719
4729
  private readonly libraryDDE;
4720
- constructor(libraryFiles: DDE_Core.LibraryFile[], ddeHistory: DDE.HistoryJson | null);
4730
+ constructor(manifest: Manifest.Manifest, libraryFiles: DDE_Core.LibraryFile[], ddeHistory: DDE.HistoryJson | null);
4721
4731
  /**
4722
4732
  * Parse the given library files and return the diagnostics
4723
4733
  */
@@ -4729,10 +4739,12 @@ declare namespace YamlPreCompiler {
4729
4739
  diagnostics: Diagnostics.File[];
4730
4740
  };
4731
4741
  /**
4732
- * Parse a given yaml file
4733
- * @param filePath
4734
- * @param yaml
4735
- * @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.
4736
4748
  */
4737
4749
  parseYamlFile(filePath: URI, yaml: string, libraryName?: string): {
4738
4750
  parsedDDE: DDEJson;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseUnknownLongOptions = parseUnknownLongOptions;
4
+ /**
5
+ * Set a nested value in an object by a dot-separated path.
6
+ * Example: setByDotPath(obj, 'dlo.mainFile', 'x') => obj = { dlo: { mainFile: 'x' } }
7
+ */
8
+ function setByDotPath(target, path, value) {
9
+ const parts = path.split('.').filter(Boolean);
10
+ let cur = target;
11
+ for (let i = 0; i < parts.length; i++) {
12
+ const key = parts[i];
13
+ const isLeaf = i === parts.length - 1;
14
+ if (isLeaf) {
15
+ cur[key] = value;
16
+ }
17
+ else {
18
+ if (typeof cur[key] !== 'object' || cur[key] === null || Array.isArray(cur[key])) {
19
+ cur[key] = {};
20
+ }
21
+ cur = cur[key];
22
+ }
23
+ }
24
+ }
25
+ /**
26
+ * Parse unknown long options (starting with `--`) into a key/value object.
27
+ * - Supports `--key=value` and `--key value`
28
+ */
29
+ function parseUnknownLongOptions(command) {
30
+ const argv = command.args;
31
+ const result = {};
32
+ for (let i = 0; i < argv.length; i++) {
33
+ const token = argv[i];
34
+ // Only process long options that start with `--`
35
+ if (!token || !token.startsWith('--')) {
36
+ continue;
37
+ }
38
+ // Split inline assignment: --key=value
39
+ const [flag, inlineVal] = token.split('=', 2);
40
+ // Unknown option: collect it
41
+ const key = flag.slice(2); // remove leading '--'
42
+ let value = true;
43
+ if (inlineVal !== undefined) {
44
+ value = inlineVal;
45
+ }
46
+ else {
47
+ // If next token exists and doesn't look like another flag, use it as the value
48
+ const next = argv[i + 1];
49
+ if (next && !next.startsWith('-')) {
50
+ value = next;
51
+ i++; // consume the value
52
+ }
53
+ }
54
+ // Dot-notation => nested object
55
+ if (key.includes('.')) {
56
+ setByDotPath(result, key, value);
57
+ }
58
+ else {
59
+ result[key] = value;
60
+ }
61
+ }
62
+ return result;
63
+ }
64
+ //# sourceMappingURL=argumentParser.js.map
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,46 @@ 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
472
  const pathParameters = [contextIdRef];
458
- let apiPath = `/api/1/sites/{siteUid}/${containerType}/${containerDefinition.title}`;
473
+ let apiPathOptions = [];
474
+ const containerRouteTagName = tagNameFromContainerTitle(containerDefinition.title);
475
+ // analytic projects do not have any site
476
+ if (!isAnalyticsProject) {
477
+ apiPathOptions.push({
478
+ path: `/api/1/sites/{siteUid}/${containerType}/${containerDefinition.title}`,
479
+ params: [contextIdRef],
480
+ tags: [containerRouteTagName]
481
+ });
482
+ }
459
483
  if (isAddonProject) {
460
- apiPath = `/api/1/sites/{siteUid}/addons/{addonUid}/${containerType}/${containerDefinition.title}`;
461
- pathParameters.push(addonIdRef);
484
+ apiPathOptions = [
485
+ {
486
+ path: `/api/1/sites/{siteUid}/addons/{addonUid}/${containerType}/${containerDefinition.title}`,
487
+ params: [addonIdRef],
488
+ tags: [containerRouteTagName]
489
+ }
490
+ ];
491
+ }
492
+ const appCenterPathOptions = {
493
+ path: `/api/1/app-center/apps/{appUid}/${containerType}/${containerDefinition.title}`,
494
+ params: [appIdRef],
495
+ tags: ['app-center']
496
+ };
497
+ if (isAnalyticsProject) {
498
+ appCenterPathOptions.tags.push(containerRouteTagName);
462
499
  }
500
+ apiPathOptions.push(appCenterPathOptions);
463
501
  const apiPathMethods = {
464
502
  get: undefined,
465
503
  put: undefined
466
504
  };
467
505
  const getMethod = {
468
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
506
+ tags: [],
469
507
  summary: `Retrieve the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
470
- parameters: [...pathParameters],
508
+ parameters: [],
471
509
  responses: {
472
510
  '200': {
473
511
  ...successfulOperationResponse,
@@ -498,9 +536,9 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
498
536
  apiPathMethods.get = getMethod;
499
537
  if ([TransferDirection.down, TransferDirection.backendOnly].includes(containerDefinition.transferDirection)) {
500
538
  apiPathMethods.put = {
501
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
539
+ tags: [],
502
540
  summary: `Modify the data of the "${tagNameFromContainerTitle(containerDefinition.title)}" container`,
503
- parameters: [...pathParameters],
541
+ parameters: [],
504
542
  requestBody: {
505
543
  content: {
506
544
  'application/json': {
@@ -521,11 +559,18 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
521
559
  }
522
560
  };
523
561
  }
524
- apiDescription.paths[apiPath] = apiPathMethods;
562
+ apiPathOptions.forEach(path => {
563
+ const methods = structuredClone(apiPathMethods);
564
+ Object.values(methods).forEach(value => {
565
+ value?.parameters?.unshift(...path.params);
566
+ value?.tags?.unshift(...path.tags);
567
+ });
568
+ apiDescription.paths[path.path] = methods;
569
+ });
525
570
  }
526
571
  if (containerDefinition.containerType === ContainerType.hx) {
527
572
  apiPathMethods.get = {
528
- tags: [tagNameFromContainerTitle(containerDefinition.title)],
573
+ tags: [],
529
574
  summary: `Get the historical data of "${containerDefinition.title}"`,
530
575
  parameters: [
531
576
  ...pathParameters,
@@ -555,16 +600,23 @@ function generatePathInformationFromContainer(apiDescription, containerDefinitio
555
600
  ...defaultResponseCodes
556
601
  }
557
602
  };
558
- apiDescription.paths[apiPath] = apiPathMethods;
559
- const youngestPath = `${apiPath}/youngest`;
603
+ // only site and addons support timeseries
604
+ const pathOptions = apiPathOptions[0];
605
+ const methods = structuredClone(apiPathMethods);
606
+ Object.values(methods).forEach(value => {
607
+ value?.parameters?.unshift(...pathOptions.params);
608
+ value?.tags?.unshift(...pathOptions.tags);
609
+ });
610
+ apiDescription.paths[pathOptions.path] = methods;
611
+ const youngestPath = `${pathOptions.path}/youngest`;
560
612
  const youngestMethod = {
561
- get: getMethod
613
+ get: methods.get
562
614
  };
563
615
  youngestMethod.get.summary = `Get the youngest record of "${containerDefinition.title}" container`;
564
616
  apiDescription.paths[youngestPath] = youngestMethod;
565
- const oldestPath = `${apiPath}/oldest`;
617
+ const oldestPath = `${pathOptions.path}/oldest`;
566
618
  const oldestMethod = {
567
- get: getMethod
619
+ get: methods.get
568
620
  };
569
621
  oldestMethod.get.summary = `Get the oldest record of "${containerDefinition.title}" container`;
570
622
  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/globals.js CHANGED
@@ -71,7 +71,7 @@ var Globals;
71
71
  }
72
72
  }
73
73
  // load the projects main.ini and overwrite existing defines
74
- const mainIni = vscode_uri_1.Utils.joinPath(cwd, defaultFiles_1.DefaultFiles.filePaths.dlo.mainINI);
74
+ const mainIni = vscode_uri_1.Utils.joinPath(cwd, manifest.dlo?.iniFile || defaultFiles_1.DefaultFiles.filePaths.dlo.mainINI);
75
75
  if (await fs.stat(mainIni)) {
76
76
  const iniContent = await readIniFile(fs, mainIni);
77
77
  Object.assign(allDefines, iniContent);
package/out/main.js CHANGED
@@ -12,6 +12,7 @@ const globals_1 = require("./globals");
12
12
  const update_notifier_1 = __importDefault(require("update-notifier"));
13
13
  const api_1 = require("./api");
14
14
  const log_1 = require("./log");
15
+ const argumentParser_1 = require("./argumentParser");
15
16
  const program = new commander_1.Command();
16
17
  const notifier = (0, update_notifier_1.default)({ pkg: package_json_1.default });
17
18
  const logger = new log_1.Log.Logger();
@@ -30,14 +31,21 @@ const devPath = () => {
30
31
  };
31
32
  const cwd = vscode_uri_1.Utils.joinPath(vscode_uri_1.URI.file(process.cwd()), devPath());
32
33
  program.version(package_json_1.default.version).usage('<command>');
34
+ program.description('This tool is used to manage the project dependencies, build, package and publish the project.\n' +
35
+ 'The manifest options can be overwritten with the --option=value syntax.\n' +
36
+ 'Example: "studio build --dlo.mainFile="./dlo/test.dlo" will overwrite the dlo.mainFile option of the manifest.\n' +
37
+ '\nUse "studio --help" for more information.');
33
38
  /**
34
39
  * Executes a task and ensures that the update available notification is sent after the task completes,
35
40
  * regardless of whether it succeeds or fails.
36
41
  *
42
+ * @param command
37
43
  * @param {Promise<void>} task - A promise representing the task to be executed.
38
44
  * @return {void} This function does not return a value.
39
45
  */
40
- function main(task) {
46
+ function main(command, task) {
47
+ const manifestOptions = (0, argumentParser_1.parseUnknownLongOptions)(command);
48
+ api_1.Manifest.setOverwrites(manifestOptions);
41
49
  // todo add task catch handler like: https://github.com/microsoft/vscode-vsce/blob/5518f2f242983d23f1983f6dd0350474f2011543/src/main.ts#L51
42
50
  task.finally(() => {
43
51
  notifier.notify();
@@ -94,9 +102,10 @@ program
94
102
  .allowExcessArguments(true)
95
103
  .addOption(optionStudioEnv)
96
104
  .addOption(optionStudioToken)
97
- .action((dependencies, opts) => {
105
+ .allowUnknownOption(true)
106
+ .action((dependencies, opts, command) => {
98
107
  const { env, token } = getDefaultOptions(opts);
99
- main((0, api_1.install)(cwd, cliFS_1.cliFS, env, token, dependencies));
108
+ main(command, (0, api_1.install)(cwd, cliFS_1.cliFS, env, token, dependencies));
100
109
  });
101
110
  program
102
111
  .command('build')
@@ -104,9 +113,10 @@ program
104
113
  .allowExcessArguments(true)
105
114
  .addOption(optionStudioEnv)
106
115
  .addOption(optionStudioToken)
107
- .action(opts => {
116
+ .allowUnknownOption(true)
117
+ .action((opts, command) => {
108
118
  const { env, token } = getDefaultOptions(opts);
109
- main((0, api_1.build)(cwd, cliFS_1.cliFS, logger, env, token, opts.part));
119
+ main(command, (0, api_1.build)(cwd, cliFS_1.cliFS, logger, env, token, opts.part));
110
120
  });
111
121
  program
112
122
  .command('publish')
@@ -114,10 +124,11 @@ program
114
124
  .addOption(optionAllowedBackends)
115
125
  .addOption(optionStudioEnv)
116
126
  .addOption(optionStudioToken)
127
+ .allowUnknownOption(true)
117
128
  .option('-i, --packagePath <path>', 'Publish the provided library or app package. Use "*" for autodetection.')
118
- .action(async (opts) => {
129
+ .action(async (opts, command) => {
119
130
  const { env, token } = getDefaultOptions(opts);
120
- main((0, api_1.publish)(cwd, cliFS_1.cliFS, logger, env, token, opts.allowedBackends, opts.phase, opts.packagePath));
131
+ main(command, (0, api_1.publish)(cwd, cliFS_1.cliFS, logger, env, token, opts.allowedBackends, opts.phase, opts.packagePath));
121
132
  });
122
133
  program
123
134
  .command('package')
@@ -126,9 +137,10 @@ program
126
137
  .addOption(optionReleasePhase)
127
138
  .addOption(optionStudioEnv)
128
139
  .addOption(optionStudioToken)
129
- .action(async (opts) => {
140
+ .allowUnknownOption(true)
141
+ .action(async (opts, command) => {
130
142
  const { env, token } = getDefaultOptions(opts);
131
- main((0, api_1.pack)(cwd, cliFS_1.cliFS, logger, env, token, opts.phase));
143
+ main(command, (0, api_1.pack)(cwd, cliFS_1.cliFS, logger, env, token, opts.phase));
132
144
  });
133
145
  module.exports = function (argv) {
134
146
  program.parse(argv);
package/out/manifest.js CHANGED
@@ -12,20 +12,26 @@ const registryAPI_1 = require("./registryAPI");
12
12
  const preload_1 = __importDefault(require("semver/preload"));
13
13
  const apm_1 = require("./package/apm");
14
14
  var isNumeric = helper_1.Helper.isNumeric;
15
+ const deepmerge_ts_1 = require("deepmerge-ts");
15
16
  const PRODUCT_ID_SUPPORTED = '52v007';
16
17
  const SEMVER_SUPPORTED = '53v001';
17
18
  const ADDON_SUPPORTED = '54v001';
19
+ const ANALYTICS_SUPPORTED = '56v001';
18
20
  // Cache to reduce the disk read requests
19
21
  const manifestCache = {
20
22
  _manifest: null,
21
23
  _stat: null,
24
+ _overwrites: null,
22
25
  get manifest() {
23
26
  // clone the cached object to prevent any reference changes
24
- return structuredClone(this._manifest);
27
+ return (0, deepmerge_ts_1.deepmerge)(structuredClone(this._manifest), manifestCache._overwrites || {});
25
28
  },
26
29
  set manifest(manifest) {
27
30
  this._manifest = manifest;
28
31
  },
32
+ set overwrites(manifest) {
33
+ this._overwrites = manifest;
34
+ },
29
35
  get stat() {
30
36
  return this._stat;
31
37
  },
@@ -61,6 +67,7 @@ var Manifest;
61
67
  ProjectTypes["app"] = "app";
62
68
  ProjectTypes["library"] = "library";
63
69
  ProjectTypes["addon"] = "addon";
70
+ ProjectTypes["analytics"] = "analytics";
64
71
  })(ProjectTypes = Manifest.ProjectTypes || (Manifest.ProjectTypes = {}));
65
72
  let BloAccessLevel;
66
73
  (function (BloAccessLevel) {
@@ -72,6 +79,14 @@ var Manifest;
72
79
  PovLocation["embedded"] = "$embedded";
73
80
  PovLocation["pure"] = "$pure";
74
81
  })(PovLocation = Manifest.PovLocation || (Manifest.PovLocation = {}));
82
+ function setOverwrites(manifest) {
83
+ manifestCache.overwrites = manifest;
84
+ }
85
+ Manifest.setOverwrites = setOverwrites;
86
+ function clearOverwrites() {
87
+ manifestCache.overwrites = null;
88
+ }
89
+ Manifest.clearOverwrites = clearOverwrites;
75
90
  /**
76
91
  * Read the studio.json manifest and parse it
77
92
  * @param cwd
@@ -143,6 +158,14 @@ var Manifest;
143
158
  return manifest.type === ProjectTypes.app;
144
159
  }
145
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;
146
169
  /**
147
170
  * Validates if the properties within
148
171
  * @param cwd
@@ -314,7 +337,7 @@ var Manifest;
314
337
  throw new Error('Setting "engines.backend" is invalid');
315
338
  }
316
339
  validateHwFwOrProductId(manifest);
317
- validateAddonSupported(manifest);
340
+ validateAddonOrAnalyticsSupported(manifest);
318
341
  }
319
342
  Manifest.validateEngineSettings = validateEngineSettings;
320
343
  /**
@@ -329,9 +352,10 @@ var Manifest;
329
352
  manifest = await Manifest.read(cwd, fs);
330
353
  }
331
354
  const isAddon = Manifest.isAddon(manifest);
355
+ const isAnalytics = Manifest.isAnalytics(manifest);
332
356
  switch (apmPart) {
333
357
  case apm_1.APM.Part.dlo:
334
- return !!manifest?.dlo?.mainFile && !isAddon;
358
+ return !!manifest?.dlo?.mainFile && !isAddon && !isAnalytics;
335
359
  case apm_1.APM.Part.blo:
336
360
  return !!(manifest.blo?.buildCommand && manifest.blo?.workingPath);
337
361
  case apm_1.APM.Part.pov:
@@ -390,19 +414,20 @@ function validateHwFwOrProductId(manifest) {
390
414
  throw new Error(`"engines.productId" requires "engines.backend" to be "${PRODUCT_ID_SUPPORTED}"`);
391
415
  }
392
416
  }
393
- function validateAddonSupported(manifest) {
394
- if (!Manifest.isAddon(manifest)) {
417
+ function validateAddonOrAnalyticsSupported(manifest) {
418
+ if (!Manifest.isAddon(manifest) && !Manifest.isAnalytics(manifest)) {
395
419
  return;
396
420
  }
397
421
  const { backend } = manifest.engines;
398
- if (!backend || backend < ADDON_SUPPORTED) {
399
- 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}"`);
400
425
  }
401
426
  if (manifest.dlo) {
402
- throw new Error('For project type "addon", "dlo" is not supported!"');
427
+ throw new Error(`For project type "${manifest.type}", "dlo" is not supported!"`);
403
428
  }
404
429
  if (manifest.dpid) {
405
- throw new Error('For project type "addon", "dpid" is not supported!"');
430
+ throw new Error(`For project type "${manifest.type}", "dpid" is not supported!"`);
406
431
  }
407
432
  }
408
433
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microtronics/studio-cli",
3
- "version": "0.32.3",
3
+ "version": "0.34.0",
4
4
  "description": "Microtronics Studio CLI Tool",
5
5
  "main": "./out/api.js",
6
6
  "typings": "./dist/studio-cli.d.ts",
@@ -25,12 +25,14 @@
25
25
  "author": "Microtronics",
26
26
  "license": "ISC",
27
27
  "dependencies": {
28
+ "@gera2ld/tarjs": "^0.3.1",
28
29
  "ajv": "^8.17.1",
29
30
  "ajv-formats": "^3.0.1",
30
31
  "ajv-keywords": "^5.1.0",
31
32
  "chalk": "^5.4.1",
32
33
  "commander": "^12.1.0",
33
34
  "cross-fetch": "^4.0.0",
35
+ "deepmerge-ts": "^7.1.5",
34
36
  "dotenv": "^16.4.7",
35
37
  "file-type-checker": "^1.1.2",
36
38
  "glob": "^11.0.1",
@@ -41,7 +43,6 @@
41
43
  "openapi-fetch": "^0.13.0",
42
44
  "pako": "^2.1.0",
43
45
  "semver": "^7.7.2",
44
- "@gera2ld/tarjs": "^0.3.1",
45
46
  "tinytar-fix": "^0.1.1",
46
47
  "update-notifier": "^7.3.1",
47
48
  "vscode-uri": "^3.0.8",