@freeappstore/compliance 0.2.0 → 0.5.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 (114) hide show
  1. package/dist/checks/brand-fonts.d.ts +2 -1
  2. package/dist/checks/brand-fonts.d.ts.map +1 -1
  3. package/dist/checks/brand-fonts.js +11 -7
  4. package/dist/checks/brand-fonts.js.map +1 -1
  5. package/dist/checks/brand-tokens.d.ts +22 -0
  6. package/dist/checks/brand-tokens.d.ts.map +1 -0
  7. package/dist/checks/brand-tokens.js +53 -0
  8. package/dist/checks/brand-tokens.js.map +1 -0
  9. package/dist/checks/bundle-size.d.ts +7 -1
  10. package/dist/checks/bundle-size.d.ts.map +1 -1
  11. package/dist/checks/bundle-size.js +35 -15
  12. package/dist/checks/bundle-size.js.map +1 -1
  13. package/dist/checks/dark-mode.d.ts +20 -0
  14. package/dist/checks/dark-mode.d.ts.map +1 -0
  15. package/dist/checks/dark-mode.js +48 -0
  16. package/dist/checks/dark-mode.js.map +1 -0
  17. package/dist/checks/html-meta.d.ts +16 -0
  18. package/dist/checks/html-meta.d.ts.map +1 -0
  19. package/dist/checks/html-meta.js +46 -0
  20. package/dist/checks/html-meta.js.map +1 -0
  21. package/dist/checks/license-mit.d.ts +13 -0
  22. package/dist/checks/license-mit.d.ts.map +1 -0
  23. package/dist/checks/license-mit.js +38 -0
  24. package/dist/checks/license-mit.js.map +1 -0
  25. package/dist/checks/manifest.d.ts +2 -1
  26. package/dist/checks/manifest.d.ts.map +1 -1
  27. package/dist/checks/manifest.js +7 -12
  28. package/dist/checks/manifest.js.map +1 -1
  29. package/dist/checks/no-brand-overrides.d.ts +2 -1
  30. package/dist/checks/no-brand-overrides.d.ts.map +1 -1
  31. package/dist/checks/no-brand-overrides.js +13 -18
  32. package/dist/checks/no-brand-overrides.js.map +1 -1
  33. package/dist/checks/no-env-production.d.ts +16 -0
  34. package/dist/checks/no-env-production.d.ts.map +1 -0
  35. package/dist/checks/no-env-production.js +33 -0
  36. package/dist/checks/no-env-production.js.map +1 -0
  37. package/dist/checks/no-placeholders.d.ts +2 -1
  38. package/dist/checks/no-placeholders.d.ts.map +1 -1
  39. package/dist/checks/no-placeholders.js +13 -24
  40. package/dist/checks/no-placeholders.js.map +1 -1
  41. package/dist/checks/no-scroll.d.ts +25 -0
  42. package/dist/checks/no-scroll.d.ts.map +1 -0
  43. package/dist/checks/no-scroll.js +111 -0
  44. package/dist/checks/no-scroll.js.map +1 -0
  45. package/dist/checks/no-tracking.d.ts +2 -1
  46. package/dist/checks/no-tracking.d.ts.map +1 -1
  47. package/dist/checks/no-tracking.js +12 -8
  48. package/dist/checks/no-tracking.js.map +1 -1
  49. package/dist/checks/pwa-meta.d.ts +19 -0
  50. package/dist/checks/pwa-meta.d.ts.map +1 -0
  51. package/dist/checks/pwa-meta.js +38 -0
  52. package/dist/checks/pwa-meta.js.map +1 -0
  53. package/dist/checks/store-link.d.ts +17 -0
  54. package/dist/checks/store-link.d.ts.map +1 -0
  55. package/dist/checks/store-link.js +36 -0
  56. package/dist/checks/store-link.js.map +1 -0
  57. package/dist/checks/unsafe-vh.d.ts +25 -0
  58. package/dist/checks/unsafe-vh.d.ts.map +1 -0
  59. package/dist/checks/unsafe-vh.js +108 -0
  60. package/dist/checks/unsafe-vh.js.map +1 -0
  61. package/dist/checks/viewport-support.d.ts +24 -0
  62. package/dist/checks/viewport-support.d.ts.map +1 -0
  63. package/dist/checks/viewport-support.js +92 -0
  64. package/dist/checks/viewport-support.js.map +1 -0
  65. package/dist/index.d.ts +29 -8
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +51 -14
  68. package/dist/index.js.map +1 -1
  69. package/dist/lib/file-source.d.ts +52 -0
  70. package/dist/lib/file-source.d.ts.map +1 -0
  71. package/dist/lib/file-source.js +148 -0
  72. package/dist/lib/file-source.js.map +1 -0
  73. package/dist/lib/gzip.d.ts +8 -0
  74. package/dist/lib/gzip.d.ts.map +1 -0
  75. package/dist/lib/gzip.js +26 -0
  76. package/dist/lib/gzip.js.map +1 -0
  77. package/dist/lib/project-type.d.ts +18 -0
  78. package/dist/lib/project-type.d.ts.map +1 -0
  79. package/dist/lib/project-type.js +34 -0
  80. package/dist/lib/project-type.js.map +1 -0
  81. package/dist/live/index.d.ts +50 -0
  82. package/dist/live/index.d.ts.map +1 -0
  83. package/dist/live/index.js +313 -0
  84. package/dist/live/index.js.map +1 -0
  85. package/package.json +1 -1
  86. package/LICENSE +0 -21
  87. package/dist/checks/brand-fonts.test.d.ts +0 -2
  88. package/dist/checks/brand-fonts.test.d.ts.map +0 -1
  89. package/dist/checks/brand-fonts.test.js +0 -45
  90. package/dist/checks/brand-fonts.test.js.map +0 -1
  91. package/dist/checks/bundle-size.test.d.ts +0 -2
  92. package/dist/checks/bundle-size.test.d.ts.map +0 -1
  93. package/dist/checks/bundle-size.test.js +0 -57
  94. package/dist/checks/bundle-size.test.js.map +0 -1
  95. package/dist/checks/manifest.test.d.ts +0 -2
  96. package/dist/checks/manifest.test.d.ts.map +0 -1
  97. package/dist/checks/manifest.test.js +0 -53
  98. package/dist/checks/manifest.test.js.map +0 -1
  99. package/dist/checks/no-brand-overrides.test.d.ts +0 -2
  100. package/dist/checks/no-brand-overrides.test.d.ts.map +0 -1
  101. package/dist/checks/no-brand-overrides.test.js +0 -102
  102. package/dist/checks/no-brand-overrides.test.js.map +0 -1
  103. package/dist/checks/no-placeholders.test.d.ts +0 -2
  104. package/dist/checks/no-placeholders.test.d.ts.map +0 -1
  105. package/dist/checks/no-placeholders.test.js +0 -46
  106. package/dist/checks/no-placeholders.test.js.map +0 -1
  107. package/dist/checks/no-tracking.test.d.ts +0 -2
  108. package/dist/checks/no-tracking.test.d.ts.map +0 -1
  109. package/dist/checks/no-tracking.test.js +0 -42
  110. package/dist/checks/no-tracking.test.js.map +0 -1
  111. package/dist/lib/walk.d.ts +0 -6
  112. package/dist/lib/walk.d.ts.map +0 -1
  113. package/dist/lib/walk.js +0 -35
  114. package/dist/lib/walk.js.map +0 -1
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Flag `100vh` (and Tailwind shortcuts that compile to it) in source.
3
+ *
4
+ * Why: on iOS Safari with the URL bar visible, `100vh` resolves to the
5
+ * *layout* viewport (e.g. 852px on iPhone 15) but the actual visible
6
+ * area is the *visual* viewport (~750px). An element sized at `100vh`
7
+ * is then ~100px taller than the screen, forcing the page to scroll on
8
+ * first load. This is invisible to headless Playwright (no dynamic URL
9
+ * bar) and to the static audit Worker (no rendering), so this fast
10
+ * static rule is the cheapest place to catch it.
11
+ *
12
+ * Fix: use `100svh` (small viewport height — accounts for visible
13
+ * browser UI) for "as tall as the screen" intent, or `100dvh` (dynamic
14
+ * viewport height — recomputes as the URL bar shows/hides) for full-
15
+ * height containers that should grow when chrome retracts.
16
+ *
17
+ * Scope: warn-by-default, not fail. There ARE legitimate uses of
18
+ * `100vh` (intentionally taking layout-viewport height for hero
19
+ * sections that should stay constant as URL bar moves). Creators can
20
+ * acknowledge by leaving an `// allow-100vh` comment on the same line.
21
+ */
22
+ export async function checkUnsafeVh(source) {
23
+ const issues = [];
24
+ for await (const path of source.list()) {
25
+ if (!SCAN_EXTS.has(extOf(path)))
26
+ continue;
27
+ const content = await source.read(path);
28
+ if (!content)
29
+ continue;
30
+ for (const { re, label } of PATTERNS) {
31
+ // Reset lastIndex defensively; we use `g` to find all matches.
32
+ re.lastIndex = 0;
33
+ let m;
34
+ while ((m = re.exec(content)) !== null) {
35
+ const line = lineNumberAt(content, m.index);
36
+ // Allow opt-out via an inline `allow-100vh` comment on the same line.
37
+ if (lineHasAllowComment(content, m.index))
38
+ continue;
39
+ issues.push(`${path}:${line} ${label} — use 100svh or 100dvh`);
40
+ }
41
+ }
42
+ }
43
+ if (issues.length === 0) {
44
+ return {
45
+ name: 'No unsafe 100vh',
46
+ status: 'pass',
47
+ detail: 'no 100vh / h-screen patterns found',
48
+ };
49
+ }
50
+ const head = issues.slice(0, MAX_REPORTED);
51
+ const tail = issues.length > MAX_REPORTED ? [`...and ${issues.length - MAX_REPORTED} more`] : [];
52
+ return {
53
+ name: 'No unsafe 100vh',
54
+ status: 'warn',
55
+ detail: `${issues.length} occurrence${issues.length === 1 ? '' : 's'} of 100vh / h-screen — breaks on iOS Safari with URL bar visible`,
56
+ suggestions: [
57
+ ...head,
58
+ ...tail,
59
+ 'Replace with 100svh (preferred) or 100dvh. Add `// allow-100vh` on the line if intentional.',
60
+ ],
61
+ };
62
+ }
63
+ const SCAN_EXTS = new Set(['.css', '.scss', '.tsx', '.ts', '.jsx', '.js', '.html']);
64
+ const MAX_REPORTED = 5;
65
+ /**
66
+ * Patterns to flag. Each is global (`g`) so we find every occurrence.
67
+ *
68
+ * Boundary handling:
69
+ * - `100vh` uses `\b` since CSS values are tokenised normally.
70
+ * - Tailwind classes use a custom hyphen-aware boundary
71
+ * `(?<![\w-])foo(?![\w-])` so `h-screen` doesn't match inside
72
+ * `min-h-screen` (and we don't double-count `min-h-screen` as both
73
+ * itself and its `h-screen` substring).
74
+ */
75
+ const PATTERNS = [
76
+ { re: /\b100vh\b/g, label: '100vh' },
77
+ // Tailwind: h-screen → height: 100vh; same for min-/max-.
78
+ { re: /(?<![\w-])h-screen(?![\w-])/g, label: 'h-screen (Tailwind: → 100vh)' },
79
+ { re: /(?<![\w-])min-h-screen(?![\w-])/g, label: 'min-h-screen (Tailwind: → min-height: 100vh)' },
80
+ { re: /(?<![\w-])max-h-screen(?![\w-])/g, label: 'max-h-screen (Tailwind: → max-height: 100vh)' },
81
+ ];
82
+ function extOf(path) {
83
+ const dot = path.lastIndexOf('.');
84
+ const slash = path.lastIndexOf('/');
85
+ return dot > slash ? path.slice(dot).toLowerCase() : '';
86
+ }
87
+ function lineNumberAt(content, index) {
88
+ let n = 1;
89
+ for (let i = 0; i < index; i++)
90
+ if (content.charCodeAt(i) === 10)
91
+ n++;
92
+ return n;
93
+ }
94
+ /**
95
+ * True if the rest of the line containing `index` includes the literal
96
+ * string "allow-100vh" (typically inside a comment). Cheap and cheerful
97
+ * — doesn't validate comment syntax, just trusts the marker.
98
+ */
99
+ function lineHasAllowComment(content, index) {
100
+ let end = content.indexOf('\n', index);
101
+ if (end === -1)
102
+ end = content.length;
103
+ let start = content.lastIndexOf('\n', index);
104
+ if (start === -1)
105
+ start = 0;
106
+ return content.slice(start, end).includes('allow-100vh');
107
+ }
108
+ //# sourceMappingURL=unsafe-vh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unsafe-vh.js","sourceRoot":"","sources":["../../src/checks/unsafe-vh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { FileSource } from '../lib/file-source.js';
2
+ import type { CheckResult } from '../types.js';
3
+ /**
4
+ * Apps and games must declare which screens and orientations they
5
+ * support, so the storefront can render a coverage badge ("works on
6
+ * 92% of devices · portrait + landscape") and visitors know what to
7
+ * expect before they tap.
8
+ *
9
+ * Two declarations are required, both in `web/public/manifest.json`:
10
+ *
11
+ * "orientation": "any" | "portrait" | "landscape" |
12
+ * "portrait-primary" | "landscape-primary"
13
+ * "min_viewport_width": 320 | 360 | 414 | 600 | 768 | 1024
14
+ *
15
+ * `orientation` is a standard PWA manifest field. `min_viewport_width`
16
+ * is a custom platform field — the smallest screen width (in CSS px)
17
+ * the app renders correctly at. The storefront maps that to a global
18
+ * device-share percentile to render coverage.
19
+ *
20
+ * If you genuinely don't care about orientation, set `"any"`. Don't
21
+ * leave it off — that's the only value that returns a fail.
22
+ */
23
+ export declare function checkViewportSupport(source: FileSource): Promise<CheckResult>;
24
+ //# sourceMappingURL=viewport-support.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport-support.d.ts","sourceRoot":"","sources":["../../src/checks/viewport-support.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAqEnF"}
@@ -0,0 +1,92 @@
1
+ const MANIFEST_PATH = 'web/public/manifest.json';
2
+ /**
3
+ * Apps and games must declare which screens and orientations they
4
+ * support, so the storefront can render a coverage badge ("works on
5
+ * 92% of devices · portrait + landscape") and visitors know what to
6
+ * expect before they tap.
7
+ *
8
+ * Two declarations are required, both in `web/public/manifest.json`:
9
+ *
10
+ * "orientation": "any" | "portrait" | "landscape" |
11
+ * "portrait-primary" | "landscape-primary"
12
+ * "min_viewport_width": 320 | 360 | 414 | 600 | 768 | 1024
13
+ *
14
+ * `orientation` is a standard PWA manifest field. `min_viewport_width`
15
+ * is a custom platform field — the smallest screen width (in CSS px)
16
+ * the app renders correctly at. The storefront maps that to a global
17
+ * device-share percentile to render coverage.
18
+ *
19
+ * If you genuinely don't care about orientation, set `"any"`. Don't
20
+ * leave it off — that's the only value that returns a fail.
21
+ */
22
+ export async function checkViewportSupport(source) {
23
+ const raw = await source.read(MANIFEST_PATH);
24
+ if (raw === null) {
25
+ return {
26
+ name: 'Viewport support',
27
+ status: 'fail',
28
+ detail: `${MANIFEST_PATH} not found`,
29
+ };
30
+ }
31
+ let manifest;
32
+ try {
33
+ manifest = JSON.parse(raw);
34
+ }
35
+ catch {
36
+ return {
37
+ name: 'Viewport support',
38
+ status: 'fail',
39
+ detail: `${MANIFEST_PATH} unparseable`,
40
+ };
41
+ }
42
+ const orientation = manifest['orientation'];
43
+ const minWidth = manifest['min_viewport_width'];
44
+ const issues = [];
45
+ const warnings = [];
46
+ if (typeof orientation !== 'string' || !VALID_ORIENTATIONS.has(orientation)) {
47
+ issues.push(`manifest.orientation is "${String(orientation)}" — must be one of: ${[...VALID_ORIENTATIONS].join(', ')}`);
48
+ }
49
+ if (typeof minWidth !== 'number') {
50
+ warnings.push('manifest.min_viewport_width is missing — assuming 320 (strictest). ' +
51
+ 'Set explicitly to e.g. 360 / 414 / 600 / 768 / 1024 to declare which devices the app supports.');
52
+ }
53
+ else if (minWidth < 320) {
54
+ issues.push(`manifest.min_viewport_width=${minWidth} is below the 320px minimum`);
55
+ }
56
+ else if (!RECOMMENDED_WIDTHS.includes(minWidth)) {
57
+ warnings.push(`manifest.min_viewport_width=${minWidth} is unusual — recommended values: ${RECOMMENDED_WIDTHS.join(', ')}`);
58
+ }
59
+ if (issues.length > 0) {
60
+ return {
61
+ name: 'Viewport support',
62
+ status: 'fail',
63
+ detail: `${issues.length} viewport-declaration issue${issues.length === 1 ? '' : 's'}`,
64
+ suggestions: [
65
+ ...issues,
66
+ 'Edit web/public/manifest.json: add "orientation": "any" (or portrait/landscape) and "min_viewport_width": 360.',
67
+ ],
68
+ };
69
+ }
70
+ if (warnings.length > 0) {
71
+ return {
72
+ name: 'Viewport support',
73
+ status: 'warn',
74
+ detail: warnings[0],
75
+ suggestions: warnings,
76
+ };
77
+ }
78
+ return {
79
+ name: 'Viewport support',
80
+ status: 'pass',
81
+ detail: `orientation=${orientation} · min ${minWidth}px`,
82
+ };
83
+ }
84
+ const VALID_ORIENTATIONS = new Set([
85
+ 'any',
86
+ 'portrait',
87
+ 'landscape',
88
+ 'portrait-primary',
89
+ 'landscape-primary',
90
+ ]);
91
+ const RECOMMENDED_WIDTHS = [320, 360, 414, 600, 768, 1024];
92
+ //# sourceMappingURL=viewport-support.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport-support.js","sourceRoot":"","sources":["../../src/checks/viewport-support.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,16 +1,37 @@
1
- import { checkNoPlaceholders } from './checks/no-placeholders.js';
2
- import { checkNoTracking } from './checks/no-tracking.js';
3
1
  import { checkBrandFonts } from './checks/brand-fonts.js';
