@cosmicdrift/kumiko-guards 0.399.0 → 0.400.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-guards",
3
- "version": "0.399.0",
3
+ "version": "0.400.0",
4
4
  "description": "AST-based security guards for Kumiko repos: direct-fs/fetch, tenant escalation, admin-API, escape hatches and related checks, run over a shared ts-morph project.",
5
5
  "license": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -36,7 +36,7 @@
36
36
  "kumiko-pre-push": "./src/pre-push.sh"
37
37
  },
38
38
  "dependencies": {
39
- "@cosmicdrift/kumiko-types": "0.399.0",
39
+ "@cosmicdrift/kumiko-types": "0.400.0",
40
40
  "ts-morph": "^28.0.0",
41
41
  "zod": "^4.6.7"
42
42
  },
@@ -52,7 +52,7 @@
52
52
  "LICENSE"
53
53
  ],
54
54
  "peerDependencies": {
55
- "@cosmicdrift/kumiko-framework": "0.399.0"
55
+ "@cosmicdrift/kumiko-framework": "0.400.0"
56
56
  },
57
57
  "peerDependenciesMeta": {
58
58
  "@cosmicdrift/kumiko-framework": {
@@ -60,6 +60,6 @@
60
60
  }
61
61
  },
62
62
  "devDependencies": {
63
- "@cosmicdrift/kumiko-framework": "0.399.0"
63
+ "@cosmicdrift/kumiko-framework": "0.400.0"
64
64
  }
65
65
  }
@@ -48,6 +48,14 @@ function stringLiteralValue(obj: ObjectLiteralExpression, name: string): string
48
48
  return init?.isKind(SyntaxKind.StringLiteral) ? init.getLiteralValue() : undefined;
49
49
  }
50
50
 
51
+ function booleanLiteralValue(obj: ObjectLiteralExpression, name: string): boolean | undefined {
52
+ const prop = obj.getProperty(name);
53
+ if (!prop?.isKind(SyntaxKind.PropertyAssignment)) return undefined;
54
+ const init = prop.getInitializer();
55
+ if (init?.isKind(SyntaxKind.TrueKeyword)) return true;
56
+ return init?.isKind(SyntaxKind.FalseKeyword) ? false : undefined;
57
+ }
58
+
51
59
  function hasDeclaredProperty(obj: ObjectLiteralExpression, name: string): boolean {
52
60
  const prop = obj.getProperty(name);
53
61
  if (!prop?.isKind(SyntaxKind.PropertyAssignment)) return false;
@@ -55,7 +63,7 @@ function hasDeclaredProperty(obj: ObjectLiteralExpression, name: string): boolea
55
63
  return init !== undefined && init.getKind() !== SyntaxKind.UndefinedKeyword;
56
64
  }
57
65
 
58
- // R2: a relatedList section with neither rowClick nor rowActions renders a
66
+ // R2: a relatedList section with neither rowClick, rowActions nor readOnly renders a
59
67
  // list nobody can act on.
60
68
  function checkRelatedListSections(
61
69
  root: ObjectLiteralExpression,
@@ -65,6 +73,7 @@ function checkRelatedListSections(
65
73
  for (const obj of root.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression)) {
66
74
  if (stringLiteralValue(obj, "kind") !== "relatedList") continue;
67
75
  if (hasIgnoreTag(obj, IGNORE_TAG)) continue;
76
+ if (booleanLiteralValue(obj, "readOnly") === true) continue;
68
77
  if (hasDeclaredProperty(obj, "rowClick") || hasDeclaredProperty(obj, "rowActions")) continue;
69
78
  violations.push({
70
79
  file: sf.getFilePath(),
@@ -107,7 +116,7 @@ export const guard: AstGuard = {
107
116
  name: "Screen-Conventions Guard",
108
117
  scan: SCAN,
109
118
  hint:
110
- "relatedList sections need `rowClick` or `rowActions`; a rowAction label " +
119
+ "relatedList sections need `rowClick`, `rowActions` or `readOnly: true`; a rowAction label " +
111
120
  'must not end in ".action.edit" ' +
112
121
  'when the `id` is not "edit". ' +
113
122
  `Justified exception: // ${IGNORE_TAG} <reason>`,