@remkoj/optimizely-cms-cli 6.0.0-pre3 → 6.0.0-pre5

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { globSync, glob } from 'glob';
1
+ import { globSync, glob, globIterate } from 'glob';
2
2
  import { config } from 'dotenv';
3
3
  import { expand } from 'dotenv-expand';
4
- import { getCmsIntegrationApiConfigFromEnvironment, createClient, OptiCmsVersion, IntegrationApi, ContentRoots } from '@remkoj/optimizely-cms-api';
4
+ import { readPartialEnvConfig, getCmsIntegrationApiConfigFromEnvironment, createClient, OptiCmsVersion, IntegrationApi, ContentRoots } from '@remkoj/optimizely-cms-api';
5
5
  import yargs from 'yargs';
6
6
  import chalk from 'chalk';
7
7
  import path from 'node:path';
@@ -9,6 +9,10 @@ import fs from 'node:fs';
9
9
  import figures from 'figures';
10
10
  import Table from 'cli-table3';
11
11
  import { input, select, confirm } from '@inquirer/prompts';
12
+ import createDeepMerge from '@fastify/deepmerge';
13
+ import { Ajv } from 'ajv';
14
+ import addFormats from 'ajv-formats';
15
+ import deepEqual from 'fast-deep-equal';
12
16
 
13
17
  /**
14
18
  * Prepare the application context, by parsing the .env files in the main
@@ -17,19 +21,25 @@ import { input, select, confirm } from '@inquirer/prompts';
17
21
  * @returns A string array with the files processed
18
22
  */
19
23
  function prepare() {
20
- const envFiles = globSync(".env*").sort((a, b) => b.length - a.length).filter(n => n == ".env" || n == ".env.local" || (process.env.NODE_ENV && n == `.env.${process.env.NODE_ENV}.local`));
21
- expand(config({ path: envFiles }));
24
+ const globResult = globSync(".env*");
25
+ const envFiles = globResult.sort((a, b) => b.length - a.length).filter(n => n == ".env" || n == ".env.local" || (process.env.NODE_ENV && n == `.env.${process.env.NODE_ENV}.local`));
26
+ expand(config({ path: envFiles, debug: false, quiet: true }));
22
27
  return envFiles;
23
28
  }
24
29
 
25
30
  function createOptiCmsApp(scriptName, version, epilogue, envFiles) {
31
+ if (envFiles) {
32
+ process.stdout.write(chalk.bold(`✅ Loaded environment files:`) + "\n");
33
+ envFiles.forEach(envFile => {
34
+ process.stdout.write(` 👉 ${envFile}\n`);
35
+ });
36
+ process.stdout.write("\n");
37
+ }
26
38
  let config;
27
39
  try {
28
- config = getCmsIntegrationApiConfigFromEnvironment();
40
+ config = readPartialEnvConfig();
29
41
  }
30
42
  catch (e) {
31
- if (envFiles)
32
- console.error(chalk.gray(`Included environment files: ${envFiles.join(', ')}`));
33
43
  console.error(chalk.redBright(`${chalk.bold("[ERROR]:")} Error processing environment variables: ${e.message}`));
34
44
  process.exit(1);
35
45
  }
@@ -51,12 +61,10 @@ function createOptiCmsApp(scriptName, version, epilogue, envFiles) {
51
61
  .epilogue(`Copyright Remko Jantzen - 2023-${(new Date(Date.now())).getFullYear()}`)
52
62
  .help()
53
63
  .fail((msg, error, args) => {
54
- if (envFiles)
55
- console.error(chalk.gray(`Included environment files: ${envFiles.join(', ')}\n`));
56
64
  if (msg)
57
- console.error(msg + "\n");
65
+ console.error(chalk.redBright(msg) + "\n");
58
66
  if (error)
59
- console.error(`[${chalk.bold(error.name ?? 'Error')}]: ${error.message ?? ''}\n`);
67
+ console.error(chalk.redBright(`[${chalk.bold(error.name ?? 'Error')}]: ${error.message ?? ''}`) + '\n');
60
68
  args.showHelp("error");
61
69
  });
62
70
  }
@@ -202,67 +210,98 @@ function getEnumOptions(enumObject) {
202
210
  return isNaN(Number(item));
203
211
  });
204
212
  }