4
- import { checkNoBrandOverrides } from './checks/no-brand-overrides.js';
5
- import { checkManifest } from './checks/manifest.js';
2
+ import { checkBrandTokens } from './checks/brand-tokens.js';
6
3
  import { checkBundleSize } from './checks/bundle-size.js';
4
+ import { checkDarkMode } from './checks/dark-mode.js';
5
+ import { checkHtmlMeta } from './checks/html-meta.js';
6
+ import { checkLicenseMit } from './checks/license-mit.js';
7
+ import { checkManifest } from './checks/manifest.js';
8
+ import { checkNoBrandOverrides } from './checks/no-brand-overrides.js';
9
+ import { checkNoEnvProduction } from './checks/no-env-production.js';
10
+ import { checkNoPlaceholders } from './checks/no-placeholders.js';
11
+ import { checkNoScroll } from './checks/no-scroll.js';
12
+ import { checkNoTracking } from './checks/no-tracking.js';
13
+ import { checkPwaMeta } from './checks/pwa-meta.js';
14
+ import { checkStoreLink } from './checks/store-link.js';
15
+ import { checkUnsafeVh } from './checks/unsafe-vh.js';
16
+ import { checkViewportSupport } from './checks/viewport-support.js';
17
+ import { fsFileSource, mapFileSource } from './lib/file-source.js';
18
+ import { isGameProject } from './lib/project-type.js';
7
19
  import type { CheckResult } from './types.js';
