@freegamestore/compliance 0.1.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.
Files changed (94) hide show
  1. package/README.md +27 -0
  2. package/dist/checks/brand-fonts.d.ts +10 -0
  3. package/dist/checks/brand-fonts.d.ts.map +1 -0
  4. package/dist/checks/brand-fonts.js +47 -0
  5. package/dist/checks/brand-fonts.js.map +1 -0
  6. package/dist/checks/brand-tokens.d.ts +22 -0
  7. package/dist/checks/brand-tokens.d.ts.map +1 -0
  8. package/dist/checks/brand-tokens.js +53 -0
  9. package/dist/checks/brand-tokens.js.map +1 -0
  10. package/dist/checks/bundle-size.d.ts +14 -0
  11. package/dist/checks/bundle-size.d.ts.map +1 -0
  12. package/dist/checks/bundle-size.js +84 -0
  13. package/dist/checks/bundle-size.js.map +1 -0
  14. package/dist/checks/claude-md-slim.d.ts +4 -0
  15. package/dist/checks/claude-md-slim.d.ts.map +1 -0
  16. package/dist/checks/claude-md-slim.js +88 -0
  17. package/dist/checks/claude-md-slim.js.map +1 -0
  18. package/dist/checks/dark-mode.d.ts +20 -0
  19. package/dist/checks/dark-mode.d.ts.map +1 -0
  20. package/dist/checks/dark-mode.js +48 -0
  21. package/dist/checks/dark-mode.js.map +1 -0
  22. package/dist/checks/html-meta.d.ts +16 -0
  23. package/dist/checks/html-meta.d.ts.map +1 -0
  24. package/dist/checks/html-meta.js +46 -0
  25. package/dist/checks/html-meta.js.map +1 -0
  26. package/dist/checks/license-mit.d.ts +13 -0
  27. package/dist/checks/license-mit.d.ts.map +1 -0
  28. package/dist/checks/license-mit.js +38 -0
  29. package/dist/checks/license-mit.js.map +1 -0
  30. package/dist/checks/manifest.d.ts +9 -0
  31. package/dist/checks/manifest.d.ts.map +1 -0
  32. package/dist/checks/manifest.js +43 -0
  33. package/dist/checks/manifest.js.map +1 -0
  34. package/dist/checks/no-brand-overrides.d.ts +25 -0
  35. package/dist/checks/no-brand-overrides.d.ts.map +1 -0
  36. package/dist/checks/no-brand-overrides.js +159 -0
  37. package/dist/checks/no-brand-overrides.js.map +1 -0
  38. package/dist/checks/no-env-production.d.ts +16 -0
  39. package/dist/checks/no-env-production.d.ts.map +1 -0
  40. package/dist/checks/no-env-production.js +33 -0
  41. package/dist/checks/no-env-production.js.map +1 -0
  42. package/dist/checks/no-placeholders.d.ts +10 -0
  43. package/dist/checks/no-placeholders.d.ts.map +1 -0
  44. package/dist/checks/no-placeholders.js +41 -0
  45. package/dist/checks/no-placeholders.js.map +1 -0
  46. package/dist/checks/no-scroll.d.ts +25 -0
  47. package/dist/checks/no-scroll.d.ts.map +1 -0
  48. package/dist/checks/no-scroll.js +111 -0
  49. package/dist/checks/no-scroll.js.map +1 -0
  50. package/dist/checks/no-tracking.d.ts +4 -0
  51. package/dist/checks/no-tracking.d.ts.map +1 -0
  52. package/dist/checks/no-tracking.js +52 -0
  53. package/dist/checks/no-tracking.js.map +1 -0
  54. package/dist/checks/pwa-meta.d.ts +19 -0
  55. package/dist/checks/pwa-meta.d.ts.map +1 -0
  56. package/dist/checks/pwa-meta.js +38 -0
  57. package/dist/checks/pwa-meta.js.map +1 -0
  58. package/dist/checks/store-link.d.ts +8 -0
  59. package/dist/checks/store-link.d.ts.map +1 -0
  60. package/dist/checks/store-link.js +25 -0
  61. package/dist/checks/store-link.js.map +1 -0
  62. package/dist/checks/unsafe-vh.d.ts +25 -0
  63. package/dist/checks/unsafe-vh.d.ts.map +1 -0
  64. package/dist/checks/unsafe-vh.js +108 -0
  65. package/dist/checks/unsafe-vh.js.map +1 -0
  66. package/dist/checks/viewport-support.d.ts +24 -0
  67. package/dist/checks/viewport-support.d.ts.map +1 -0
  68. package/dist/checks/viewport-support.js +92 -0
  69. package/dist/checks/viewport-support.js.map +1 -0
  70. package/dist/index.d.ts +38 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +62 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/lib/file-source.d.ts +52 -0
  75. package/dist/lib/file-source.d.ts.map +1 -0
  76. package/dist/lib/file-source.js +148 -0
  77. package/dist/lib/file-source.js.map +1 -0
  78. package/dist/lib/gzip.d.ts +8 -0
  79. package/dist/lib/gzip.d.ts.map +1 -0
  80. package/dist/lib/gzip.js +26 -0
  81. package/dist/lib/gzip.js.map +1 -0
  82. package/dist/lib/project-type.d.ts +11 -0
  83. package/dist/lib/project-type.d.ts.map +1 -0
  84. package/dist/lib/project-type.js +27 -0
  85. package/dist/lib/project-type.js.map +1 -0
  86. package/dist/live/index.d.ts +50 -0
  87. package/dist/live/index.d.ts.map +1 -0
  88. package/dist/live/index.js +313 -0
  89. package/dist/live/index.js.map +1 -0
  90. package/dist/types.d.ts +12 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +2 -0
  93. package/dist/types.js.map +1 -0
  94. package/package.json +47 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Filesystem-agnostic file access for compliance checks.