205
- async function getContentTypes(client, args, pageSize = 100, allowSystem = false) {
213
+ async function getContentTypes(client, args, pageSize = 5, allowSystem = false, customFilter) {
206
214
  const { _config: cfg, excludeBaseTypes, excludeTypes, baseTypes, types, all } = parseArgs(args);
207
- process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pulling Content Types from Optimizely CMS\n`));
208
- if (cfg.debug)
209
- process.stdout.write(chalk.gray(`${figures.arrowRight} Fetching page 1 of ? (${pageSize} items per page)\n`));
210
- let resultsPage = await client.contentTypesList({ query: { pageIndex: 0, pageSize } });
211
- const results = resultsPage.items ?? [];
212
- let pagesRemaining = Math.ceil(resultsPage.totalItemCount / resultsPage.pageSize) - (resultsPage.pageIndex + 1);
213
- while (pagesRemaining > 0 && results.length < resultsPage.totalItemCount) {
214
- if (cfg.debug)
215
- process.stdout.write(chalk.gray(`${figures.arrowRight} Fetching page ${resultsPage.pageIndex + 2} of ${Math.ceil(resultsPage.totalItemCount / resultsPage.pageSize)} (${resultsPage.pageSize} items per page)\n`));
216
- resultsPage = await client.contentTypesList({ query: { pageIndex: resultsPage.pageIndex + 1, pageSize: resultsPage.pageSize } });
217
- results.push(...resultsPage.items);
218
- pagesRemaining = Math.ceil(resultsPage.totalItemCount / resultsPage.pageSize) - (resultsPage.pageIndex + 1);
219
- }
220
- if (cfg.debug) {
221
- process.stdout.write(chalk.gray(`${figures.arrowRight} Fetched ${results.length} Content-Types from Optimizely CMS\n`));
222
- process.stdout.write(chalk.gray(`${figures.arrowRight} Filtering Content-Types based upon arguments\n`));
223
- }
224
215
  const validBaseTypes = getEnumOptions(IntegrationApi.ContentBaseType).map(x => x.toLowerCase());
225
- const allContentTypes = all ?
226
- // If we're returning all content types, including non-supported base types, make sure the base type is always set
227
- results.map(contentType => {
228
- return {
229
- ...contentType,
230
- baseType: contentType.baseType ?? 'default'
231
- };
232
- }) :
233
- // Otherwise filter out any non supported content base type
234
- results.filter(contentType => {
235
- const baseType = (contentType.baseType ?? 'default').toLowerCase();
236
- const isValid = validBaseTypes.includes(baseType);
237
- if (!isValid && cfg.debug)
238
- process.stdout.write(chalk.gray(`${figures.arrowRight} Removing ${contentType.key} as it has an unsupported base type: ${baseType}\n`));
239
- return isValid;
240
- });
241
- const contentTypes = allContentTypes.filter(data => {
242
- // Remove items based upon filters
243
- const keepType = (!excludeBaseTypes.includes(data.baseType)) &&
244
- (!excludeTypes.includes(data.key)) &&
245
- (baseTypes.length == 0 || baseTypes.includes(data.baseType)) &&
246
- (types.length == 0 || types.includes(data.key));
247
- if (!keepType) {
216
+ const allContentTypes = [];
217
+ const filteredContentTypes = [];
218
+ for await (const contentType of getAllContentTypes(client, cfg.debug, pageSize)) {
219
+ const normalizedContentType = { ...contentType, baseType: contentType.baseType ?? 'default' };
220
+ // Build the "All" data set
221
+ if (all)
222
+ allContentTypes.push(normalizedContentType);
223
+ else if (validBaseTypes.includes(normalizedContentType.baseType.toLowerCase()))
224
+ allContentTypes.push(normalizedContentType);
225
+ else if (cfg.debug) {
226
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping ${normalizedContentType.key} as it has an unsupported base type: ${normalizedContentType.baseType}\n`));
227
+ continue;
228
+ }
229
+ else {
230
+ continue;
231
+ }
232
+ // Skip based upon base type filters
233
+ if (!shouldInclude(normalizedContentType.baseType, baseTypes, excludeBaseTypes)) {
248
234
  if (cfg.debug)
249
- process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Content-Type ${data.key} due to applied filters\n`));
250
- return false;
235
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping ${normalizedContentType.key} as it has a restricted base type: ${normalizedContentType.baseType}\n`));
236
+ continue;
251
237
  }
252
- // Skip system types if desired
253
- if (data.source == 'system' && !allowSystem) {
238
+ // Skip based upon type filters
239
+ if (!shouldInclude(normalizedContentType.key, types, excludeTypes)) {
254
240
  if (cfg.debug)
255
- process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Content-Type ${data.key} due to it being a system type\n`));
256
- return false;
241
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping ${normalizedContentType.key} as it has a restricted type: ${normalizedContentType.key}\n`));
242
+ continue;
257
243
  }
258
- return true;
259
- });
244
+ // Skip based upon system filter
245
+ if (normalizedContentType.source == 'system' && !allowSystem) {
246
+ if (cfg.debug)
247
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Content-Type ${normalizedContentType.key} due to it being a system type\n`));
248
+ continue;
249
+ }
250
+ // Skip based upon custom filter
251
+ if (customFilter && !await customFilter(normalizedContentType)) {
252
+ if (cfg.debug)
253
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Content-Type ${normalizedContentType.key} due to the custom filter\n`));
254
+ continue;
255
+ }
256
+ // Add to list
257
+ filteredContentTypes.push(normalizedContentType);
258
+ }
260
259
  if (cfg.debug)
261
- process.stdout.write(chalk.gray(`${figures.arrowRight} Applied content type filters, reduced from ${results.length} to ${contentTypes.length} items\n`));
262
- return {
263
- all: allContentTypes,
264
- contentTypes: contentTypes
265
- };
260
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Applied content type filters, reduced from ${allContentTypes.length} to ${filteredContentTypes.length} items\n`));
261
+ return { all: allContentTypes, contentTypes: filteredContentTypes };
262
+ }
263
+ function shouldInclude(value, allow, disallow) {
264
+ // Item is allowed when either allow is not set, an empty array or has the value
265
+ const isAllowed = !Array.isArray(allow) || allow.length === 0 || allow.includes(value);
266
+ // Item is disallowed when and the array is set and includes the value
267
+ const isDisallowed = Array.isArray(disallow) && disallow.includes(value);
268
+ return isAllowed && !isDisallowed;
269
+ }
270
+ /**
271
+ * Retrieve all content types as an Async Generator, allowing processing of entries whilest they are being loaded from the CMS instance.
272
+ *
273
+ * @param client
274
+ * @param args
275
+ * @param pageSize
276
+ */
277
+ async function* getAllContentTypes(client, debug = false, pageSize = 25) {
278
+ process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pulling Content Types from Optimizely CMS\n`));
279
+ let requestPageSize = pageSize;
280
+ let requestPageIndex = 0;
281
+ let totalItemCount = 0;
282
+ let totalPages = 0;
283
+ do {
284
+ const resultsPage = await client.contentTypesList({ query: { pageIndex: requestPageIndex, pageSize: requestPageSize } }).catch((_) => {
285
+ return {
286
+ items: [],
287
+ totalItemCount: 0,
288
+ pageIndex: requestPageIndex,
289
+ pageSize: requestPageSize
290
+ };
291
+ });
292
+ // Calculate fields for next page
293
+ totalItemCount = resultsPage.totalItemCount ?? 0;
294
+ requestPageSize = resultsPage.pageSize;
295
+ requestPageIndex = resultsPage.pageIndex + 1;
296
+ totalPages = Math.ceil(totalItemCount / requestPageSize);
297
+ // Debug output
298
+ if (debug)
299
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Fetched contentTypes page ${requestPageIndex} of ${totalPages} (${requestPageSize} items per page)\n`));
300
+ // Yield items
301
+ for (const contentType of (resultsPage.items ?? [])) {
302
+ yield contentType;
303
+ }
304
+ } while (requestPageIndex < totalPages);
266
305
  }
267
306
 
268
307
  const stylesBuilder = yargs => {
@@ -274,73 +313,90 @@ const stylesBuilder = yargs => {
274
313
  newArgs.option("templateTypes", { alias: 'tt', description: "Select only these template types", choices: ['node', 'base', 'component'], type: 'array', demandOption: false, default: [] });
275
314
  return newArgs;
276
315
  };
277
- async function getStyles(client, args, pageSize = 100) {
316
+ async function getStyles(client, args, pageSize = 25) {
278
317
  if (client.runtimeCmsVersion == OptiCmsVersion.CMS12)
279
318
  return { all: [], styles: [] };
280
319
  const { _config: cfg, excludeBaseTypes, excludeTypes, excludeNodeTypes, excludeTemplates, baseTypes, types, nodes, templates, templateTypes } = parseArgs(args);
281
320
  process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pulling Style-Definitions from Optimizely CMS\n`));