8
20
  export type { CheckStatus, CheckResult } from './types.js';
9
- export { checkNoPlaceholders, checkNoTracking, checkBrandFonts, checkNoBrandOverrides, checkManifest, checkBundleSize, };
21
+ export type { FileSource } from './lib/file-source.js';
22
+ export { fsFileSource, mapFileSource, isGameProject };
23
+ export { checkBrandFonts, checkBrandTokens, checkBundleSize, checkDarkMode, checkHtmlMeta, checkLicenseMit, checkManifest, checkNoBrandOverrides, checkNoEnvProduction, checkNoPlaceholders, checkNoScroll, checkNoTracking, checkPwaMeta, checkStoreLink, checkUnsafeVh, checkViewportSupport, };
24
+ export { auditLive, checkNoTrackingLive, checkBrandFontsLive, checkManifestLive, checkBundleSizeLive, checkUnsafeVhLive, } from './live/index.js';
25
+ export type { LiveAuditInput, LiveAuditReport } from './live/index.js';
10
26
  /**
11
- * Runs every compliance check against `repoDir` (the root of an app, the
12
- * directory containing package.json + web/). Returns results in a stable
13
- * order so callers can render predictable output.
27
+ * Runs every compliance check against the source. Two front doors:
28
+ * - `runChecks(repoDir)` — CLI / CI; reads from disk.
29
+ * - `runChecksFromFiles(map)` — VibeCode agent; reads from a Map.
30
+ *
31
+ * Both call the same underlying check functions via the FileSource
32
+ * abstraction, so rules stay in one place. Results are returned in a
33
+ * stable order so callers can render predictable output.
14
34
  */
