@remkoj/optimizely-cms-cli 5.2.0 → 5.3.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/dist/index.js CHANGED
@@ -3,11 +3,12 @@ import dotenv from 'dotenv';
3
3
  import { expand } from 'dotenv-expand';
4
4
  import path from 'node:path';
5
5
  import fs from 'node:fs';
6
- import { getCmsIntegrationApiConfigFromEnvironment, createClient, OptiCmsVersion, IntegrationApi, ContentRoots } from '@remkoj/optimizely-cms-api';
6
+ import { getCmsIntegrationApiConfigFromEnvironment, createClient, ContentBaseType, OptiCmsVersion, PropertyDataType, ContentRoots } from '@remkoj/optimizely-cms-api';
7
7
  import yargs from 'yargs';
8
8
  import chalk from 'chalk';
9
9
  import figures from 'figures';
10
10
  import Table from 'cli-table3';
11
+ import { diff } from 'deep-object-diff';
11
12
  import fsAsync from 'node:fs/promises';
12
13
  import { input, select, confirm } from '@inquirer/prompts';
13
14
 
@@ -79,7 +80,7 @@ function createOptiCmsApp(scriptName, version, epilogue, envFiles, projectDir) {
79
80
  console.error(msg + "\n");
80
81
  if (error)
81
82
  console.error(`[${chalk.bold(error.name ?? 'Error')}]: ${error.message ?? ''}\n`);
82
- args.showHelp("error");
83
+ //args.showHelp("error")
83
84
  });
84
85
  }