3
+ *
4
+ * Why: the CLI scans a real directory on disk; the VibeCode agent runs
5
+ * in a Cloudflare Worker (no node:fs) and holds files in a Map. Both
6
+ * call the same checks via this interface so rules stay in one place.
7
+ *
8
+ * Conventions:
9
+ * - Paths are POSIX-style and relative to the repo root
10
+ * (e.g. `web/src/App.tsx`).
11
+ * - `read` returns null for missing files — checks decide whether
12
+ * missing == fail or missing == not-applicable.
13
+ * - `list()` already excludes noise dirs (.git, node_modules, dist,
14
+ * etc.). Implementations are responsible for that filtering.
15
+ */
16
+ import { readdir, readFile } from 'node:fs/promises';
17
+ import { join, relative, sep } from 'node:path';
18
+ const SKIP_DIRS = new Set(['.git', 'node_modules', 'dist', '.next', '.cache', '.wrangler', '.turbo']);
19
+ /**
20
+ * Filesystem-backed FileSource — used by the CLI and CI. Walks the
21
+ * directory tree once on first list() iteration; uses node:fs for
22
+ * read/readBytes/listDir.
23
+ *
24
+ * Paths containing `..` segments are rejected — every read returns
25
+ * null. Defense in depth: today's checks only pass paths that came
26
+ * from `list()` (which stays inside the root), but a future buggy or
27
+ * malicious check could try `source.read('../etc/passwd')`. We refuse
28
+ * rather than letting `path.join` resolve outside repoDir.
29
+ */
30
+ export function fsFileSource(repoDir) {
31
+ return {
32
+ async *list() {
33
+ yield* walk(repoDir, repoDir);
34
+ },
35
+ async read(path) {
36
+ if (hasTraversal(path))
37
+ return null;
38
+ try {
39
+ return await readFile(join(repoDir, path), 'utf8');
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ },
45
+ async readBytes(path) {
46
+ if (hasTraversal(path))
47
+ return null;
48
+ try {
49
+ const buf = await readFile(join(repoDir, path));
50
+ return new Uint8Array(buf);
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ },
56
+ async listDir(dir) {
57
+ if (hasTraversal(dir))
58
+ return null;
59
+ try {
60
+ return await readdir(join(repoDir, dir));
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ },
66
+ };
67
+ }
68
+ /** True if `path` contains a `..` segment or is absolute. */
69
+ function hasTraversal(path) {
70
+ if (path.startsWith('/') || /^[a-zA-Z]:/.test(path))
71
+ return true; // absolute
72
+ return path.split(/[/\\]/).some((seg) => seg === '..');
73
+ }
74
+ async function* walk(dir, root) {
75
+ let entries;
76
+ try {
77
+ entries = await readdir(dir, { withFileTypes: true });
78
+ }
79
+ catch {
80
+ return;
81
+ }
82
+ for (const entry of entries) {
83
+ const full = join(dir, entry.name);
84
+ if (entry.isDirectory()) {
85
+ if (SKIP_DIRS.has(entry.name))
86
+ continue;
87
+ yield* walk(full, root);
88
+ }
89
+ else if (entry.isFile()) {
90
+ // Always emit POSIX-style paths so checks can rely on consistent
91
+ // separators across platforms.
92
+ yield relative(root, full).split(sep).join('/');
93
+ }
94
+ }
95
+ }
96
+ /**
97
+ * Map-backed FileSource — used by the VibeCode agent (Workers env).
98
+ * The agent's session DO holds the virtual filesystem as a Map; this
99
+ * adapter exposes it through the same interface as the on-disk CLI.
100
+ *
101
+ * `readBytes` decodes the stored UTF-8 string back to bytes — fine for
102
+ * text-only checks. The agent has no built artefacts (it runs pre-
103
+ * deploy), so bundle-size will return its "not built yet" warn anyway.
104
+ *
105
+ * `listDir` synthesises directory listings from the Map keys.
106
+ */
107
+ export function mapFileSource(files) {
108
+ return {
109
+ async *list() {
110
+ for (const path of files.keys()) {
111
+ if (isSkippedPath(path))
112
+ continue;
113
+ yield path;
114
+ }
115
+ },
116
+ async read(path) {
117
+ return files.get(path) ?? null;
118
+ },
119
+ async readBytes(path) {
120
+ const text = files.get(path);
121
+ if (text === undefined)
122
+ return null;
123
+ return new TextEncoder().encode(text);
124
+ },
125
+ async listDir(dir) {
126
+ const prefix = dir.endsWith('/') ? dir : `${dir}/`;
127
+ const children = new Set();
128
+ let saw = false;
129
+ for (const path of files.keys()) {
130
+ if (!path.startsWith(prefix))
131
+ continue;
132
+ saw = true;
133
+ const rest = path.slice(prefix.length);
134
+ const slash = rest.indexOf('/');
135
+ children.add(slash === -1 ? rest : rest.slice(0, slash));
136
+ }
137
+ return saw ? [...children] : null;
138
+ },
139
+ };
140
+ }
141
+ function isSkippedPath(path) {
142
+ for (const seg of path.split('/')) {
143
+ if (SKIP_DIRS.has(seg))
144
+ return true;
145
+ }
146
+ return false;
147
+ }
148
+ //# sourceMappingURL=file-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-source.js","sourceRoot":"","sources":["../../src/lib/file-source.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Gzip via the Web Streams API. Available in Node 18+ and Cloudflare
3
+ * Workers, so the same helper works in both the CLI's filesystem
4
+ * checks and the audit Worker's live checks. Avoids divergence
5
+ * between source-side and live measurements of bundle size.
6
+ */
7
+ export declare function gzipByteLength(input: Uint8Array): Promise<number>;
8
+ //# sourceMappingURL=gzip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gzip.d.ts","sourceRoot":"","sources":["../../src/lib/gzip.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBvE"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Gzip via the Web Streams API. Available in Node 18+ and Cloudflare
3
+ * Workers, so the same helper works in both the CLI's filesystem
4
+ * checks and the audit Worker's live checks. Avoids divergence
5
+ * between source-side and live measurements of bundle size.
6
+ */
7
+ export async function gzipByteLength(input) {
8
+ const readable = new ReadableStream({
9
+ start(controller) {
10
+ controller.enqueue(input);
11
+ controller.close();
12
+ },
13
+ });
14
+ const compressed = readable.pipeThrough(new CompressionStream('gzip'));
15
+ const reader = compressed.getReader();
16
+ let total = 0;
17
+ for (;;) {
18
+ const { value, done } = await reader.read();
19
+ if (done)
20
+ break;
21
+ if (value)
22
+ total += value.byteLength;
23
+ }
24
+ return total;
25
+ }
26
+ //# sourceMappingURL=gzip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gzip.js","sourceRoot":"","sources":["../../src/lib/gzip.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAiB;IACpD,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAa;QAC9C,KAAK,CAAC,UAAU;YACd,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC1B,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KACF,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC;IACtC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,SAAS,CAAC;QACR,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI;YAAE,MAAM;QAChB,IAAI,KAAK;YAAE,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC;IACvC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { FileSource } from './file-source.js';
2
+ /**
3
+ * "Is this a game project?" — all projects in this repo are games,
4
+ * but the check remains for compatibility with shared check logic.
5
+ *
6
+ * Detection signals (any one is sufficient):
7
+ * - `@freegamestore/games` listed in any package.json
8
+ * - A TS/JS source file imports from `@freegamestore/games`
9
+ */
10
+ export declare function isGameProject(source: FileSource): Promise<boolean>;
11
+ //# sourceMappingURL=project-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-type.d.ts","sourceRoot":"","sources":["../../src/lib/project-type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAaxE"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * "Is this a game project?" — all projects in this repo are games,
3
+ * but the check remains for compatibility with shared check logic.
4
+ *
5
+ * Detection signals (any one is sufficient):
6
+ * - `@freegamestore/games` listed in any package.json
7
+ * - A TS/JS source file imports from `@freegamestore/games`
8
+ */
9
+ export async function isGameProject(source) {
10
+ for await (const path of source.list()) {
11
+ const base = path.split('/').pop() ?? '';
12
+ if (base !== 'package.json')
13
+ continue;
14
+ const content = await source.read(path);
15
+ if (content && /@freegamestore\/games/.test(content))
16
+ return true;
17
+ }
18
+ for await (const path of source.list()) {
19
+ if (!path.endsWith('.ts') && !path.endsWith('.tsx'))
20
+ continue;
21
+ const content = await source.read(path);
22
+ if (content && /from\s+['"]@freegamestore\/games['"]/.test(content))
23
+ return true;
24
+ }
25
+ return false;
26
+ }
27
+ //# sourceMappingURL=project-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-type.js","sourceRoot":"","sources":["../../src/lib/project-type.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAkB;IACpD,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QACzC,IAAI,IAAI,KAAK,cAAc;YAAE,SAAS;QACtC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,OAAO,IAAI,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;IACpE,CAAC;IACD,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,SAAS;QAC9D,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,OAAO,IAAI,sCAAsC,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;IACnF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Live-URL audit checks. Unlike the file-walking checks in ../checks/,
3
+ * these run inside a Cloudflare Worker (no filesystem) against published
4
+ * apps' live URLs to catch post-publish drift.
5
+ *
6
+ * What we can check from a live URL:
7
+ * - Tracking SDK injection (script src, inline scripts, fetched JS)
8
+ * - Brand fonts presence (link href to fonts.googleapis.com)
9
+ * - PWA manifest reachable + valid
10
+ * - Main bundle size (HEAD request)
11
+ *
12
+ * What we CAN'T check post-publish:
13
+ * - APPNAME placeholders (build erases these)
14
+ * - Brand-override CSS variables (sourcemaps not always shipped)
15
+ *
16
+ * The audit Worker runs these on a weekly cron and stores per-app
17
+ * results in D1 so creators (or platform admins) can see drift over
18
+ * time without leaving the storefront.
19
+ */
20
+ import type { CheckResult } from '../types.js';
21
+ export interface LiveAuditInput {
22
+ appId: string;
23
+ liveUrl: string;
24
+ }
25
+ export interface LiveAuditReport {
26
+ appId: string;
27
+ liveUrl: string;
28
+ checkedAt: number;
29
+ reachable: boolean;
30
+ results: CheckResult[];
31
+ }
32
+ export declare function auditLive(input: LiveAuditInput): Promise<LiveAuditReport>;
33
+ export declare function checkNoTrackingLive(html: string): CheckResult;
34
+ export declare function checkBrandFontsLive(html: string): CheckResult;
35
+ export declare function checkManifestLive(html: string, liveUrl: string): Promise<CheckResult>;
36
+ export declare function checkBundleSizeLive(html: string, liveUrl: string): Promise<CheckResult>;
37
+ /**
38
+ * Live counterpart to checkUnsafeVh. Fetches the first bundled
39
+ * stylesheet linked from the HTML and scans it for `100vh` (the
40
+ * unit, not the Tailwind class — by build time those have been
41
+ * compiled to CSS). Catches whiteboard-class iOS Safari URL-bar
42
+ * scroll bugs in apps that have already shipped.
43
+ *
44
+ * Cost: one extra subrequest per app. The audit Worker runs near the
45
+ * free-plan subrequest cap; on cap errors we degrade to `warn`
46
+ * (re-run needed) rather than `fail` (app broken) — the app isn't
47
+ * actually broken, the audit just couldn't check.
48
+ */
49
+ export declare function checkUnsafeVhLive(html: string, liveUrl: string): Promise<CheckResult>;
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/live/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAc/C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AA2BD,wBAAsB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CA2D/E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAa7D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAe7D;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8C3F;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAoD7F;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAgD3F"}
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Live-URL audit checks. Unlike the file-walking checks in ../checks/,
3
+ * these run inside a Cloudflare Worker (no filesystem) against published
4
+ * apps' live URLs to catch post-publish drift.
5
+ *
6
+ * What we can check from a live URL:
7
+ * - Tracking SDK injection (script src, inline scripts, fetched JS)
8
+ * - Brand fonts presence (link href to fonts.googleapis.com)
9
+ * - PWA manifest reachable + valid
10
+ * - Main bundle size (HEAD request)
11
+ *
12
+ * What we CAN'T check post-publish:
13
+ * - APPNAME placeholders (build erases these)
14
+ * - Brand-override CSS variables (sourcemaps not always shipped)
15
+ *
16
+ * The audit Worker runs these on a weekly cron and stores per-app
17
+ * results in D1 so creators (or platform admins) can see drift over
18
+ * time without leaving the storefront.
19
+ */
20
+ import { gzipByteLength } from '../lib/gzip.js';
21
+ const TRACKER_PATTERNS = [
22
+ /google-analytics\.com/i,
23
+ /gtag\(/i,
24
+ /googletagmanager\.com/i,
25
+ /\bamplitude\b/i,
26
+ /\bmixpanel\b/i,
27
+ /\bsegment\.com\b/i,
28
+ /\bhotjar\.com\b/i,
29
+ /plausible\.io/i,
30
+ /posthog\.com/i,
31
+ ];
32
+ /**
33
+ * Fetch with a timeout — Workers' default fetch waits 30s+, too slow
34
+ * when iterating 50+ apps in one cron tick.
35
+ */
36
+ async function fetchWithTimeout(url, ms = 8000) {
37
+ const ctrl = new AbortController();
38
+ const timer = setTimeout(() => ctrl.abort(), ms);
39
+ try {
40
+ return await fetch(url, { signal: ctrl.signal, redirect: 'follow' });
41
+ }
42
+ finally {
43
+ clearTimeout(timer);
44
+ }
45
+ }
46
+ /**
47
+ * Worker subrequest cap errors look like "Too many subrequests by
48
+ * single Worker invocation." We don't want to record those as "fail"
49
+ * verdicts because the app isn't actually broken — the audit just
50
+ * couldn't check. Detect and flag for the caller.
51
+ */
52
+ function isSubrequestCapError(err) {
53
+ const msg = err instanceof Error ? err.message : String(err);
54
+ return /too many subrequests|subrequest.*limit/i.test(msg);
55
+ }
56
+ export async function auditLive(input) {
57
+ const checkedAt = Date.now();
58
+ const report = {
59
+ appId: input.appId,
60
+ liveUrl: input.liveUrl,
61
+ checkedAt,
62
+ reachable: false,
63
+ results: [],
64
+ };
65
+ let html;
66
+ try {
67
+ const res = await fetchWithTimeout(input.liveUrl);
68
+ if (!res.ok) {
69
+ report.results.push({
70
+ name: 'Reachable',
71
+ status: 'fail',
72
+ detail: `HTTP ${res.status} from ${input.liveUrl}`,
73
+ });
74
+ return report;
75
+ }
76
+ html = await res.text();
77
+ report.reachable = true;
78
+ // Always record a Reachable row on success — otherwise stale
79
+ // "Reachable: fail" rows from previous failed runs (e.g.
80
+ // subrequest-cap noise) never get overwritten, because the
81
+ // upsert is keyed on (app_id, check_name).
82
+ report.results.push({
83
+ name: 'Reachable',
84
+ status: 'pass',
85
+ detail: `${input.liveUrl} → HTTP ${res.status}`,
86
+ });
87
+ }
88
+ catch (err) {
89
+ // Subrequest cap means we couldn't even fetch — that's a "warn"
90
+ // (re-check needed), not a "fail" (app broken). Distinguishing
91
+ // matters: failures show red badges in the storefront.
92
+ const skipped = isSubrequestCapError(err);
93
+ report.results.push({
94
+ name: 'Reachable',
95
+ status: skipped ? 'warn' : 'fail',
96
+ detail: skipped
97
+ ? 'audit Worker hit subrequest cap — re-run needed'
98
+ : err instanceof Error
99
+ ? err.message
100
+ : 'fetch failed',
101
+ });
102
+ return report;
103
+ }
104
+ // Run all checks in parallel — each is independent.
105
+ const [tracking, fonts, manifest, bundle, vh] = await Promise.all([
106
+ checkNoTrackingLive(html),
107
+ checkBrandFontsLive(html),
108
+ checkManifestLive(html, input.liveUrl),
109
+ checkBundleSizeLive(html, input.liveUrl),
110
+ checkUnsafeVhLive(html, input.liveUrl),
111
+ ]);
112
+ report.results.push(tracking, fonts, manifest, bundle, vh);
113
+ return report;
114
+ }
115
+ export function checkNoTrackingLive(html) {
116
+ const found = TRACKER_PATTERNS.filter((re) => re.test(html));
117
+ if (found.length === 0) {
118
+ return { name: 'No tracking SDKs (live)', status: 'pass', detail: 'no known trackers in HTML' };
119
+ }
120
+ return {
121
+ name: 'No tracking SDKs (live)',
122
+ status: 'fail',
123
+ detail: `${found.length} pattern${found.length === 1 ? '' : 's'} matched`,
124
+ suggestions: [
125
+ 'Tracking was injected after publish — remove the script tag or third-party loader.',
126
+ ],
127
+ };
128
+ }
129
+ export function checkBrandFontsLive(html) {
130
+ const hasManrope = /manrope/i.test(html);
131
+ const hasFraunces = /fraunces/i.test(html);
132
+ if (hasManrope && hasFraunces) {
133
+ return { name: 'Brand fonts (live)', status: 'pass', detail: 'Manrope + Fraunces referenced' };
134
+ }
135
+ const missing = [];
136
+ if (!hasManrope)
137
+ missing.push('Manrope');
138
+ if (!hasFraunces)
139
+ missing.push('Fraunces');
140
+ return {
141
+ name: 'Brand fonts (live)',
142
+ status: 'fail',
143
+ detail: `missing: ${missing.join(', ')}`,
144
+ suggestions: ['Restore the Google Fonts <link> in web/index.html.'],
145
+ };
146
+ }
147
+ export async function checkManifestLive(html, liveUrl) {
148
+ // Find the manifest link in the HTML, then fetch + validate it.
149
+ const m = /<link[^>]*rel=["']manifest["'][^>]*href=["']([^"']+)["']/i.exec(html);
150
+ if (!m) {
151
+ return {
152
+ name: 'PWA manifest (live)',
153
+ status: 'fail',
154
+ detail: 'no <link rel="manifest"> in HTML',
155
+ };
156
+ }
157
+ const href = m[1];
158
+ const manifestUrl = new URL(href, liveUrl).toString();
159
+ try {
160
+ const res = await fetchWithTimeout(manifestUrl);
161
+ if (!res.ok) {
162
+ return {
163
+ name: 'PWA manifest (live)',
164
+ status: 'fail',
165
+ detail: `${manifestUrl} → HTTP ${res.status}`,
166
+ };
167
+ }
168
+ const body = (await res.json());
169
+ const missing = [];
170
+ if (!body.name)
171
+ missing.push('name');
172
+ if (!body.display)
173
+ missing.push('display');
174
+ if (!body.start_url)
175
+ missing.push('start_url');
176
+ if (missing.length > 0) {
177
+ return {
178
+ name: 'PWA manifest (live)',
179
+ status: 'fail',
180
+ detail: `manifest missing fields: ${missing.join(', ')}`,
181
+ };
182
+ }
183
+ return { name: 'PWA manifest (live)', status: 'pass', detail: manifestUrl };
184
+ }
185
+ catch (err) {
186
+ const skipped = isSubrequestCapError(err);
187
+ return {
188
+ name: 'PWA manifest (live)',
189
+ status: skipped ? 'warn' : 'fail',
190
+ detail: skipped
191
+ ? 'audit Worker hit subrequest cap — re-run needed'
192
+ : err instanceof Error
193
+ ? err.message
194
+ : 'manifest fetch/parse failed',
195
+ };
196
+ }
197
+ }
198
+ export async function checkBundleSizeLive(html, liveUrl) {
199
+ // Find the largest-looking bundled JS reference. Vite output looks like
200
+ // src="/assets/index-XYZ.js" — starts with `/`, no chars before. Use
201
+ // `[^"']*` (zero or more) instead of `[^"']+` (one or more) so the
202
+ // root-relative path matches; with `+` every Vite app was misreported
203
+ // as "non-Vite layout" and the audit silently skipped bundle-size.
204
+ const m = /<script[^>]+src=["']([^"']*\/assets\/[^"']+\.js)["']/i.exec(html);
205
+ if (!m) {
206
+ // Plausible miss for non-Vite apps — not a fail, just skip.
207
+ return {
208
+ name: 'Bundle size (live)',
209
+ status: 'warn',
210
+ detail: 'no /assets/*.js found — non-Vite layout?',
211
+ };
212
+ }
213
+ const jsUrl = new URL(m[1], liveUrl).toString();
214
+ try {
215
+ const res = await fetchWithTimeout(jsUrl);
216
+ if (!res.ok) {
217
+ return { name: 'Bundle size (live)', status: 'fail', detail: `${jsUrl} → ${res.status}` };
218
+ }
219
+ const body = await res.arrayBuffer();
220
+ // Real gzip via the shared helper — same math the source-side
221
+ // check uses, so live and source verdicts can never disagree on
222
+ // a borderline app.
223
+ const rawKb = Math.round(body.byteLength / 1024);
224
+ const gzippedBytes = await gzipByteLength(new Uint8Array(body));
225
+ const gzipKb = Math.round(gzippedBytes / 1024);
226
+ if (gzipKb > 300) {
227
+ return {
228
+ name: 'Bundle size (live)',
229
+ status: 'fail',
230
+ detail: `${gzipKb} KB gzipped (raw ${rawKb} KB) — over 300 KB limit`,
231
+ };
232
+ }
233
+ return {
234
+ name: 'Bundle size (live)',
235
+ status: 'pass',
236
+ detail: `${gzipKb} KB gzipped (raw ${rawKb} KB)`,
237
+ };
238
+ }
239
+ catch (err) {
240
+ const skipped = isSubrequestCapError(err);
241
+ return {
242
+ name: 'Bundle size (live)',
243
+ status: 'warn',
244
+ detail: skipped
245
+ ? 'audit Worker hit subrequest cap — re-run needed'
246
+ : err instanceof Error
247
+ ? err.message
248
+ : 'bundle fetch failed',
249
+ };
250
+ }
251
+ }
252
+ /**
253
+ * Live counterpart to checkUnsafeVh. Fetches the first bundled
254
+ * stylesheet linked from the HTML and scans it for `100vh` (the
255
+ * unit, not the Tailwind class — by build time those have been
256
+ * compiled to CSS). Catches whiteboard-class iOS Safari URL-bar
257
+ * scroll bugs in apps that have already shipped.
258
+ *
259
+ * Cost: one extra subrequest per app. The audit Worker runs near the
260
+ * free-plan subrequest cap; on cap errors we degrade to `warn`
261
+ * (re-run needed) rather than `fail` (app broken) — the app isn't
262
+ * actually broken, the audit just couldn't check.
263
+ */
264
+ export async function checkUnsafeVhLive(html, liveUrl) {
265
+ // Find the first bundled stylesheet. Vite output looks like
266
+ // <link rel="stylesheet" href="/assets/index-XYZ.css">. Be
267
+ // tolerant of attribute order — rel and href can appear either way.
268
+ const m = /<link[^>]+rel=["']stylesheet["'][^>]*href=["']([^"']+\.css[^"']*)["']/i.exec(html) ??
269
+ /<link[^>]+href=["']([^"']+\.css[^"']*)["'][^>]*rel=["']stylesheet["']/i.exec(html);
270
+ if (!m) {
271
+ // No stylesheet link — could be inline-only styles, or styles
272
+ // injected at runtime. Not a fail; just nothing to check.
273
+ return {
274
+ name: 'No unsafe 100vh (live)',
275
+ status: 'warn',
276
+ detail: 'no <link rel="stylesheet"> found in HTML — skipped',
277
+ };
278
+ }
279
+ const cssUrl = new URL(m[1], liveUrl).toString();
280
+ try {
281
+ const res = await fetchWithTimeout(cssUrl);
282
+ if (!res.ok) {
283
+ return { name: 'No unsafe 100vh (live)', status: 'warn', detail: `${cssUrl} → HTTP ${res.status}` };
284
+ }
285
+ const css = await res.text();
286
+ const matches = css.match(/\b100vh\b/g) ?? [];
287
+ if (matches.length === 0) {
288
+ return { name: 'No unsafe 100vh (live)', status: 'pass', detail: `scanned ${cssUrl}` };
289
+ }
290
+ return {
291
+ name: 'No unsafe 100vh (live)',
292
+ status: 'warn',
293
+ detail: `${matches.length} occurrence(s) of 100vh in ${cssUrl}`,
294
+ suggestions: [
295
+ '100vh resolves to the layout viewport on iOS Safari — when the URL bar is visible the page scrolls.',
296
+ 'Replace with 100svh (small viewport — accounts for visible browser UI) or 100dvh (dynamic — recomputes as URL bar moves).',
297
+ ],
298
+ };
299
+ }
300
+ catch (err) {
301
+ const skipped = isSubrequestCapError(err);
302
+ return {
303
+ name: 'No unsafe 100vh (live)',
304
+ status: 'warn',
305
+ detail: skipped
306
+ ? 'audit Worker hit subrequest cap — re-run needed'
307
+ : err instanceof Error
308
+ ? err.message
309
+ : 'CSS fetch failed',
310
+ };
311
+ }
312
+ }
313
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/live/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,MAAM,gBAAgB,GAAG;IACvB,wBAAwB;IACxB,SAAS;IACT,wBAAwB;IACxB,gBAAgB;IAChB,eAAe;IACf,mBAAmB;IACnB,kBAAkB;IAClB,gBAAgB;IAChB,eAAe;CAChB,CAAC;AAeF;;;GAGG;AACH,KAAK,UAAU,gBAAgB,CAAC,GAAW,EAAE,EAAE,GAAG,IAAI;IACpD,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IACjD,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IACvE,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,GAAY;IACxC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO,yCAAyC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAqB;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAoB;QAC9B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS;QACT,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,EAAE;KACZ,CAAC;IAEF,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;gBAClB,IAAI,EAAE,WAAW;gBACjB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,QAAQ,GAAG,CAAC,MAAM,SAAS,KAAK,CAAC,OAAO,EAAE;aACnD,CAAC,CAAC;YACH,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,6DAA6D;QAC7D,yDAAyD;QACzD,2DAA2D;QAC3D,2CAA2C;QAC3C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,WAAW,GAAG,CAAC,MAAM,EAAE;SAChD,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,gEAAgE;QAChE,+DAA+D;QAC/D,uDAAuD;QACvD,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;YACjC,MAAM,EAAE,OAAO;gBACb,CAAC,CAAC,iDAAiD;gBACnD,CAAC,CAAC,GAAG,YAAY,KAAK;oBACpB,CAAC,CAAC,GAAG,CAAC,OAAO;oBACb,CAAC,CAAC,cAAc;SACrB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,oDAAoD;IACpD,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAChE,mBAAmB,CAAC,IAAI,CAAC;QACzB,mBAAmB,CAAC,IAAI,CAAC;QACzB,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;QACtC,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;QACxC,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;KACvC,CAAC,CAAC;IACH,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,yBAAyB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;IAClG,CAAC;IACD,OAAO;QACL,IAAI,EAAE,yBAAyB;QAC/B,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,UAAU;QACzE,WAAW,EAAE;YACX,oFAAoF;SACrF;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,UAAU,IAAI,WAAW,EAAE,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,+BAA+B,EAAE,CAAC;IACjG,CAAC;IACD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,WAAW;QAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3C,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACxC,WAAW,EAAE,CAAC,oDAAoD,CAAC;KACpE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAe;IACnE,gEAAgE;IAChE,MAAM,CAAC,GAAG,2DAA2D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjF,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,OAAO;YACL,IAAI,EAAE,qBAAqB;YAC3B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,kCAAkC;SAC3C,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACnB,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO;gBACL,IAAI,EAAE,qBAAqB;gBAC3B,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,GAAG,WAAW,WAAW,GAAG,CAAC,MAAM,EAAE;aAC9C,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4D,CAAC;QAC3F,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC/C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO;gBACL,IAAI,EAAE,qBAAqB;gBAC3B,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,4BAA4B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;aACzD,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC9E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC1C,OAAO;YACL,IAAI,EAAE,qBAAqB;YAC3B,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;YACjC,MAAM,EAAE,OAAO;gBACb,CAAC,CAAC,iDAAiD;gBACnD,CAAC,CAAC,GAAG,YAAY,KAAK;oBACpB,CAAC,CAAC,GAAG,CAAC,OAAO;oBACb,CAAC,CAAC,6BAA6B;SACpC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,OAAe;IACrE,wEAAwE;IACxE,qEAAqE;IACrE,mEAAmE;IACnE,sEAAsE;IACtE,mEAAmE;IACnE,MAAM,CAAC,GAAG,uDAAuD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7E,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,4DAA4D;QAC5D,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,0CAA0C;SACnD,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;QAC5F,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;QACrC,8DAA8D;QAC9D,gEAAgE;QAChE,oBAAoB;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;QACjD,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;QAC/C,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;YACjB,OAAO;gBACL,IAAI,EAAE,oBAAoB;gBAC1B,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,GAAG,MAAM,oBAAoB,KAAK,0BAA0B;aACrE,CAAC;QACJ,CAAC;QACD,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,MAAM,oBAAoB,KAAK,MAAM;SACjD,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC1C,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,OAAO;gBACb,CAAC,CAAC,iDAAiD;gBACnD,CAAC,CAAC,GAAG,YAAY,KAAK;oBACpB,CAAC,CAAC,GAAG,CAAC,OAAO;oBACb,CAAC,CAAC,qBAAqB;SAC5B,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAe;IACnE,4DAA4D;IAC5D,2DAA2D;IAC3D,oEAAoE;IACpE,MAAM,CAAC,GACL,wEAAwE,CAAC,IAAI,CAAC,IAAI,CAAC;QACnF,wEAAwE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtF,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,8DAA8D;QAC9D,0DAA0D;QAC1D,OAAO;YACL,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,oDAAoD;SAC7D,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IAClD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;QACtG,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,MAAM,EAAE,EAAE,CAAC;QACzF,CAAC;QACD,OAAO;YACL,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,8BAA8B,MAAM,EAAE;YAC/D,WAAW,EAAE;gBACX,qGAAqG;gBACrG,2HAA2H;aAC5H;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC1C,OAAO;YACL,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,OAAO;gBACb,CAAC,CAAC,iDAAiD;gBACnD,CAAC,CAAC,GAAG,YAAY,KAAK;oBACpB,CAAC,CAAC,GAAG,CAAC,OAAO;oBACb,CAAC,CAAC,kBAAkB;SACzB,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ export type CheckStatus = 'pass' | 'warn' | 'fail';
2
+ export interface CheckResult {
3
+ /** Short human-readable name. */
4
+ name: string;
5
+ /** pass | warn | fail. fail is a hard gate; warn is informational. */
6
+ status: CheckStatus;
7
+ /** One-line context (file path, count, current value). */
8
+ detail: string;
9
+ /** Optional actionable advice for fixing a fail/warn. */
10
+ suggestions?: string[];
11
+ }
12
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,MAAM,EAAE,WAAW,CAAC;IACpB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}