@cosmicdrift/kumiko-guards 0.330.2 → 0.331.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.
|
|
3
|
+
"version": "0.331.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": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"kumiko-pre-push": "./src/pre-push.sh"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@cosmicdrift/kumiko-repo-manifest": "0.
|
|
31
|
+
"@cosmicdrift/kumiko-repo-manifest": "0.331.0",
|
|
32
32
|
"ts-morph": "^28.0.0"
|
|
33
33
|
},
|
|
34
34
|
"publishConfig": {
|
package/src/changes.json
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.331.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "Primitives-discipline guard flags hand-built mx-auto + max-w-* screen containers",
|
|
6
|
+
"detail": "A className combining `mx-auto` with a `max-w-*` token now fails the guard and points to `<PageSection maxWidth>` (3xl/4xl/full); files under a `public/` segment are exempt because PublicShell is their container. Web code under a `client/` path segment (enterprise ai-foundation) is now scanned too.",
|
|
7
|
+
"migration": "Replace the hand-rolled container with `<PageSection maxWidth=\"3xl|4xl|full\">`; a deliberate exception (e.g. a narrow centered card) carries `// kumiko-lint-ignore primitives-discipline <reason>`. Web code under `client/` folders is now scanned as well."
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"version": "0.327.0",
|
|
4
11
|
"type": "improvement",
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
* <dialog> → <DefaultDialog>
|
|
23
23
|
* alert() / window.alert() → <DefaultDialog> from @cosmicdrift/kumiko-renderer-web
|
|
24
24
|
* className "bg-card" (hand-rolled card) → <Card> (slots/options)
|
|
25
|
+
* className "mx-auto" + "max-w-*" (hand-rolled screen container)
|
|
26
|
+
* → <PageSection maxWidth> (3xl/4xl/full)
|
|
25
27
|
*
|
|
26
28
|
* Allowed: containers and text — `<div>`, `<span>`, `<section>`,
|
|
27
29
|
* `<header>`, `<main>`, `<nav>`, `<aside>`, `<article>`,
|
|
@@ -73,6 +75,16 @@ const FORBIDDEN_CLASSES: ReadonlyArray<{
|
|
|
73
75
|
readonly counterpart: string;
|
|
74
76
|
}> = [{ token: "bg-card", counterpart: "<Card>" }];
|
|
75
77
|
|
|
78
|
+
// Hand-rolled screen container: `mx-auto` and a `max-w-*` token in the same
|
|
79
|
+
// line. Screens get their width from `<PageSection maxWidth>`; a private
|
|
80
|
+
// container drifts from the shell (an ai-models screen rendered 896px wide).
|
|
81
|
+
const SCREEN_CONTAINER_LABEL = "mx-auto + max-w-*";
|
|
82
|
+
const SCREEN_CONTAINER_COUNTERPART = "<PageSection maxWidth> (3xl/4xl/full)";
|
|
83
|
+
const MX_AUTO_PATTERN = /(?<![\w-])mx-auto(?![\w-])/u;
|
|
84
|
+
// Arbitrary values (`max-w-[1100px]`) contain `[`/`]`/`.`, so the token body
|
|
85
|
+
// is any non-whitespace/non-quote run.
|
|
86
|
+
const MAX_W_PATTERN = /(?<![\w-])max-w-[^\s"'`}]+/u;
|
|
87
|
+
|
|
76
88
|
const IGNORE_TAG = "kumiko-lint-ignore primitives-discipline";
|
|
77
89
|
|
|
78
90
|
export type ScopeKind = "bundled-features" | "samples" | "app";
|
|
@@ -127,13 +139,14 @@ type Violation = {
|
|
|
127
139
|
};
|
|
128
140
|
|
|
129
141
|
function isWebFile(absPath: string, scopeRoot: string): boolean {
|
|
130
|
-
// Only .tsx files under a `web/` or `public/` path segment
|
|
142
|
+
// Only .tsx files under a `web/`, `client/` or `public/` path segment
|
|
143
|
+
// (`client` = enterprise ai-foundation keeps its web code there; prevents
|
|
131
144
|
// scanning random .tsx in screens/ or feature roots — those are schema
|
|
132
145
|
// definitions, not web code).
|
|
133
146
|
if (!absPath.endsWith(".tsx")) return false;
|
|
134
147
|
const rel = path.relative(scopeRoot, absPath);
|
|
135
148
|
const parts = rel.split(path.sep);
|
|
136
|
-
return parts.includes("web") || parts.includes("public");
|
|
149
|
+
return parts.includes("web") || parts.includes("client") || parts.includes("public");
|
|
137
150
|
}
|
|
138
151
|
|
|
139
152
|
function walk(dir: string, out: string[]): void {
|
|
@@ -178,6 +191,12 @@ const CLASS_PATTERNS = FORBIDDEN_CLASSES.map((c) => ({
|
|
|
178
191
|
pattern: new RegExp(`(?<![\\w-])${c.token}(?![\\w-])`, "u"),
|
|
179
192
|
}));
|
|
180
193
|
|
|
194
|
+
function isPublicPage(file: string): boolean {
|
|
195
|
+
// Public pages live outside the app shell; PublicShell is their container,
|
|
196
|
+
// so a centered max-width there is not a screen container.
|
|
197
|
+
return file.split(path.sep).includes("public");
|
|
198
|
+
}
|
|
199
|
+
|
|
181
200
|
function hasIgnore(currentLine: string, prevLine: string): boolean {
|
|
182
201
|
return currentLine.includes(IGNORE_TAG) || prevLine.includes(IGNORE_TAG);
|
|
183
202
|
}
|
|
@@ -190,6 +209,7 @@ export function checkFile(
|
|
|
190
209
|
const text = fs.readFileSync(file, "utf-8");
|
|
191
210
|
const lines = text.split("\n");
|
|
192
211
|
const violations: Violation[] = [];
|
|
212
|
+
const publicPage = isPublicPage(path.relative(root, file));
|
|
193
213
|
let inBlockComment = false;
|
|
194
214
|
for (let i = 0; i < lines.length; i++) {
|
|
195
215
|
const line = lines[i] ?? "";
|
|
@@ -252,6 +272,17 @@ export function checkFile(
|
|
|
252
272
|
});
|
|
253
273
|
}
|
|
254
274
|
}
|
|
275
|
+
if (!publicPage && MX_AUTO_PATTERN.test(codeOnly) && MAX_W_PATTERN.test(codeOnly)) {
|
|
276
|
+
violations.push({
|
|
277
|
+
file: path.relative(root, file),
|
|
278
|
+
line: i + 1,
|
|
279
|
+
kind: "class",
|
|
280
|
+
tag: SCREEN_CONTAINER_LABEL,
|
|
281
|
+
counterpart: SCREEN_CONTAINER_COUNTERPART,
|
|
282
|
+
excerpt: trimmed.length > 120 ? `${trimmed.slice(0, 117)}...` : trimmed,
|
|
283
|
+
scope,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
255
286
|
}
|
|
256
287
|
return violations;
|
|
257
288
|
}
|