85
86
  function isDemanded(value) {
@@ -138,79 +139,6 @@ function createCmsClient(args) {
138
139
  return client;
139
140
  }
140
141
 
141
- const StylesPushCommand = {
142
- command: "styles:push",
143
- describe: "Push Visual Builder style definitions into the CMS (create/replace)",
144
- builder: (yargs) => {
145
- yargs.option('excludeTemplates', { alias: 'e', description: "Exclude these templates", string: true, type: 'array', demandOption: false, default: [] });
146
- yargs.option("templates", { alias: 't', description: "Select only these templates", string: true, type: 'array', demandOption: false, default: [] });
147
- return yargs;
148
- },
149
- handler: async (args) => {
150
- const { _config: cfg, excludeTemplates, templates, ...opts } = parseArgs(args);
151
- const client = createCmsClient(args);
152
- if (client.runtimeCmsVersion == OptiCmsVersion.CMS12) {
153
- process.stdout.write(chalk.gray(`${figures.cross} Styles are not supported on CMS12\n`));
154
- return;
155
- }
156
- process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pushing (create/replace) DisplayStyles into Optimizely CMS\n`));
157
- const styleDefinitionFiles = await glob("./**/*.opti-style.json", {
158
- cwd: opts.components
159
- });
160
- const results = (await Promise.all(styleDefinitionFiles.map(async (styleDefinitionFile) => {
161
- const filePath = path.normalize(path.join(opts.components, styleDefinitionFile));
162
- const styleDefinition = tryReadJsonFile(filePath, cfg.debug);
163
- const styleKey = styleDefinition.key;
164
- if (!styleKey) {
165
- process.stderr.write(chalk.redBright(`${chalk.bold(figures.cross)} The style definition in ${path.relative(opts.path, filePath)} does not have a key defined\n`));
166
- return undefined;
167
- }
168
- if (excludeTemplates.includes(styleKey))
169
- return undefined; // Skip excluded styles
170
- if (templates.length > 0 && !templates.includes(styleKey))
171
- return undefined; // Only include defined styles, if any
172
- if (cfg.debug)
173
- process.stdout.write(chalk.gray(`${figures.arrowRight} Pushing: ${styleKey}\n`));
174
- const newTemplate = await client.displayTemplates.displayTemplatesPut(styleKey, styleDefinition);
175
- return newTemplate;
176
- }))).filter(isNotNullOrUndefined);
177
- const styles = new Table({
178
- head: [
179
- chalk.yellow(chalk.bold("Name")),
180
- chalk.yellow(chalk.bold("Key")),
181
- chalk.yellow(chalk.bold("Default")),
182
- chalk.yellow(chalk.bold("Target"))
183
- ],
184
- colWidths: [31, 20, 9, 20],
185
- colAligns: ["left", "left", "center", "left"]
186
- });
187
- results.forEach(tpl => {
188
- styles.push([
189
- tpl.displayName,
190
- tpl.key,
191
- tpl.isDefault ? figures.tick : figures.cross,
192
- tpl.contentType ? `${tpl.contentType} (C)` : tpl.baseType ? `${tpl.baseType} (B)` : `${tpl.nodeType} (N)`
193
- ]);
194
- });
195
- process.stdout.write(styles.toString() + "\n");
196
- process.stdout.write(chalk.green(chalk.bold(figures.tick + " Done")) + "\n");
197
- }
198
- };
199
- function tryReadJsonFile(filePath, debug = false) {
200
- try {
201
- if (debug)
202
- process.stdout.write(chalk.gray(`${figures.arrowRight} Reading style definition from ${filePath}\n`));
203
- return JSON.parse(fs.readFileSync(filePath, { encoding: 'utf-8' }));
204
- }
205
- catch (e) {
206
- process.stderr.write(chalk.redBright(`${chalk.bold(figures.cross)} Error while reading ${filePath}\n`));
207
- }
208
- return undefined;
209
- }
210
- function isNotNullOrUndefined(i) {
211
- return i ? true : false;
212
- }
213
-
214
142
  const ContentTypesArgsDefaults = {
215
143
  excludeBaseTypes: [],
216
144
  excludeTypes: ['folder', 'media', 'image', 'video'],
@@ -250,7 +178,7 @@ async function getContentTypes(client, args, pageSize = 100, allowSystem = false
250
178
  process.stdout.write(chalk.gray(`${figures.arrowRight} Fetched ${results.length} Content-Types from Optimizely CMS\n`));
251
179
  process.stdout.write(chalk.gray(`${figures.arrowRight} Filtering Content-Types based upon arguments\n`));
252
180
  }
253
- const validBaseTypes = getEnumOptions(IntegrationApi.ContentBaseType).map(x => x.toLowerCase());
181
+ const validBaseTypes = getEnumOptions(ContentBaseType).map(x => x.toLowerCase());
254
182
  if (cfg.debug)
255
183
  process.stdout.write(`Allowing base types: ${validBaseTypes.join(', ')}\n`);
256
184
  const allContentTypes = all ?
@@ -409,6 +337,269 @@ async function getStyleFilePath(definition, opts) {
409
337
  throw new Error(`Unable to resolve the target for the DisplayTemplate: ${definition.key}`);
410
338
  }
411
339
 
340
+ function normalizeMergePatch(value, omitKeys, requiredKeys = [], requiredSource) {
341
+ if (value === undefined || value === null)
342
+ return null;
343
+ if (Array.isArray(value))
344
+ return value.map(item => normalizeMergePatch(item, omitKeys));
345
+ if (typeof value !== 'object')
346
+ return value;
347
+ const output = Object.entries(value).reduce((acc, [key, entry]) => {
348
+ if (!omitKeys.includes(key))
349
+ acc[key] = normalizeMergePatch(entry, omitKeys);
350
+ return acc;
351
+ }, {});
352
+ const withRequiredKeys = requiredKeys.reduce((acc, key) => {
353
+ if (requiredSource && !Object.keys(acc).includes(key))
354
+ acc[key] = requiredSource[key];
355
+ return acc;
356
+ }, output);
357
+ return withRequiredKeys;
358
+ }
359
+ /**
360
+ * Computes a JSON Merge Patch (RFC 7396) that transforms `currentValue` into `newValue`.
361
+ *
362
+ * The resulting patch can be sent directly as the request body of an `application/merge-patch+json`
363
+ * request. Changed and added properties are included with their new value; removed properties are
364
+ * included with a `null` value (as required by RFC 7396); unchanged properties are omitted entirely.
365
+ *
366
+ * @see https://datatracker.ietf.org/doc/html/rfc7396
367
+ *
368
+ * @example Basic usage
369
+ * ```ts
370
+ * const current = { name: 'Alice', age: 30, role: 'user' }
371
+ * const updated = { name: 'Alice', age: 31 }
372
+ *
373
+ * generatePatch(current, updated)
374
+ * // => { age: 31, role: null }
375
+ * // `age` is replaced, `role` is removed (null), `name` is unchanged (omitted)
376
+ * ```
377
+ *
378
+ * @example Excluding keys from the patch
379
+ * ```ts
380
+ * const current = { id: '123', name: 'Alice', version: 1 }
381
+ * const updated = { id: '123', name: 'Bob', version: 2 }
382
+ *
383
+ * generatePatch(current, updated, { readonlyFields: ['id', 'version'] })
384
+ * // => { name: 'Bob' }
385
+ * // `id` and `version` are excluded even though they are present in the diff
386
+ * ```
387
+ *
388
+ * @example Always include required fields
389
+ * ```ts
390
+ * const current = { id: '123', name: 'Alice', version: 1 }
391
+ * const updated = { id: '123', name: 'Alice', version: 2 }
392
+ *
393
+ * generatePatch(current, updated, { requiredFields: ['id'] })
394
+ * // => { id: '123', version: 2 }
395
+ * // `id` is included even though it did not change
396
+ * ```
397
+ *
398
+ * @example Nested objects
399
+ * ```ts
400
+ * const current = { address: { city: 'Amsterdam', zip: '1000AA' } }
401
+ * const updated = { address: { city: 'Utrecht' } }
402
+ *
403
+ * generatePatch(current, updated)
404
+ * // => { address: { city: 'Utrecht', zip: null } }
405
+ * ```
406
+ *
407
+ * @param currentValue - The current state of the resource.
408
+ * @param newValue - The desired state of the resource.
409
+ * @param options - Optional configuration.
410
+ * @param options.readonlyFields - Keys that should never appear in the generated patch,
411
+ * regardless of whether they changed.
412
+ * @param options.requiredFields - Keys that should always appear in the generated patch,
413
+ * using values from `newValue`.
414
+ * @returns A {@link MergePatch} object ready to be serialised as an `application/merge-patch+json` body.
415
+ */
416
+ function generatePatch(currentValue, newValue, options = {}) {
417
+ const requiredKeys = options.requiredFields ?? [];
418
+ const omitKeys = (options.readonlyFields ?? []).filter(key => !requiredKeys.includes(key));
419
+ const patch = diff(currentValue, newValue);
420
+ return normalizeMergePatch(patch, omitKeys, requiredKeys, newValue);
421
+ }
422
+ /**
423
+ * Extracts all field paths from a {@link MergePatch} as dot-separated strings.
424
+ * Nested objects are flattened recursively; leaf values (including `null`) produce a path entry.
425
+ *
426
+ * @example
427
+ * ```ts
428
+ * getPatchFields({ a: { b: 0 }, c: 'value' })
429
+ * // => ['a.b', 'c']
430
+ * ```
431
+ *
432
+ * @param patch - The merge patch to extract fields from.
433
+ * @param prefix - Internal prefix used during recursion; omit when calling directly.
434
+ * @returns An array of dot-separated field path strings.
435
+ */
436
+ function getPatchFields(patch, prefix = '') {
437
+ return Object.entries(patch).flatMap(([key, value]) => {
438
+ const path = prefix ? `${prefix}.${key}` : key;
439
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
440
+ ? getPatchFields(value, path)
441
+ : [path];
442
+ });
443
+ }
444
+
445
+ /**
446
+ * CLI command that synchronizes local `.opti-style.json` definitions with Optimizely CMS.
447
+ *
448
+ * Existing styles are patched, missing styles are created, and command options allow
449
+ * selecting or excluding template keys during the push.
450
+ */
451
+ const StylesPushCommand = {
452
+ command: 'styles:push',
453
+ describe: 'Push Visual Builder style definitions into the CMS (create/patch)',
454
+ builder: (yargs) => {
455
+ yargs.option('excludeTemplates', {
456
+ alias: 'e',
457
+ description: 'Exclude these templates',
458
+ string: true,
459
+ type: 'array',
460
+ demandOption: false,
461
+ default: [],
462
+ });
463
+ yargs.option('templates', {
464
+ alias: 't',
465
+ description: 'Select only these templates',
466
+ string: true,
467
+ type: 'array',
468
+ demandOption: false,
469
+ default: [],
470
+ });
471
+ return yargs;
472
+ },
473
+ handler: async (args) => {
474
+ const { _config: { debug = false }, excludeTemplates, templates, ...opts } = parseArgs(args);
475
+ const client = createCmsClient(args);
476
+ if (client.runtimeCmsVersion == OptiCmsVersion.CMS12) {
477
+ process.stdout.write(chalk.gray(`${figures.cross} Styles are not supported on CMS12\n`));
478
+ return;
479
+ }
480
+ const { styles: displayTemplates } = await getStyles(client, {
481
+ // Defaults
482
+ all: true,
483
+ baseTypes: [],
484
+ excludeBaseTypes: [],
485
+ excludeNodeTypes: [],
486
+ excludeTemplates: [],
487
+ excludeTypes: [],
488
+ nodes: [],
489
+ templates: [],
490
+ templateTypes: [],
491
+ types: [],
492
+ // Arguments from this method
493
+ ...args,
494
+ }, 50);
495
+ process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pushing (create/patch) DisplayStyles into Optimizely CMS\n`));
496
+ // Listing all style files
497
+ const styleDefinitionFiles = await glob('./**/*.opti-style.json', {
498
+ cwd: opts.components,
499
+ });
500
+ // Helper function to read the files from disk
501
+ function readStyleFile(styleDefinitionFile) {
502
+ const filePath = path.normalize(path.join(opts.components, styleDefinitionFile));
503
+ const styleDefinition = tryReadJsonFile(filePath, debug);
504
+ return styleDefinition ? {
505
+ styleKey: styleDefinition.key,
506
+ styleDefinition,
507
+ styleFile: filePath
508
+ } : undefined;
509
+ }
510
+ // Helper function to filter the files read from disk
511
+ function filterStyleDefinition(data) {
512
+ if (!data || !data.styleKey)
513
+ return false;
514
+ const { styleKey } = data;
515
+ if (excludeTemplates.includes(styleKey)) {
516
+ if (debug)
517
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping ${styleKey} - explicitly excluded\n`));
518
+ return false;
519
+ }
520
+ // Skip if there a selectin and this template is not in it
521
+ if (templates.length > 0 && !templates.includes(styleKey)) {
522
+ if (debug)
523
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Skipping ${styleKey} - not in selected list of templates\n`));
524
+ return false; // Only include defined styles, if any
525
+ }
526
+ return true;
527
+ }
528
+ // Processing each file
529
+ const results = await Promise.allSettled(styleDefinitionFiles
530
+ .map(readStyleFile)
531
+ .filter(filterStyleDefinition)
532
+ .map(async ({ styleKey, styleDefinition, styleFile }) => {
533
+ const displayTemplate = displayTemplates.find(dt => dt.key === styleKey);
534
+ // Confirm we're including
535
+ process.stdout.write(chalk.yellowBright(`${figures.arrowRight} ${displayTemplate ? 'Updating' : 'Creating'} ${styleKey}\n`));
536
+ if (debug)
537
+ process.stdout.write(chalk.gray(`${figures.arrowRight} from ${styleFile || 'unknown source'}\n`));
538
+ // Patch or create the template
539
+ const newTemplate = await (displayTemplate ? (async () => {
540
+ const patch = generatePatch(displayTemplate, styleDefinition, {
541
+ readonlyFields: ['key', 'created', 'createdBy', 'lastModified', 'lastModifiedBy'],
542
+ });
543
+ if (!path || Object.entries(patch).length === 0)
544
+ return displayTemplate;
545
+ // @ts-expect-error: We're expecting an error here due to the CMS Logic not matching
546
+ // the OpenAPI Specification.
547
+ return client.displayTemplates.displayTemplatesPatch(styleKey, patch);
548
+ })() : client.displayTemplates.displayTemplatesCreate(displayTemplate));
549
+ // Validate the result
550
+ const unpatchedFields = generatePatch(newTemplate, styleDefinition, {
551
+ readonlyFields: ['key', 'created', 'createdBy', 'lastModified', 'lastModifiedBy']
552
+ });
553
+ if (unpatchedFields && Object.entries(unpatchedFields).length > 0)
554
+ throw new Error(`Creating/patching of displayTemplate failed, the following fields failed: ${getPatchFields(unpatchedFields).join('; ')}`);
555
+ // Return the template after Create/Patch
556
+ return newTemplate;
557
+ }));
558
+ const styles = new Table({
559
+ head: [
560
+ chalk.yellow(chalk.bold('Name')),
561
+ chalk.yellow(chalk.bold('Key')),
562
+ chalk.yellow(chalk.bold('Default')),
563
+ chalk.yellow(chalk.bold('Target')),
564
+ ],
565
+ colWidths: [31, 20, 9, 20],
566
+ colAligns: ['left', 'left', 'center', 'left'],
567
+ });
568
+ results.forEach((result) => {
569
+ if (result.status === 'fulfilled') {
570
+ const tpl = result.value;
571
+ styles.push([
572
+ tpl.displayName,
573
+ tpl.key,
574
+ tpl.isDefault ? figures.tick : figures.cross,
575
+ tpl.contentType
576
+ ? `${tpl.contentType} (C)`
577
+ : tpl.baseType
578
+ ? `${tpl.baseType} (B)`
579
+ : `${tpl.nodeType} (N)`,
580
+ ]);
581
+ }
582
+ else {
583
+ const error = result.reason;
584
+ process.stderr.write(`Error creating/updating style: ${error}`);
585
+ }
586
+ });
587
+ process.stdout.write(styles.toString() + '\n');
588
+ process.stdout.write(chalk.green(chalk.bold(figures.tick + ' Done')) + '\n');
589
+ },
590
+ };
591
+ function tryReadJsonFile(filePath, debug = false) {
592
+ try {
593
+ if (debug)
594
+ process.stdout.write(chalk.gray(`${figures.arrowRight} Reading style definition from ${filePath}\n`));
595
+ return JSON.parse(fs.readFileSync(filePath, { encoding: 'utf-8' }));
596
+ }
597
+ catch (e) {
598
+ process.stderr.write(chalk.redBright(`${chalk.bold(figures.cross)} Error while reading ${filePath}\n`));
599
+ }
600
+ return undefined;
601
+ }
602
+
412
603
  const StylesListCommand = {
413
604
  command: "styles:list",
414
605
  describe: "List Visual Builder style definitions from the CMS",
@@ -864,7 +1055,7 @@ const TypesPullCommand = {
864
1055
 
865
1056
  const TypesPushCommand = {
866
1057
  command: "types:push",
867
- describe: "Push content type definition into Optimizely CMS (create / replace)",
1058
+ describe: "Push content type definition into Optimizely CMS (create / patch)",
868
1059
  builder: (yargs) => {
869
1060
  yargs.option('force', { alias: 'f', description: "Overwrite existing files", boolean: true, type: 'boolean', demandOption: false, default: false });
870
1061
  yargs.option('excludeTypes', { alias: 'ect', description: "Exclude these content types", string: true, type: 'array', demandOption: false, default: [] });
@@ -877,7 +1068,7 @@ const TypesPushCommand = {
877
1068
  const { _config: cfg, components: basePath, excludeBaseTypes, excludeTypes, baseTypes, types, force } = parseArgs(args);
878
1069
  const client = createClient(cfg);
879
1070
  // Find all type files
880
- process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pushing (create/replace) Content Types into Optimizely CMS\n`));
1071
+ process.stdout.write(chalk.yellowBright(`${figures.arrowRight} Pushing (create/patch) Content Types into Optimizely CMS\n`));
881
1072
  const typeDefinitionFiles = await glob("./**/*.opti-type.json", { cwd: basePath });
882
1073
  // Read & filter all identified type files
883
1074
  const typeDefinitions = typeDefinitionFiles.map(file => {
@@ -907,7 +1098,7 @@ const TypesPushCommand = {
907
1098
  delete outType.features;
908
1099
  if (outType.usage)
909
1100
  delete outType.usage;
910
- return client.contentTypes.contentTypesPut(outType.key, outType, force)
1101
+ return client.contentTypes.contentTypesPatch(outType.key, outType, force)
911
1102
  .then(ct => { return { key: type.definition.key, type: ct, file: type.file }; })
912
1103
  .catch(e => { return { key: type.definition.key, type: type.definition, file: type.file, error: e }; });
913
1104
  }));
@@ -1090,11 +1281,11 @@ function renderProperties(contentType, generatedProps = [], forCms12 = false) {
1090
1281
  const propName = isConflict ? `${contentType.key}${propKey}: ${propKey}` : propKey;
1091
1282
  // Write the property
1092
1283
  switch (propType) {
1093
- case IntegrationApi.PropertyDataType.ARRAY:
1284
+ case PropertyDataType.ARRAY:
1094
1285
  {
1095
1286
  const typeData = typeProps[propKey];
1096
1287
  switch (typeData.items.type) {
1097
- case IntegrationApi.PropertyDataType.INTEGER:
1288
+ case PropertyDataType.INTEGER:
1098
1289
  if (typeData.format == 'categorization') {
1099
1290
  //fragmentFields.push(`${propName} { Id, Name, Description }`)
1100
1291
  console.warn(chalk.redBright(`❗ Property ${propName} is a 'categorization', this is not supported. If you need to use this property, add it manually into the generated files`));
@@ -1102,16 +1293,16 @@ function renderProperties(contentType, generatedProps = [], forCms12 = false) {
1102
1293
  else
1103
1294
  fragmentFields.push(propName);
1104
1295
  break;
1105
- case IntegrationApi.PropertyDataType.STRING:
1296
+ case PropertyDataType.STRING:
1106
1297
  fragmentFields.push(propName);
1107
1298
  break;
1108
- case IntegrationApi.PropertyDataType.CONTENT:
1299
+ case PropertyDataType.CONTENT:
1109
1300
  if (contentType.baseType == 'page' || contentType.baseType == 'experience')
1110
1301
  fragmentFields.push(`${propName} { ...${forCms12 ? 'PageIContentListItem' : 'BlockData'} }`);
1111
1302
  else
1112
1303
  fragmentFields.push(`${propName} { ...IContentListItem }`);
1113
1304
  break;
1114
- case IntegrationApi.PropertyDataType.COMPONENT:
1305
+ case PropertyDataType.COMPONENT:
1115
1306
  const componentType = typeData.items.contentType.split(':').pop();
1116
1307
  switch (componentType) {
1117
1308
  case 'link':
@@ -1126,7 +1317,7 @@ function renderProperties(contentType, generatedProps = [], forCms12 = false) {
1126
1317
  }
1127
1318
  }
1128
1319
  break;
1129
- case IntegrationApi.PropertyDataType.CONTENT_REFERENCE:
1320
+ case PropertyDataType.CONTENT_REFERENCE:
1130
1321
  fragmentFields.push(`${propName} { ...ReferenceData }`);
1131
1322
  break;
1132
1323
  default:
@@ -1135,7 +1326,7 @@ function renderProperties(contentType, generatedProps = [], forCms12 = false) {
1135
1326
  }
1136
1327
  break;
1137
1328
  }
1138
- case IntegrationApi.PropertyDataType.STRING: {
1329
+ case PropertyDataType.STRING: {
1139
1330
  const propDetails = typeProps[propKey];
1140
1331
  switch (propDetails.format ?? "") {
1141
1332
  case 'html':
@@ -1153,13 +1344,13 @@ function renderProperties(contentType, generatedProps = [], forCms12 = false) {
1153
1344
  }
1154
1345
  break;
1155
1346
  }
1156
- case IntegrationApi.PropertyDataType.URL:
1347
+ case PropertyDataType.URL:
1157
1348
  fragmentFields.push(forCms12 ? propName : `${propName} { ...LinkData }`);
1158
1349
  break;
1159
- case IntegrationApi.PropertyDataType.CONTENT_REFERENCE:
1350
+ case PropertyDataType.CONTENT_REFERENCE:
1160
1351
  fragmentFields.push(`${propName} { ...ReferenceData }`);
1161
1352
  break;
1162
- case IntegrationApi.PropertyDataType.COMPONENT: {
1353
+ case PropertyDataType.COMPONENT: {
1163
1354
  const componentType = typeProps[propKey].contentType.split(':').pop();
1164
1355
  if (componentType == "link") {
1165
1356
  fragmentFields.push(`${propName} { ...LinkItemData }`);
@@ -1171,10 +1362,10 @@ function renderProperties(contentType, generatedProps = [], forCms12 = false) {
1171
1362
  }
1172
1363
  break;
1173
1364
  }
1174
- case IntegrationApi.PropertyDataType.BINARY:
1365
+ case PropertyDataType.BINARY:
1175
1366
  fragmentFields.push(propName);
1176
1367
  break;
1177
- case IntegrationApi.PropertyDataType.CONTENT:
1368
+ case PropertyDataType.CONTENT:
1178
1369
  fragmentFields.push(`${propName} { ...${forCms12 ? 'PageIContentListItem' : 'BlockData'} }`);
1179
1370
  break;
1180
1371
  default:
@@ -2169,7 +2360,7 @@ const commands = [
2169
2360
  CmsVersionCommand
2170
2361
  ];
2171
2362
 
2172
- var version = "5.2.0";
2363
+ var version = "5.3.0";
2173
2364
  var name = "opti-cms";
2174
2365
  var APP = {
2175
2366
  version: version,