15
35
  export declare function runChecks(repoDir: string): Promise<CheckResult[]>;
36
+ export declare function runChecksFromFiles(files: Map<string, string>): Promise<CheckResult[]>;
16
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,eAAe,GAChB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CASvE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAmB,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AACtD,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,aAAa,EACb,eAAe,EACf,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,cAAc,EACd,aAAa,EACb,oBAAoB,GACrB,CAAC;AAKF,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvE;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAEvE;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAE3F"}
package/dist/index.js CHANGED
@@ -1,23 +1,60 @@
1
- import { checkNoPlaceholders } from './checks/no-placeholders.js';
2
- import { checkNoTracking } from './checks/no-tracking.js';
3
1
  import { checkBrandFonts } from './checks/brand-fonts.js';
4
- import { checkNoBrandOverrides } from './checks/no-brand-overrides.js';
5
- import { checkManifest } from './checks/manifest.js';
2
+ import { checkBrandTokens } from './checks/brand-tokens.js';
6
3
  import { checkBundleSize } from './checks/bundle-size.js';
7
- export { checkNoPlaceholders, checkNoTracking, checkBrandFonts, checkNoBrandOverrides, checkManifest, checkBundleSize, };
4
+ import { checkDarkMode } from './checks/dark-mode.js';
5
+ import { checkHtmlMeta } from './checks/html-meta.js';
6
+ import { checkLicenseMit } from './checks/license-mit.js';
7
+ import { checkManifest } from './checks/manifest.js';
8
+ import { checkNoBrandOverrides } from './checks/no-brand-overrides.js';
9
+ import { checkNoEnvProduction } from './checks/no-env-production.js';
10
+ import { checkNoPlaceholders } from './checks/no-placeholders.js';
11
+ import { checkNoScroll } from './checks/no-scroll.js';
12
+ import { checkNoTracking } from './checks/no-tracking.js';
13
+ import { checkPwaMeta } from './checks/pwa-meta.js';
14
+ import { checkStoreLink } from './checks/store-link.js';
15
+ import { checkUnsafeVh } from './checks/unsafe-vh.js';
16
+ import { checkViewportSupport } from './checks/viewport-support.js';
17
+ import { fsFileSource, mapFileSource } from './lib/file-source.js';
18
+ import { isGameProject } from './lib/project-type.js';
19
+ export { fsFileSource, mapFileSource, isGameProject };
20
+ export { checkBrandFonts, checkBrandTokens, checkBundleSize, checkDarkMode, checkHtmlMeta, checkLicenseMit, checkManifest, checkNoBrandOverrides, checkNoEnvProduction, checkNoPlaceholders, checkNoScroll, checkNoTracking, checkPwaMeta, checkStoreLink, checkUnsafeVh, checkViewportSupport, };
21
+ // Live-URL audit (used by the compliance audit Worker; runs in
22
+ // browser/Workers env, no filesystem). Separate export path so callers
23
+ // don't accidentally pull node:fs in via the file-walking checks.
24
+ export { auditLive, checkNoTrackingLive, checkBrandFontsLive, checkManifestLive, checkBundleSizeLive, checkUnsafeVhLive, } from './live/index.js';
8
25
  /**
9
- * Runs every compliance check against `repoDir` (the root of an app, the
10
- * directory containing package.json + web/). Returns results in a stable
11
- * order so callers can render predictable output.
26
+ * Runs every compliance check against the source. Two front doors:
27
+ * - `runChecks(repoDir)` — CLI / CI; reads from disk.
28
+ * - `runChecksFromFiles(map)` — VibeCode agent; reads from a Map.
29
+ *
30
+ * Both call the same underlying check functions via the FileSource
31
+ * abstraction, so rules stay in one place. Results are returned in a
32
+ * stable order so callers can render predictable output.
12
33
  */