282
- if (cfg.debug)
283
- process.stdout.write(chalk.gray(`${figures.arrowRight} Fetching page 1 of ? (${pageSize} items per page)\n`));
284
- let resultsPage = await client.displayTemplatesList({ query: { pageIndex: 0, pageSize } });
285
- const results = resultsPage.items ?? [];
286
- let pagesRemaining = Math.ceil(resultsPage.totalItemCount / resultsPage.pageSize) - (resultsPage.pageIndex + 1);
287
- while (pagesRemaining > 0 && results.length < resultsPage.totalItemCount) {
288
- if (cfg.debug)
289
- process.stdout.write(chalk.gray(`${figures.arrowRight} Fetching page ${resultsPage.pageIndex + 2} of ${Math.ceil(resultsPage.totalItemCount / resultsPage.pageSize)} (${resultsPage.pageSize} items per page)\n`));
290
- resultsPage = await client.displayTemplatesList({ query: { pageIndex: resultsPage.pageIndex + 1, pageSize: resultsPage.pageSize } });
291
- results.push(...resultsPage.items);
292
- pagesRemaining = Math.ceil(resultsPage.totalItemCount / resultsPage.pageSize) - (resultsPage.pageIndex + 1);
293
- }
294
- if (cfg.debug) {
295
- process.stdout.write(chalk.gray(`${figures.arrowRight} Fetched ${results.length} Style-Definitions from Optimizely CMS\n`));
296
- process.stdout.write(chalk.gray(`${figures.arrowRight} Filtering Style-Definitions based upon arguments\n`));
297
- }
298
- const styles = results.filter(data => {
299
- if (isExcluded(data.key, excludeTemplates, templates)) {
321
+ const allDisplayTemplates = [];
322
+ const filteredDisplayTemplates = [];
323
+ for await (const displayTemplate of getAllStyles(client, cfg.debug, pageSize)) {
324
+ allDisplayTemplates.push(displayTemplate);
325
+ const templateType = displayTemplate.baseType ? 'base' : displayTemplate.nodeType ? 'node' : displayTemplate.contentType ? 'component' : 'unknown';
326
+ if (isExcluded(displayTemplate.key, excludeTemplates, templates)) {
300
327
  if (cfg.debug)
301
- process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style defintion key filtering active\n`));
302
- return false;
328
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${displayTemplate.key} - Style defintion key filtering active\n`));
329
+ continue;
303
330
  }
304
- const templateType = data.baseType ? 'base' : data.nodeType ? 'node' : data.contentType ? 'component' : 'unknown';
305
331
  if (isExcluded(templateType, [], templateTypes)) {
306
332
  if (cfg.debug)
307
- process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style type filtering is active\n`));
308
- return false;
333
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${displayTemplate.key} - Style type filtering is active\n`));
334
+ continue;
309
335
  }
310
- if (data.baseType && isExcluded(data.baseType, excludeBaseTypes, baseTypes)) {
336
+ if (displayTemplate.baseType && isExcluded(displayTemplate.baseType, excludeBaseTypes, baseTypes)) {
311
337
  if (cfg.debug)
312
- process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style is defined at base type level and base type filtering is active\n`));
313
- return false;
338
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${displayTemplate.key} - Style is defined at base type level and base type filtering is active\n`));
339
+ continue;
314
340
  }
315
- if (data.contentType && isExcluded(data.contentType, excludeTypes, types)) {
341
+ if (displayTemplate.contentType && isExcluded(displayTemplate.contentType, excludeTypes, types)) {
316
342
  if (cfg.debug)
317
- process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style is defined at component type level and component type filtering is active\n`));
318
- return false;
343
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${displayTemplate.key} - Style is defined at component type level and component type filtering is active\n`));
344
+ continue;
319
345
  }
320
346
  if (templateType != 'component' && types.length > 0) {
321
347
  if (cfg.debug)
322
- process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style is targeting the ${templateType} level and component type selection is active\n`));
323
- return false;
348
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${displayTemplate.key} - Style is targeting the ${templateType} level and component type selection is active\n`));
349
+ continue;
324
350
  }
325
- if (data.nodeType && isExcluded(data.nodeType, excludeNodeTypes, nodes)) {
351
+ if (displayTemplate.nodeType && isExcluded(displayTemplate.nodeType, excludeNodeTypes, nodes)) {
326
352
  if (cfg.debug)
327
- process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style is defined at node type level and node type filtering is active\n`));
328
- return false;
353
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${displayTemplate.key} - Style is defined at node type level and node type filtering is active\n`));
354
+ continue;
329
355
  }
330
356
  if (templateType != 'node' && nodes.length > 0) {
331
357
  if (cfg.debug)
332
- process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${data.key} - Style is targeting the ${templateType} level and node type selection is active\n`));
333
- return false;
358
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping Style-Defintion ${displayTemplate.key} - Style is targeting the ${templateType} level and node type selection is active\n`));
359
+ continue;
334
360
  }
335
- return true;
336
- });
361
+ filteredDisplayTemplates.push(displayTemplate);
362
+ }
337
363
  if (cfg.debug)
