@contentstack/cli-audit 2.0.0-beta.3 → 2.0.0-beta.5

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/README.md CHANGED
@@ -19,7 +19,7 @@ $ npm install -g @contentstack/cli-audit
19
19
  $ csdx COMMAND
20
20
  running command...
21
21
  $ csdx (--version|-v)
22
- @contentstack/cli-audit/2.0.0-beta.3 linux-x64 node-v22.22.0
22
+ @contentstack/cli-audit/2.0.0-beta.5 linux-x64 node-v22.22.0
23
23
  $ csdx --help [COMMAND]
24
24
  USAGE
25
25
  $ csdx COMMAND
@@ -341,10 +341,10 @@ class AuditBaseCommand extends base_command_1.BaseCommand {
341
341
  * `gfSchema`. The values of these properties are the parsed JSON data from two different files.
342
342
  */
343
343
  getCtAndGfSchema() {
344
- const ctPath = (0, path_1.join)(this.sharedConfig.basePath, this.sharedConfig.moduleConfig['content-types'].dirName, this.sharedConfig.moduleConfig['content-types'].fileName);
344
+ const ctDirPath = (0, path_1.join)(this.sharedConfig.basePath, this.sharedConfig.moduleConfig['content-types'].dirName);
345
345
  const gfPath = (0, path_1.join)(this.sharedConfig.basePath, this.sharedConfig.moduleConfig['global-fields'].dirName, this.sharedConfig.moduleConfig['global-fields'].fileName);
346
346
  const gfSchema = (0, fs_1.existsSync)(gfPath) ? JSON.parse((0, fs_1.readFileSync)(gfPath, 'utf8')) : [];
347
- const ctSchema = (0, fs_1.existsSync)(ctPath) ? JSON.parse((0, fs_1.readFileSync)(ctPath, 'utf8')) : [];
347
+ const ctSchema = ((0, cli_utilities_1.readContentTypeSchemas)(ctDirPath) || []);
348
348
  return { ctSchema, gfSchema };
349
349
  }
350
350
  /**
@@ -20,7 +20,7 @@ const config = {
20
20
  moduleConfig: {
21
21
  'content-types': {
22
22
  name: 'content type',
23
- fileName: 'schema.json',
23
+ fileName: 'schema.json', // Not used - reads from individual files
24
24
  dirName: 'content_types',
25
25
  },
26
26
  'global-fields': {
@@ -152,7 +152,7 @@ class ContentType extends base_class_1.default {
152
152
  * JSON to the specified file path.
153
153
  */
154
154
  async writeFixContent() {
155
- var _a, _b, _c;
155
+ var _a, _b, _c, _d, _e;
156
156
  cli_utilities_1.log.debug('Starting writeFixContent process', this.config.auditContext);
157
157
  let canWrite = true;
158
158
  if (!this.inMemoryFix && this.fix) {
@@ -165,10 +165,19 @@ class ContentType extends base_class_1.default {
165
165
  cli_utilities_1.log.debug('Skipping confirmation due to copy-dir or external-config flags', this.config.auditContext);
166
166
  }
167
167
  if (canWrite) {
168
- const filePath = (0, path_1.join)(this.folderPath, this.config.moduleConfig[this.moduleName].fileName);
169
- cli_utilities_1.log.debug(`Writing fixed schema to: ${filePath}`, this.config.auditContext);
170
- (0, fs_1.writeFileSync)(filePath, JSON.stringify(this.schema));
171
- cli_utilities_1.log.debug(`Successfully wrote ${((_c = this.schema) === null || _c === void 0 ? void 0 : _c.length) || 0} schemas to file`, this.config.auditContext);
168
+ // For content-types, write individual files instead of schema.json
169
+ cli_utilities_1.log.debug(`Writing ${((_c = this.schema) === null || _c === void 0 ? void 0 : _c.length) || 0} content types to individual files`, this.config.auditContext);
170
+ for (const contentType of (_d = this.schema) !== null && _d !== void 0 ? _d : []) {
171
+ if (contentType.uid) {
172
+ const filePath = (0, path_1.join)(this.folderPath, `${contentType.uid}.json`);
173
+ cli_utilities_1.log.debug(`Writing fixed content type to: ${filePath}`, this.config.auditContext);
174
+ (0, fs_1.writeFileSync)(filePath, JSON.stringify(contentType));
175
+ }
176
+ else {
177
+ cli_utilities_1.log.warn(`Skipping content type without uid: ${JSON.stringify(contentType).substring(0, 100)}`, this.config.auditContext);
178
+ }
179
+ }
180
+ cli_utilities_1.log.debug(`Successfully wrote ${((_e = this.schema) === null || _e === void 0 ? void 0 : _e.length) || 0} content types to individual files`, this.config.auditContext);
172
181
  }
173
182
  else {
174
183
  cli_utilities_1.log.debug('User declined to write fix content', this.config.auditContext);
@@ -23,6 +23,21 @@ export default class Entries extends BaseClass {
23
23
  moduleName: keyof typeof auditConfig.moduleConfig;
24
24
  constructor({ fix, config, moduleName, ctSchema, gfSchema }: ModuleConstructorParam & CtConstructorParam);
25
25
  validateModules(moduleName: keyof typeof auditConfig.moduleConfig, moduleConfig: Record<string, unknown>): keyof typeof auditConfig.moduleConfig;
26
+ /**
27
+ * Returns whether a referenced entry's content type is allowed by the schema's reference_to.
28
+ * @param refCtUid - Content type UID of the referenced entry (e.g. from _content_type_uid)
29
+ * @param referenceTo - Schema's reference_to (string or string[])
30
+ * @param configOverride - Optional config with skipRefs; falls back to this.config
31
+ * @returns true if allowed or check cannot be performed; false if refCtUid is not in reference_to
32
+ */
33
+ protected isRefContentTypeAllowed(refCtUid: string | undefined, referenceTo: string | string[] | undefined, configOverride?: {
34
+ skipRefs?: string[];
35
+ }): boolean;
36
+ /**
37
+ * If ref CT is not allowed, pushes to missingRefs.
38
+ * @returns true if invalid (pushed), false if valid
39
+ */
40
+ private addInvalidRefIfNeeded;
26
41
  /**
27
42
  * The `run` function checks if a folder path exists, sets the schema based on the module name,
28
43
  * iterates over the schema and looks for references, and returns a list of missing references.
@@ -47,6 +47,39 @@ class Entries extends base_class_1.default {
47
47
  cli_utilities_1.log.debug(`Module ${moduleName} not found in config, defaulting to: entries`, this.config.auditContext);
48
48
  return 'entries';
49
49
  }
50
+ /**
51
+ * Returns whether a referenced entry's content type is allowed by the schema's reference_to.
52
+ * @param refCtUid - Content type UID of the referenced entry (e.g. from _content_type_uid)
53
+ * @param referenceTo - Schema's reference_to (string or string[])
54
+ * @param configOverride - Optional config with skipRefs; falls back to this.config
55
+ * @returns true if allowed or check cannot be performed; false if refCtUid is not in reference_to
56
+ */
57
+ isRefContentTypeAllowed(refCtUid, referenceTo, configOverride) {
58
+ var _a, _b;
59
+ if (refCtUid === undefined)
60
+ return true;
61
+ const skipRefs = (_b = (_a = configOverride === null || configOverride === void 0 ? void 0 : configOverride.skipRefs) !== null && _a !== void 0 ? _a : this.config.skipRefs) !== null && _b !== void 0 ? _b : [];
62
+ if (Array.isArray(skipRefs) && skipRefs.includes(refCtUid))
63
+ return true;
64
+ if (referenceTo === undefined || referenceTo === null)
65
+ return true;
66
+ const refToList = Array.isArray(referenceTo) ? referenceTo : [referenceTo];
67
+ if (refToList.length === 0)
68
+ return false;
69
+ return refToList.includes(refCtUid);
70
+ }
71
+ /**
72
+ * If ref CT is not allowed, pushes to missingRefs.
73
+ * @returns true if invalid (pushed), false if valid
74
+ */
75
+ addInvalidRefIfNeeded(missingRefs, uidValue, refCtUid, referenceTo, fullRef, logLabel) {
76
+ if (this.isRefContentTypeAllowed(refCtUid, referenceTo))
77
+ return false;
78
+ cli_utilities_1.log.debug(`${logLabel} has wrong content type: ${refCtUid} not in reference_to`);
79
+ const refList = Array.isArray(referenceTo) ? referenceTo : referenceTo != null ? [referenceTo] : [];
80
+ missingRefs.push(refList.length === 1 ? { uid: uidValue, _content_type_uid: refCtUid } : fullRef);
81
+ return true;
82
+ }
50
83
  /**
51
84
  * The `run` function checks if a folder path exists, sets the schema based on the module name,
52
85
  * iterates over the schema and looks for references, and returns a list of missing references.
@@ -633,7 +666,7 @@ class Entries extends base_class_1.default {
633
666
  cli_utilities_1.log.debug(`Validating modular blocks field: ${fieldStructure.display_name}`, this.config.auditContext);
634
667
  cli_utilities_1.log.debug(`Modular blocks field UID: ${fieldStructure.uid}`, this.config.auditContext);
635
668
  cli_utilities_1.log.debug(`Found ${field.length} modular blocks`, this.config.auditContext);
636
- cli_utilities_1.log.debug(`Available blocks: ${fieldStructure.blocks.map(b => b.title).join(', ')}`);
669
+ cli_utilities_1.log.debug(`Available blocks: ${fieldStructure.blocks.map((b) => b.title).join(', ')}`);
637
670
  if (!this.fix) {
638
671
  cli_utilities_1.log.debug('Checking modular block references (non-fix mode)');
639
672
  for (const index in field) {
@@ -696,6 +729,7 @@ class Entries extends base_class_1.default {
696
729
  * objects.
697
730
  */
698
731
  validateReferenceValues(tree, fieldStructure, field) {
732
+ var _a;
699
733
  cli_utilities_1.log.debug(`Validating reference values for field: ${fieldStructure.display_name}`);
700
734
  if (this.fix) {
701
735
  cli_utilities_1.log.debug('Fix mode enabled, skipping reference validation');
@@ -703,8 +737,9 @@ class Entries extends base_class_1.default {
703
737
  }
704
738
  const missingRefs = [];
705
739
  const { uid: data_type, display_name, reference_to } = fieldStructure;
740
+ const refToList = Array.isArray(reference_to) ? reference_to : reference_to != null ? [reference_to] : [];
706
741
  cli_utilities_1.log.debug(`Reference field UID: ${data_type}`);
707
- cli_utilities_1.log.debug(`Reference to: ${(reference_to === null || reference_to === void 0 ? void 0 : reference_to.join(', ')) || 'none'}`);
742
+ cli_utilities_1.log.debug(`Reference to: ${refToList.join(', ') || 'none'}`);
708
743
  cli_utilities_1.log.debug(`Found ${(field === null || field === void 0 ? void 0 : field.length) || 0} references to validate`);
709
744
  for (const index in field !== null && field !== void 0 ? field : []) {
710
745
  const reference = field[index];
@@ -723,7 +758,10 @@ class Entries extends base_class_1.default {
723
758
  }
724
759
  }
725
760
  else {
726
- cli_utilities_1.log.debug(`Reference ${reference} is valid`);
761
+ const refCtUid = refExist.ctUid;
762
+ if (!this.addInvalidRefIfNeeded(missingRefs, reference, refCtUid, reference_to, reference, `Reference ${reference}`)) {
763
+ cli_utilities_1.log.debug(`Reference ${reference} is valid`);
764
+ }
727
765
  }
728
766
  }
729
767
  // NOTE Can skip specific references keys (Ex, system defined keys can be skipped)
@@ -736,7 +774,10 @@ class Entries extends base_class_1.default {
736
774
  missingRefs.push(reference);
737
775
  }
738
776
  else {
739
- cli_utilities_1.log.debug(`Reference ${uid} is valid`);
777
+ const refCtUid = (_a = reference._content_type_uid) !== null && _a !== void 0 ? _a : refExist.ctUid;
778
+ if (!this.addInvalidRefIfNeeded(missingRefs, uid, refCtUid, reference_to, reference, `Reference ${uid}`)) {
779
+ cli_utilities_1.log.debug(`Reference ${uid} is valid`);
780
+ }
740
781
  }
741
782
  }
742
783
  }
@@ -1384,6 +1425,7 @@ class Entries extends base_class_1.default {
1384
1425
  cli_utilities_1.log.debug(`Parsed entry: ${Array.isArray(entry) ? entry.length : 'N/A'} references`);
1385
1426
  }
1386
1427
  entry = entry === null || entry === void 0 ? void 0 : entry.map((reference, index) => {
1428
+ var _a, _b;
1387
1429
  const { uid } = reference;
1388
1430
  const { reference_to } = field;
1389
1431
  cli_utilities_1.log.debug(`Processing reference ${index}: ${uid || reference}`);
@@ -1400,6 +1442,10 @@ class Entries extends base_class_1.default {
1400
1442
  }
1401
1443
  }
1402
1444
  else {
1445
+ const refCtUid = (_a = reference._content_type_uid) !== null && _a !== void 0 ? _a : refExist.ctUid;
1446
+ if (this.addInvalidRefIfNeeded(missingRefs, reference, refCtUid, reference_to, reference, `Blt reference ${reference}`)) {
1447
+ return null;
1448
+ }
1403
1449
  cli_utilities_1.log.debug(`Blt reference ${reference} is valid`);
1404
1450
  return { uid: reference, _content_type_uid: refExist.ctUid };
1405
1451
  }
@@ -1413,6 +1459,10 @@ class Entries extends base_class_1.default {
1413
1459
  return null;
1414
1460
  }
1415
1461
  else {
1462
+ const refCtUid = (_b = reference._content_type_uid) !== null && _b !== void 0 ? _b : refExist.ctUid;
1463
+ if (this.addInvalidRefIfNeeded(missingRefs, uid, refCtUid, reference_to, reference, `Reference ${uid}`)) {
1464
+ return null;
1465
+ }
1416
1466
  cli_utilities_1.log.debug(`Reference ${uid} is valid`);
1417
1467
  return reference;
1418
1468
  }
@@ -1460,7 +1510,7 @@ class Entries extends base_class_1.default {
1460
1510
  */
1461
1511
  modularBlockRefCheck(tree, blocks, entryBlock, index) {
1462
1512
  cli_utilities_1.log.debug(`Checking modular block references for block ${index}`);
1463
- cli_utilities_1.log.debug(`Available block UIDs: ${blocks.map(b => b.uid).join(', ')}`);
1513
+ cli_utilities_1.log.debug(`Available block UIDs: ${blocks.map((b) => b.uid).join(', ')}`);
1464
1514
  cli_utilities_1.log.debug(`Entry block keys: ${Object.keys(entryBlock).join(', ')}`);
1465
1515
  const validBlockUid = blocks.map((block) => block.uid);
1466
1516
  const invalidKeys = Object.keys(entryBlock).filter((key) => !validBlockUid.includes(key));
@@ -1528,6 +1578,25 @@ class Entries extends base_class_1.default {
1528
1578
  return null;
1529
1579
  }
1530
1580
  else {
1581
+ const refCtUid = contentTypeUid !== null && contentTypeUid !== void 0 ? contentTypeUid : refExist.ctUid;
1582
+ const referenceTo = schema.reference_to;
1583
+ if (!this.isRefContentTypeAllowed(refCtUid, referenceTo)) {
1584
+ cli_utilities_1.log.debug(`JSON RTE embed ${entryUid} has wrong content type: ${refCtUid} not in reference_to`);
1585
+ this.missingRefs[this.currentUid].push({
1586
+ tree,
1587
+ uid: this.currentUid,
1588
+ name: this.currentTitle,
1589
+ data_type: schema.data_type,
1590
+ display_name: schema.display_name,
1591
+ fixStatus: this.fix ? 'Fixed' : undefined,
1592
+ treeStr: tree
1593
+ .map(({ name }) => name)
1594
+ .filter((val) => val)
1595
+ .join(' ➜ '),
1596
+ missingRefs: [{ uid: entryUid, 'content-type-uid': refCtUid }],
1597
+ });
1598
+ return this.fix ? null : true;
1599
+ }
1531
1600
  cli_utilities_1.log.debug(`Entry reference ${entryUid} is valid`);
1532
1601
  }
1533
1602
  }
@@ -183,12 +183,10 @@ class FieldRule extends base_class_1.default {
183
183
  cli_utilities_1.log.debug(`Fixing field rule ${index + 1}`, this.config.auditContext);
184
184
  cli_utilities_1.log.debug(`Original actions count: ${((_a = fr.actions) === null || _a === void 0 ? void 0 : _a.length) || 0}`, this.config.auditContext);
185
185
  cli_utilities_1.log.debug(`Original conditions count: ${((_b = fr.conditions) === null || _b === void 0 ? void 0 : _b.length) || 0}`, this.config.auditContext);
186
- const validActions = (_d = (_c = fr.actions) === null || _c === void 0 ? void 0 : _c.filter(action => {
186
+ const validActions = (_d = (_c = fr.actions) === null || _c === void 0 ? void 0 : _c.filter((action) => {
187
187
  const isValid = this.schemaMap.includes(action.target_field);
188
188
  cli_utilities_1.log.debug(`Action target_field=${action.target_field}, valid=${isValid}`, this.config.auditContext);
189
- const logMsg = isValid
190
- ? messages_1.auditMsg.FIELD_RULE_TARGET_SCAN_MESSAGE
191
- : messages_1.auditMsg.FIELD_RULE_TARGET_ABSENT;
189
+ const logMsg = isValid ? messages_1.auditMsg.FIELD_RULE_TARGET_SCAN_MESSAGE : messages_1.auditMsg.FIELD_RULE_TARGET_ABSENT;
192
190
  if (isValid) {
193
191
  cli_utilities_1.log.info((0, messages_1.$t)(logMsg, Object.assign({ num: index.toString(), ctUid: schema.uid }, (action.target_field && { target_field: action.target_field }))), this.config.auditContext);
194
192
  }
@@ -200,18 +198,16 @@ class FieldRule extends base_class_1.default {
200
198
  this.addMissingReferences(action, 'Fixed');
201
199
  cli_utilities_1.log.info((0, messages_1.$t)(messages_1.auditFixMsg.FIELD_RULE_FIX_MESSAGE, {
202
200
  num: index.toString(),
203
- ctUid: schema.uid
201
+ ctUid: schema.uid,
204
202
  }), this.config.auditContext);
205
203
  }
206
204
  return isValid;
207
205
  })) !== null && _d !== void 0 ? _d : [];
208
206
  cli_utilities_1.log.debug(`Valid actions after filtering: ${validActions.length}`, this.config.auditContext);
209
- const validConditions = (_f = (_e = fr.conditions) === null || _e === void 0 ? void 0 : _e.filter(condition => {
207
+ const validConditions = (_f = (_e = fr.conditions) === null || _e === void 0 ? void 0 : _e.filter((condition) => {
210
208
  const isValid = this.schemaMap.includes(condition.operand_field);
211
209
  cli_utilities_1.log.debug(`Condition operand_field=${condition.operand_field}, valid=${isValid}`, this.config.auditContext);
212
- const logMsg = isValid
213
- ? messages_1.auditMsg.FIELD_RULE_CONDITION_SCAN_MESSAGE
214
- : messages_1.auditMsg.FIELD_RULE_CONDITION_ABSENT;
210
+ const logMsg = isValid ? messages_1.auditMsg.FIELD_RULE_CONDITION_SCAN_MESSAGE : messages_1.auditMsg.FIELD_RULE_CONDITION_ABSENT;
215
211
  if (isValid) {
216
212
  cli_utilities_1.log.info((0, messages_1.$t)(logMsg, Object.assign({ num: index.toString(), ctUid: schema.uid }, (condition.operand_field && { condition_field: condition.operand_field }))), this.config.auditContext);
217
213
  }
@@ -223,7 +219,7 @@ class FieldRule extends base_class_1.default {
223
219
  this.addMissingReferences(condition, 'Fixed');
224
220
  cli_utilities_1.log.info((0, messages_1.$t)(messages_1.auditFixMsg.FIELD_RULE_FIX_MESSAGE, {
225
221
  num: index.toString(),
226
- ctUid: schema.uid
222
+ ctUid: schema.uid,
227
223
  }), this.config.auditContext);
228
224
  }
229
225
  return isValid;
@@ -231,7 +227,8 @@ class FieldRule extends base_class_1.default {
231
227
  cli_utilities_1.log.debug(`Valid conditions after filtering: ${validConditions.length}`, this.config.auditContext);
232
228
  const shouldKeepRule = validActions.length && validConditions.length;
233
229
  cli_utilities_1.log.debug(`Field rule ${index + 1} ${shouldKeepRule ? 'kept' : 'removed'} (actions: ${validActions.length}, conditions: ${validConditions.length})`, this.config.auditContext);
234
- return shouldKeepRule ? Object.assign(Object.assign({}, fr), { actions: validActions, conditions: validConditions }) : null;
230
+ return shouldKeepRule
231
+ ? Object.assign(Object.assign({}, fr), { actions: validActions, conditions: validConditions }) : null;
235
232
  })
236
233
  .filter(Boolean);
237
234
  cli_utilities_1.log.debug(`Field rules fix completed for schema: ${schema.uid}. ${schema.field_rules.length} rules remaining`, this.config.auditContext);
@@ -322,11 +319,20 @@ class FieldRule extends base_class_1.default {
322
319
  cli_utilities_1.log.debug(`Skipping confirmation due to flags`, this.config.auditContext);
323
320
  }
324
321
  if (canWrite) {
325
- const outputPath = (0, path_1.join)(this.folderPath, this.config.moduleConfig[this.moduleName].fileName);
326
- cli_utilities_1.log.debug(`Writing fixed schema to: ${outputPath}`, this.config.auditContext);
327
- cli_utilities_1.log.debug(`Schema items to write: ${((_d = this.schema) === null || _d === void 0 ? void 0 : _d.length) || 0}`, this.config.auditContext);
328
- (0, fs_1.writeFileSync)(outputPath, JSON.stringify(this.schema));
329
- cli_utilities_1.log.debug(`Successfully wrote fixed schema to file`, this.config.auditContext);
322
+ // const outputPath = join(this.folderPath, this.config.moduleConfig[this.moduleName].fileName);
323
+ // log.debug(`Writing fixed schema to: ${outputPath}`, this.config.auditContext);
324
+ // log.debug(`Schema items to write: ${this.schema?.length || 0}`, this.config.auditContext);
325
+ // writeFileSync(outputPath, JSON.stringify(this.schema));
326
+ // log.debug(`Successfully wrote fixed schema to file`, this.config.auditContext);
327
+ for (const schema of (_d = this.schema) !== null && _d !== void 0 ? _d : []) {
328
+ if (!(schema === null || schema === void 0 ? void 0 : schema.uid)) {
329
+ cli_utilities_1.log.warn(`Skipping schema with missing uid`, this.config.auditContext);
330
+ continue;
331
+ }
332
+ const filePath = (0, path_1.join)(this.folderPath, `${schema.uid}.json`);
333
+ (0, fs_1.writeFileSync)(filePath, JSON.stringify(schema));
334
+ cli_utilities_1.log.debug(`Wrote fixed schema: ${schema.uid} → ${filePath}`, this.config.auditContext);
335
+ }
330
336
  }
331
337
  else {
332
338
  cli_utilities_1.log.debug(`Skipping file write - user declined confirmation`, this.config.auditContext);
@@ -340,5 +340,5 @@
340
340
  ]
341
341
  }
342
342
  },
343
- "version": "2.0.0-beta.3"
343
+ "version": "2.0.0-beta.5"
344
344
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentstack/cli-audit",
3
- "version": "2.0.0-beta.3",
3
+ "version": "2.0.0-beta.5",
4
4
  "description": "Contentstack audit plugin",
5
5
  "author": "Contentstack CLI",
6
6
  "homepage": "https://github.com/contentstack/cli",
@@ -18,8 +18,8 @@
18
18
  "/oclif.manifest.json"
19
19
  ],
20
20
  "dependencies": {
21
- "@contentstack/cli-command": "~1.7.2",
22
- "@contentstack/cli-utilities": "~1.17.2",
21
+ "@contentstack/cli-command": "~2.0.0-beta",
22
+ "@contentstack/cli-utilities": "~2.0.0-beta.1",
23
23
  "@oclif/core": "^4.3.0",
24
24
  "@oclif/plugin-help": "^6.2.28",
25
25
  "chalk": "^4.1.2",