13
34
  export async function runChecks(repoDir) {
35
+ return runChecksOn(fsFileSource(repoDir));
36
+ }
37
+ export async function runChecksFromFiles(files) {
38
+ return runChecksOn(mapFileSource(files));
39
+ }
40
+ async function runChecksOn(source) {
14
41
  return Promise.all([
15
- checkNoPlaceholders(repoDir),
16
- checkNoTracking(repoDir),
17
- checkBrandFonts(repoDir),
18
- checkNoBrandOverrides(repoDir),
19
- checkManifest(repoDir),
20
- checkBundleSize(repoDir),
42
+ checkLicenseMit(source),
43
+ checkNoEnvProduction(source),
44
+ checkNoPlaceholders(source),
45
+ checkNoTracking(source),
46
+ checkBrandFonts(source),
47
+ checkBrandTokens(source),
48
+ checkNoBrandOverrides(source),
49
+ checkNoScroll(source),
50
+ checkViewportSupport(source),
51
+ checkUnsafeVh(source),
52
+ checkHtmlMeta(source),
53
+ checkPwaMeta(source),
54
+ checkManifest(source),
55
+ checkStoreLink(source),
56
+ checkDarkMode(source),
57
+ checkBundleSize(source),
21
58
  ]);
22
59
  }
23
60
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI1D,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,eAAe,GAChB,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe;IAC7C,OAAO,OAAO,CAAC,GAAG,CAAC;QACjB,mBAAmB,CAAC,OAAO,CAAC;QAC5B,eAAe,CAAC,OAAO,CAAC;QACxB,eAAe,CAAC,OAAO,CAAC;QACxB,qBAAqB,CAAC,OAAO,CAAC;QAC9B,aAAa,CAAC,OAAO,CAAC;QACtB,eAAe,CAAC,OAAO,CAAC;KACzB,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAmB,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAKtD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AACtD,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,aAAa,EACb,eAAe,EACf,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,cAAc,EACd,aAAa,EACb,oBAAoB,GACrB,CAAC;AAEF,+DAA+D;AAC/D,uEAAuE;AACvE,kEAAkE;AAClE,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAGzB;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe;IAC7C,OAAO,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAA0B;IACjE,OAAO,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAAkB;IAC3C,OAAO,OAAO,CAAC,GAAG,CAAC;QACjB,eAAe,CAAC,MAAM,CAAC;QACvB,oBAAoB,CAAC,MAAM,CAAC;QAC5B,mBAAmB,CAAC,MAAM,CAAC;QAC3B,eAAe,CAAC,MAAM,CAAC;QACvB,eAAe,CAAC,MAAM,CAAC;QACvB,gBAAgB,CAAC,MAAM,CAAC;QACxB,qBAAqB,CAAC,MAAM,CAAC;QAC7B,aAAa,CAAC,MAAM,CAAC;QACrB,oBAAoB,CAAC,MAAM,CAAC;QAC5B,aAAa,CAAC,MAAM,CAAC;QACrB,aAAa,CAAC,MAAM,CAAC;QACrB,YAAY,CAAC,MAAM,CAAC;QACpB,aAAa,CAAC,MAAM,CAAC;QACrB,cAAc,CAAC,MAAM,CAAC;QACtB,aAAa,CAAC,MAAM,CAAC;QACrB,eAAe,CAAC,MAAM,CAAC;KACxB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,52 @@
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
+ export interface FileSource {
17
+ /** Yield every relevant file path. Implementations skip noise dirs. */
18
+ list(): AsyncIterable<string>;
19
+ /** UTF-8 text. Returns null if the path doesn't exist. */
20
+ read(path: string): Promise<string | null>;
21
+ /** Raw bytes. Returns null if the path doesn't exist. Optional — only
22
+ * bundle-size needs binary access today. */
23
+ readBytes?(path: string): Promise<Uint8Array | null>;
24
+ /** Names of direct children of `dir`. Returns null if dir missing.
25
+ * Optional — only bundle-size needs directory enumeration. */
26
+ listDir?(dir: string): Promise<string[] | null>;
27
+ }
28
+ /**
29
+ * Filesystem-backed FileSource — used by the CLI and CI. Walks the
30
+ * directory tree once on first list() iteration; uses node:fs for
31
+ * read/readBytes/listDir.
32
+ *
33
+ * Paths containing `..` segments are rejected — every read returns
34
+ * null. Defense in depth: today's checks only pass paths that came
35
+ * from `list()` (which stays inside the root), but a future buggy or
36
+ * malicious check could try `source.read('../etc/passwd')`. We refuse
37
+ * rather than letting `path.join` resolve outside repoDir.
38
+ */
39
+ export declare function fsFileSource(repoDir: string): FileSource;
40
+ /**
41
+ * Map-backed FileSource — used by the VibeCode agent (Workers env).
42
+ * The agent's session DO holds the virtual filesystem as a Map; this
43
+ * adapter exposes it through the same interface as the on-disk CLI.
44
+ *
45
+ * `readBytes` decodes the stored UTF-8 string back to bytes — fine for
46
+ * text-only checks. The agent has no built artefacts (it runs pre-
47
+ * deploy), so bundle-size will return its "not built yet" warn anyway.
48
+ *
49
+ * `listDir` synthesises directory listings from the Map keys.
50
+ */
51
+ export declare function mapFileSource(files: Map<string, string>): FileSource;
52
+ //# sourceMappingURL=file-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-source.d.ts","sourceRoot":"","sources":["../../src/lib/file-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,IAAI,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9B,0DAA0D;IAC1D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3C;iDAC6C;IAC7C,SAAS,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACrD;mEAC+D;IAC/D,OAAO,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;CACjD;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CA+BxD;AA4BD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,UAAU,CA8BpE"}
@@ -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"}