338
- process.stdout.write(chalk.gray(`${figures.arrowRight} Applied content type filters, reduced from ${results.length} to ${styles.length} items\n`));
364
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Applied style filters, reduced from ${allDisplayTemplates.length} to ${filteredDisplayTemplates.length} items\n`));
339
365
  return {
340
- all: results,
341
- styles
366
+ all: allDisplayTemplates,
367
+ styles: filteredDisplayTemplates
342
368
  };
343
369
  }
370
+ async function* getAllStyles(client, debug = false, pageSize = 5) {
371
+ if (client.runtimeCmsVersion == OptiCmsVersion.CMS12)
372
+ return;
373
+ let requestPageSize = pageSize;
374
+ let requestPageIndex = 0;
375
+ let totalItemCount = 0;
376
+ let totalPages = 0;
377
+ do {
378
+ const resultsPage = await client.displayTemplatesList({ query: { pageIndex: requestPageIndex, pageSize: requestPageSize } }).catch((_) => {
379
+ return {
380
+ items: [],
381
+ totalItemCount: 0,
382
+ pageIndex: requestPageIndex,
383
+ pageSize: requestPageSize
384
+ };
385
+ });
386
+ // Calculate fields for next page
387
+ totalItemCount = resultsPage.totalItemCount ?? 0;
388
+ requestPageSize = resultsPage.pageSize;
389
+ requestPageIndex = resultsPage.pageIndex + 1;
390
+ totalPages = Math.ceil(totalItemCount / requestPageSize);
391
+ // Debug output
392
+ if (debug)
393
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Fetched displayTemplates page ${requestPageIndex} of ${totalPages} (${requestPageSize} items per page)\n`));
394
+ // Yield items
395
+ for (const displayTemplate of (resultsPage.items ?? [])) {
396
+ yield displayTemplate;
397
+ }
398
+ } while (requestPageIndex < totalPages);
399
+ }
344
400
  function isExcluded(value, exclusions, inclusions) {
345
401
  if (value == undefined || value == null)
346
402
  return false;
@@ -504,7 +560,7 @@ const StylesPullCommand = {
504
560
  // Write Style definition
505
561
  const imports = [
506
562
  'import type { LayoutProps } from "@remkoj/optimizely-cms-react"',
507
- 'import type { ReactNode } from "react"'
563
+ 'import type { ReactNode, JSX } from "react"'
508
564
  ];
509
565
  const typeContents = [];
510
566
  const props = [];
@@ -573,11 +629,11 @@ const StylesCreateCommand = {
573
629
  process.stdout.write(chalk.gray(`${figures.cross} Styles are not supported on CMS12\n`));
574
630
  return;
575
631
  }
576
- const allowedBaseTypes = ['section', 'element'];
632
+ const allowedBaseTypes = ['section', 'component', 'experience'];
577
633
  // Prepare
578
634
  process.stdout.write(chalk.yellowBright(chalk.bold(`Reading current information from Optimizely CMS\n`)));
579
635
  const [{ contentTypes }, { styles }] = await Promise.all([
580
- getContentTypes(client, { ...args, excludeBaseTypes: [], excludeTypes: [], baseTypes: allowedBaseTypes, types: [], all: false }),
636
+ getContentTypes(client, { ...args, excludeBaseTypes: [], excludeTypes: [], baseTypes: allowedBaseTypes, types: [], all: false }, 25, false, (ct) => ct.compositionBehaviors?.includes('elementEnabled') || ct.compositionBehaviors?.includes('sectionEnabled') || ct.baseType == 'experience' || ct.baseType == 'section'),
581
637
  getStyles(client, { ...args, excludeBaseTypes: [], excludeNodeTypes: [], excludeTemplates: [], excludeTypes: [], baseTypes: allowedBaseTypes, types: [], nodes: [], components: '', templates: [], templateTypes: [], all: false })
582
638
  ]);
583
639
  const styleKeys = styles.map(x => x.key);
@@ -585,27 +641,34 @@ const StylesCreateCommand = {
585
641
  process.stdout.write(chalk.yellowBright(chalk.bold(`\nConfigure your style definition\n`)));
586
642
  const key = await input({ message: "Identifier:", validate: (val) => { return val.match(/^[a-zA-Z][A-Za-z0-9\-_]*$/) != null && !styleKeys.includes(val); } });
587
643
  const displayName = await input({ message: "Display name:" });
588
- const type = (await select({ message: "Style target:", choices: [
589
- { value: "baseType", name: "Base Type", description: "Target all Content-Types that inherit from the specified base type" },
590
- { value: "contentType", name: "Content-Type", description: "Target a specific Content-Type that supports styling" },
591
- { value: "nodeType", name: "Experience Node", description: "Target a specific node type from a Section" }
592
- ] }));
644
+ const type = (await select({
645
+ message: "Style target:", choices: [
646
+ { value: "baseType", name: "Base type", description: "Target all Content-Types that inherit from the specified base type" },
647
+ { value: "contentType", name: "Content type", description: "Target a specific Content-Type that supports styling" },
648
+ { value: "nodeType", name: "Node", description: "Target a specific node type from a Section" }
649
+ ]
650
+ }));
593
651
  let typeId = "";
594
652
  switch (type) {
595
653
  case 'contentType':
596
654
  typeId = await select({ message: "Content-Type:", choices: contentTypes.map(x => { return { value: x.key, description: x.description, name: x.displayName }; }) });
597
655
  break;
598
656
  case 'nodeType':
599
- typeId = await select({ message: "Node type:", choices: [
657
+ typeId = await select({
658
+ message: "Node type:", choices: [
600
659
  { value: "row", description: "Row", name: "Row" },
601
660
  { value: "column", description: "Column", name: "Column" }
602
- ] });
661
+ ]
662
+ });
603
663
  break;
604
664
  case 'baseType':
605
- typeId = await select({ message: "Base type:", choices: [
665
+ typeId = await select({
666
+ message: "Base type:", choices: [
667
+ { value: "experience", description: "Target all experience types", name: "Experience" },
606
668
  { value: "section", description: "Target all section types", name: "Section" },
607
- { value: "element", description: "Target all element types", name: "Element" }
608
- ] });
669
+ { value: "component", description: "Target all component types", name: "Component" }
670
+ ]
671
+ });
609
672
  break;
610
673
  }
611
674
  const isDefault = await confirm({ message: "Should this style be marked as default?" });
@@ -625,8 +688,18 @@ const StylesCreateCommand = {
625
688
  process.exit(0);
626
689
  }
627
690
  }
691
+ // @ToDo: Add properties
628
692
  fs.writeFileSync(path.join(basePath, styleFilePath), JSON.stringify(definition, undefined, 4));
629
693
  process.stdout.write(chalk.yellowBright(chalk.bold(`\nWritten style defintion template to: ${path.normalize(path.join(basePath, styleFilePath))}\n`)));
694
+ if (await confirm({ message: "Do you want to publish this style into Optimizely CMS?" })) {
695
+ const response = await client.displayTemplatesPut({ body: definition, path: { key: definition.key } }).catch(_ => undefined);
696
+ if (!response) {
697
+ process.stderr.write(chalk.redBright(chalk.bold(`\[ERROR] Unable to create style definition ${definition.displayName} in Optimizely CMS\n`)));
698
+ }
699
+ else {
700
+ process.stdout.write(chalk.yellowBright(chalk.bold(`\nCreated style definition ${definition.displayName} in Optimizely CMS\n`)));
701
+ }
702
+ }
630
703
  process.stdout.write(chalk.yellowBright(`\n1. Add your properties to the 'settings' list in the file`));
631
704
  process.stdout.write(chalk.yellowBright(`\n2. Run `));
632
705
  process.stdout.write(chalk.whiteBright(`yarn opti-cms styles:push -t ${definition.key}`));
@@ -1132,20 +1205,19 @@ ${varName}.getMetaData = async (contentLink, locale, client) => {
1132
1205
  export default ${varName}`,
1133
1206
  // Template for all experience component types
1134
1207
  experience: (contentType, varName, displayTemplate) => `import { type OptimizelyNextPage as CmsComponent } from "@remkoj/optimizely-cms-nextjs";
1135
- import { getFragmentData } from "@/gql/fragment-masking";
1136
- import { ExperienceDataFragmentDoc, CompositionDataFragmentDoc, ${contentType.key}DataFragmentDoc, type ${contentType.key}DataFragment } from "@/gql/graphql";
1208
+ import { ${contentType.key}DataFragmentDoc, type ExperienceDataFragment, type ${contentType.key}DataFragment } from "@/gql/graphql";
1137
1209
  import { OptimizelyComposition, isNode, CmsEditable } from "@remkoj/optimizely-cms-react/rsc";${displayTemplate ? `
1138
1210
  import { ${displayTemplate} } from "./displayTemplates";` : ''}
1139
- import { getSdk } from "@/gql"
1211
+ import { getSdk } from "@/gql/client"
1140
1212
 
1141
1213
  /**
1142
1214
  * ${contentType.displayName}
1143
1215
  * ${contentType.description}
1144
1216
  */
1145
1217
  export const ${varName} : CmsComponent<${contentType.key}DataFragment${displayTemplate ? ', ' + displayTemplate : ''}> = ({ data${displayTemplate ? ', layoutProps' : ''}, ctx }) => {
1146
- const composition = getFragmentData(CompositionDataFragmentDoc, getFragmentData(ExperienceDataFragmentDoc, data)?.composition)
1218
+ const composition = (data as ExperienceDataFragment)?.composition
1147
1219
  return <CmsEditable as="div" className="mx-auto px-2 container" cmsFieldName="unstructuredData" ctx={ctx}>
1148
- { composition && isNode(composition) && <OptimizelyComposition node={composition} /> }
1220
+ { composition && isNode(composition) && <OptimizelyComposition node={composition} ctx={ctx} /> }
1149
1221
  </CmsEditable>
1150
1222
  }
1151
1223
  ${varName}.displayName = "${contentType.displayName} (${ucFirst(contentType.baseType)}/${contentType.key})"
@@ -1493,6 +1565,386 @@ const NextJsCreateCommand = {
1493
1565
  }
1494
1566
  };
1495
1567
 
1568
+ const deepmerge$1 = createDeepMerge();
1569
+ async function loadSchema(client, schemaName) {
1570
+ const schemas = Array.isArray(schemaName) ? schemaName : [schemaName];
1571
+ process.stdout.write(`${figures.arrowRight} Downloading current JSON Schema\n`);
1572
+ const spec = await client.getOpenApiSpec();
1573
+ const specSchemas = spec.components?.schemas ?? {};
1574
+ process.stdout.write(` ${figures.tick} Complete\n`);
1575
+ const result = [];
1576
+ process.stdout.write(`\n${figures.arrowRight} Constructing schema for ${schemas.join(', ')}\n`);
1577
+ for await (const schema of schemas)
1578
+ if (specSchemas[schema]) {
1579
+ const definitions = {};
1580
+ const processedSchema = processSchema(specSchemas[schema], definitions, spec);
1581
+ const jsonSchema = {
1582
+ //"$schema": "https://json-schema.org/draft-07/schema",
1583
+ "$id": `https://api.cms.optimizely.com/schema/${schema}`,
1584
+ type: "object",
1585
+ title: schema,
1586
+ ...processedSchema,
1587
+ definitions
1588
+ };
1589
+ result.push({
1590
+ title: schema,
1591
+ schema: postProcessDefintions(jsonSchema)
1592
+ });
1593
+ process.stdout.write(` ${figures.tick} Constructed schema of ${schema}\n`);
1594
+ }
1595
+ return result;
1596
+ }
1597
+ function postProcessDefintions(jsonSchema) {
1598
+ if (!jsonSchema.definitions)
1599
+ return jsonSchema;
1600
+ for (const definitionName in jsonSchema.definitions) {
1601
+ if ((definitionName.endsWith("Property") || definitionName.endsWith("ListItem")) && jsonSchema.definitions[definitionName].properties) {
1602
+ const type = jsonSchema.definitions[definitionName].properties?.type;
1603
+ if (isRefSchema(type)) {
1604
+ const typeSchema = resolveRefSchema(type, jsonSchema);
1605
+ if (typeSchema && typeSchema.type === 'string' && Array.isArray(typeSchema.enum)) {
1606
+ let typeValue = definitionName.substring(0, definitionName.length - 8);
1607
+ typeValue = typeValue[0].toLowerCase() + typeValue.substring(1);
1608
+ typeValue = typeValue === 'list' ? 'array' : typeValue;
1609
+ typeValue = typeValue === 'jsonString' ? 'json' : typeValue;
1610
+ if (typeSchema.enum.includes(typeValue)) {
1611
+ const newTypeDef = deepmerge$1({}, typeSchema);
1612
+ newTypeDef.enum = newTypeDef.enum.filter((x) => x === typeValue);
1613
+ jsonSchema.definitions[definitionName].properties.type = newTypeDef;
1614
+ }
1615
+ }
1616
+ }
1617
+ }
1618
+ }
1619
+ return jsonSchema;
1620
+ }
1621
+ /**
1622
+ * Schema normalization to tranform the schema from a .Net generated OpenAPI Schema
1623
+ * to a AJV compatible JSON Schema
1624
+ *
1625
+ * @see https://ajv.js.org/
1626
+ * @param schema
1627
+ * @param defs
1628
+ * @param spec
1629
+ * @returns
1630
+ */
1631
+ function processSchema(schema, defs, spec, mergeAllOf = true) {
1632
+ if (Array.isArray(schema))
1633
+ return schema.map(s => processSchema(s, defs, spec, mergeAllOf));
1634
+ if (typeof schema !== 'object' || schema === null)
1635
+ return schema;
1636
+ const props = Object.getOwnPropertyNames(schema);
1637
+ if (props.length === 1 && props[0] === '$ref') {
1638
+ const ref = schema['$ref'];
1639
+ if (isLocalRef(ref)) {
1640
+ const refName = getLocalRefName(ref);
1641
+ if (!defs[refName]) {
1642
+ const refItem = resolveLocalRef(ref, spec);
1643
+ defs[refName] = processSchema(refItem, defs, spec, mergeAllOf);
1644
+ }
1645
+ const newRef = `#/definitions/${refName}`;
1646
+ return { "$ref": newRef };
1647
+ }
1648
+ else {
1649
+ return schema;
1650
+ }
1651
+ }
1652
+ else if (mergeAllOf && props.includes('allOf') && Array.isArray(schema['allOf'])) {
1653
+ // process all of
1654
+ const newObject = props.reduce((generated, propName) => {
1655
+ if (propName != 'allOf')
1656
+ generated[propName] = schema[propName];
1657
+ return generated;
1658
+ }, {});
1659
+ const allOfSchemas = schema['allOf'].map((subschema) => {
1660
+ const resolvedSchema = isRefSchema(subschema) ? resolveRefSchema(subschema, spec) : subschema;
1661
+ const resolved = processSchema(resolvedSchema, defs, spec, mergeAllOf);
1662
+ return resolved;
1663
+ });
1664
+ const merged = allOfSchemas.reduce((previous, current) => deepmerge$1(previous, current), newObject);
1665
+ if (newObject['description'])
1666
+ merged['description'] = newObject['description'];
1667
+ if (newObject['title'])
1668
+ merged['title'] = newObject['title'];
1669
+ return merged;
1670
+ }
1671
+ else {
1672
+ const newSchema = {};
1673
+ for (const key of props) {
1674
+ if (key === 'pattern' && typeof schema[key] === 'string') {
1675
+ newSchema[key] = safeCreateUnicodeRegExp(schema[key]);
1676
+ /*} else if (key === 'readOnly' && schema[key] === true) {
1677
+ return undefined*/
1678
+ }
1679
+ else if (['additionalProperties', 'required', 'properties', 'discriminator'].includes(key) && typeof schema[key] !== 'object') ;
1680
+ else if (key === 'discriminator' && !Array.isArray(schema['oneOf'])) ;
1681
+ else if (key === 'discriminator' && !schema['type']) {
1682
+ newSchema.type = 'object';
1683
+ }
1684
+ else {
1685
+ const keyVal = processSchema(schema[key], defs, spec, mergeAllOf);
1686
+ if (keyVal !== undefined)
1687
+ newSchema[key] = keyVal;
1688
+ }
1689
+ }
1690
+ return newSchema;
1691
+ }
1692
+ }
1693
+ function isRefSchema(schema) {
1694
+ if (typeof schema != 'object' || schema == null)
1695
+ return false;
1696
+ return Object.getOwnPropertyNames(schema).length === 1 && typeof schema['$ref'] === 'string';
1697
+ }
1698
+ function resolveRefSchema(schema, spec) {
1699
+ if (!isRefSchema(schema))
1700
+ return undefined;
1701
+ return resolveLocalRef(schema['$ref'], spec);
1702
+ }
1703
+ function isLocalRef(ref) {
1704
+ return typeof ref === 'string' && ref.startsWith('#');
1705
+ }
1706
+ function getLocalRefName(ref) {
1707
+ if (!isLocalRef(ref))
1708
+ return undefined;
1709
+ const refPath = ref.substring(2).split('/');
1710
+ return refPath.at(refPath.length - 1);
1711
+ }
1712
+ function resolveLocalRef(ref, spec) {
1713
+ const refPath = ref.substring(2).split('/');
1714
+ return refPath.reduce((integrator, current) => {
1715
+ return integrator ? integrator[current] : undefined;
1716
+ }, spec);
1717
+ }
1718
+ const replaceEscapedChars = [
1719
+ [new RegExp('\\\\ ', 'g'), ' '],
1720
+ [new RegExp('\\\\!', 'g'), '!'],
1721
+ [new RegExp('\\\\"', 'g'), '"'],
1722
+ [new RegExp('\\\\#', 'g'), '#'],
1723
+ [new RegExp('\\\\%', 'g'), '%'],
1724
+ [new RegExp('\\\\&', 'g'), '&'],
1725
+ [new RegExp("\\\\'", 'g'), "'"],
1726
+ [new RegExp('\\\\,', 'g'), ','],
1727
+ [new RegExp('\\\\-', 'g'), '-'],
1728
+ [new RegExp('\\\\:', 'g'), ':'],
1729
+ [new RegExp('\\\\;', 'g'), ';'],
1730
+ [new RegExp('\\\\<', 'g'), '<'],
1731
+ [new RegExp('\\\\=', 'g'), '='],
1732
+ [new RegExp('\\\\>', 'g'), '>'],
1733
+ [new RegExp('\\\\@', 'g'), '@'],
1734
+ [new RegExp('\\\\_', 'g'), '_'],
1735
+ [new RegExp('\\\\`', 'g'), '`'],
1736
+ [new RegExp('\\\\~', 'g'), '~'],
1737
+ [new RegExp('\\\\[zZ]', 'g'), '$'],
1738
+ ];
1739
+ function safeCreateUnicodeRegExp(pattern) {
1740
+ for (const unEscape of replaceEscapedChars) {
1741
+ pattern = pattern.replace(unEscape[0], unEscape[1]);
1742
+ }
1743
+ return pattern;
1744
+ }
1745
+
1746
+ const SchemaVsCodeCommand = {
1747
+ command: "schema:vscode",
1748
+ describe: "Configure schema validation for opti-type.json & opti-style.json files by VSCode in the current project folder",
1749
+ builder: (yargs) => {
1750
+ const newYargs = yargs;
1751
+ //newYargs.option('schemas', { alias: 's', description: "The schema's to download", array: true, type: 'string', demandOption: false, default: ['DisplayTemplate', 'ContentType'] })
1752
+ //newYargs.option('force', { alias: 'f', description: "Overwrite existing files", boolean: true, type: 'boolean', demandOption: false, default: false })
1753
+ //newYargs.option("definitions", { alias: 'd', description: "Create/overwrite typescript definitions", boolean: true, type: 'boolean', demandOption: false, default: true })
1754
+ return newYargs;
1755
+ },
1756
+ handler: async (args) => {
1757
+ const projectPath = args.path;
1758
+ const client = createCmsClient(args);
1759
+ const schemas = await loadSchema(client, ['DisplayTemplate', 'ContentType']);
1760
+ process.stdout.write(`\n${figures.arrowRight} Writing schema files\n`);
1761
+ for (const schema of schemas) {
1762
+ const schemaFile = path.join(projectPath, '.vscode', `${schema.title.toLowerCase()}.schema.json`);
1763
+ void await writeFileAsync(schemaFile, JSON.stringify(schema.schema, undefined, 2));
1764
+ process.stdout.write(` ${figures.tick} Written the ${schema.title} schema to ${path.relative(projectPath, schemaFile)}\n`);
1765
+ }
1766
+ process.stdout.write(`\n${figures.arrowRight} Updating folder settings\n`);
1767
+ const settingsFile = path.join(projectPath, '.vscode', 'settings.json');
1768
+ const settings = (await readJsonFileAsync(settingsFile).catch(() => undefined)) ?? {};
1769
+ settings['json.validate.enable'] = true;
1770
+ settings['json.schemaDownload.enable'] = true;
1771
+ settings['json.schemas'] = settings['json.schemas'] || [];
1772
+ const hasContentType = settings['json.schemas'].findIndex(v => v.fileMatch.findIndex(fm => fm.endsWith('.opti-type.json')) >= 0) >= 0;
1773
+ const hasStyleDefinition = settings['json.schemas'].findIndex(v => v.fileMatch.findIndex(fm => fm.endsWith('.opti-style.json')) >= 0) >= 0;
1774
+ const hasSchemaType = settings['json.schemas'].findIndex(v => v.fileMatch.findIndex(fm => fm.endsWith('.schema.json')) >= 0) >= 0;
1775
+ if (!hasContentType)
1776
+ settings['json.schemas'].push({
1777
+ "fileMatch": [
1778
+ "**/*.opti-type.json"
1779
+ ],
1780
+ "url": "./.vscode/contenttype.schema.json"
1781
+ });
1782
+ if (!hasStyleDefinition)
1783
+ settings['json.schemas'].push({
1784
+ "fileMatch": [
1785
+ "**/*.opti-style.json"
1786
+ ],
1787
+ "url": "./.vscode/displaytemplate.schema.json"
1788
+ });
1789
+ if (!hasSchemaType)
1790
+ settings['json.schemas'].push({
1791
+ "fileMatch": [
1792
+ "**/*.schema.json"
1793
+ ],
1794
+ "url": "https://json-schema.org/draft-07/schema"
1795
+ });
1796
+ if (settings['json.schemas'].length == 0)
1797
+ delete settings['json.schemas'];
1798
+ await writeFileAsync(settingsFile, JSON.stringify(settings, undefined, 2));
1799
+ }
1800
+ };
1801
+ function readJsonFileAsync(path) {
1802
+ return new Promise((resolve, reject) => fs.readFile(path, (err, data) => {
1803
+ if (err) {
1804
+ if (err?.code === 'ENOENT')
1805
+ resolve(undefined);
1806
+ else
1807
+ reject(err);
1808
+ }
1809
+ else {
1810
+ try {
1811
+ const d = data.toString();
1812
+ resolve(JSON.parse(d));
1813
+ }
1814
+ catch (e) {
1815
+ reject(e);
1816
+ }
1817
+ }
1818
+ }));
1819
+ }
1820
+ function writeFileAsync(path, data) {
1821
+ return new Promise((resolve, reject) => {
1822
+ fs.writeFile(path, data, (err) => {
1823
+ if (err)
1824
+ reject(err);
1825
+ else
1826
+ resolve();
1827
+ });
1828
+ });
1829
+ }
1830
+
1831
+ const deepmerge = createDeepMerge();
1832
+ const SchemaValidateCommand = {
1833
+ command: "schema:validate",
1834
+ describe: "Validate the opti-type.json & opti-style.json files",
1835
+ builder: (yargs) => {
1836
+ const newYargs = yargs;
1837
+ //newYargs.option('schemas', { alias: 's', description: "The schema's to download", array: true, type: 'string', demandOption: false, default: ['DisplayTemplate', 'ContentType'] })
1838
+ //newYargs.option('force', { alias: 'f', description: "Overwrite existing files", boolean: true, type: 'boolean', demandOption: false, default: false })
1839
+ //newYargs.option("definitions", { alias: 'd', description: "Create/overwrite typescript definitions", boolean: true, type: 'boolean', demandOption: false, default: true })
1840
+ return newYargs;
1841
+ },
1842
+ handler: async (args) => {
1843
+ const projectPath = args.path;
1844
+ const client = createCmsClient(args);
1845
+ const schemas = await loadSchema(client, ['DisplayTemplate', 'ContentType']);
1846
+ process.stdout.write(``);
1847
+ const styleSchema = schemas.find(x => x.title == 'DisplayTemplate')?.schema;
1848
+ const typeSchema = schemas.find(x => x.title == 'ContentType')?.schema;
1849
+ const [styleValidator, typeValidator] = await Promise.all([getValidator(styleSchema), getValidator(typeSchema)]);
1850
+ process.stdout.write(`\n${figures.arrowRight} Validating display templates (*.opti-style.json) and content types (*.opti-type.json).\n`);
1851
+ const patterns = ["./**/*.opti-type.json", "./**/*.opti-style.json"];
1852
+ const globMatches = globIterate(patterns, { cwd: projectPath, absolute: true });
1853
+ let allOk = true;
1854
+ for await (const fileMatch of globMatches) {
1855
+ try {
1856
+ const content = JSON.parse(fs.readFileSync(fileMatch).toString());
1857
+ let errors = undefined;
1858
+ if (fileMatch.endsWith('.opti-type.json') && !typeValidator(content)) {
1859
+ errors = typeValidator.errors;
1860
+ }
1861
+ else if (fileMatch.endsWith('.opti-style.json') && !styleValidator(content)) {
1862
+ errors = styleValidator.errors;
1863
+ }
1864
+ if (errors) {
1865
+ allOk = false;
1866
+ process.stdout.write(chalk.redBright(chalk.bold(`\n${figures.cross} ${path.relative(projectPath, fileMatch)}\n`)));
1867
+ const groupedErrors = groupErrors(errors);
1868
+ for (const instancepath in groupedErrors) {
1869
+ for (const key in groupedErrors[instancepath]) {
1870
+ const errorList = groupedErrors[instancepath][key].filter((v, i, a) => a.findIndex(pv => deepEqual(v, pv)) === i);
1871
+ for (const error of errorList) {
1872
+ process.stdout.write(` Node ${instancepath} ${error.message}\n`);
1873
+ switch (error.keyword) {
1874
+ case 'enum':
1875
+ process.stdout.write(` Allowed values: ${error.params.allowedValues.map((x) => x.toString()).join(', ')}\n`);
1876
+ break;
1877
+ case 'oneOf':
1878
+ const matching = Array.isArray(error.params.passingSchemas) ? error.params.passingSchemas : [];
1879
+ if (matching.length === 0)
1880
+ process.stdout.write(` Currently none matching\n`);
1881
+ else
1882
+ process.stdout.write(` Currently matching ${matching.map((x) => x.toString()).join(", ")}\n`);
1883
+ break;
1884
+ }
1885
+ }
1886
+ }
1887
+ }
1888
+ }
1889
+ }
1890
+ catch (e) {
1891
+ allOk = false;
1892
+ process.stdout.write(chalk.redBright(chalk.bold(`${figures.cross} ${path.relative(projectPath, fileMatch)}\n`)));
1893
+ process.stdout.write(` ${e.message}\n`);
1894
+ }
1895
+ }
1896
+ process.stdout.write(`\n`);
1897
+ if (allOk)
1898
+ process.stdout.write(chalk.greenBright(`${figures.tick} All types & styles match the schema\n`));
1899
+ else {
1900
+ process.stdout.write(chalk.redBright(`${figures.cross} Some types & styles contain errors\n`));
1901
+ process.exit(1);
1902
+ }
1903
+ }
1904
+ };
1905
+ function groupErrors(errors) {
1906
+ const errorrsByInstance = groupErrorsByPath(errors);
1907
+ const instancePaths = Object.getOwnPropertyNames(errorrsByInstance);
1908
+ return instancePaths.reduce((previous, current) => {
1909
+ previous[current] = groupErrorsByKeyword(errorrsByInstance[current]);
1910
+ return previous;
1911
+ }, {});
1912
+ }
1913
+ function groupErrorsByPath(errors) {
1914
+ return errors.reduce((previous, current) => {
1915
+ const instancePath = current.instancePath == "" ? "[ROOT]" : current.instancePath;
1916
+ const partial = {};
1917
+ partial[instancePath] = [current];
1918
+ return deepmerge(previous, partial);
1919
+ }, {});
1920
+ }
1921
+ function groupErrorsByKeyword(errors) {
1922
+ const toMerge = errors.reduce((previous, current) => {
1923
+ const keyword = current.keyword;
1924
+ const partial = {};
1925
+ partial[keyword] = [current];
1926
+ return deepmerge(previous, partial);
1927
+ }, {});
1928
+ // Enum errors must be merged
1929
+ if (toMerge.enum)
1930
+ toMerge.enum = [toMerge.enum.reduce((prev, current) => prev ? deepmerge(prev, current) : current, undefined)];
1931
+ return toMerge;
1932
+ }
1933
+ function getValidator(schemaObject) {
1934
+ const ajv = new Ajv({
1935
+ discriminator: true
1936
+ });
1937
+ addFormats(ajv);
1938
+ return ajv.compile(schemaObject);
1939
+ }
1940
+
1941
+ var version = "6.0.0-pre4";
1942
+ var name = "opti-cms";
1943
+ var APP = {
1944
+ version: version,
1945
+ name: name
1946
+ };
1947
+
1496
1948
  const CmsVersionCommand = {
1497
1949
  command: "cms:version",
1498
1950
  aliases: "$0",
@@ -1520,10 +1972,11 @@ const CmsVersionCommand = {
1520
1972
  colAligns: ["left", "left"]
1521
1973
  });
1522
1974
  info.push(["Instance", client.cmsUrl.host]);
1523
- info.push(["Client", client.apiVersion]);
1524
- info.push(["API", versionInfo.apiVersion]);
1525
- info.push(["CMS", versionInfo.cmsVersion]);
1526
- info.push(["Service", versionInfo.serviceVersion]);
1975
+ info.push(["Client API", client.apiVersion]);
1976
+ info.push(["Service API", versionInfo.apiVersion]);
1977
+ info.push(["CMS Build", versionInfo.cmsVersion]);
1978
+ info.push(["Service Build", versionInfo.serviceVersion]);
1979
+ info.push(["SDK", APP.version]);
1527
1980
  process.stdout.write(info.toString() + "\n");
1528
1981
  process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Optimizely CMS Status: ${versionInfo.status}\n`));
1529
1982
  process.stdout.write(chalk.green(chalk.bold(figures.tick + " Done")) + "\n");
@@ -1845,16 +2298,11 @@ const commands = [
1845
2298
  NextJsVisualBuilderCommand,
1846
2299
  NextJsFactoryCommand,
1847
2300
  CmsResetCommand,
1848
- CmsVersionCommand
2301
+ CmsVersionCommand,
2302
+ SchemaVsCodeCommand,
2303
+ SchemaValidateCommand
1849
2304
  ];
1850
2305
 
1851
- var version = "6.0.0-pre3";
1852
- var name = "opti-cms";
1853
- var APP = {
1854
- version: version,
1855
- name: name
1856
- };
1857
-
1858
2306
  async function main() {
1859
2307
  const envFiles = prepare();
1860
2308
  const app = createOptiCmsApp(APP.name, APP.version, undefined, envFiles);
@@ -1864,7 +2312,7 @@ async function main() {
1864
2312
  }
1865
2313
  catch {
1866
2314
  //We're ignoring error here, as yargs will already generate the "nice output" for it
1867
- //console.log ('Caught error')
2315
+ //console.log('Caught error')
1868
2316
  }
1869
2317
  }
1870
2318
  main();