@freeappstore/compliance 0.5.0 → 0.6.1

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 (67) hide show
  1. package/dist/checks/claude-md-slim.d.ts +4 -0
  2. package/dist/checks/claude-md-slim.d.ts.map +1 -0
  3. package/dist/checks/claude-md-slim.js +88 -0
  4. package/dist/checks/claude-md-slim.js.map +1 -0
  5. package/dist/checks/dark-mode.d.ts +1 -1
  6. package/dist/checks/dark-mode.d.ts.map +1 -1
  7. package/dist/checks/dark-mode.js +11 -3
  8. package/dist/checks/dark-mode.js.map +1 -1
  9. package/dist/checks/html-meta.d.ts.map +1 -1
  10. package/dist/checks/html-meta.js +7 -2
  11. package/dist/checks/html-meta.js.map +1 -1
  12. package/dist/checks/license-mit.d.ts.map +1 -1
  13. package/dist/checks/license-mit.js +8 -1
  14. package/dist/checks/license-mit.js.map +1 -1
  15. package/dist/checks/manifest.d.ts +9 -3
  16. package/dist/checks/manifest.d.ts.map +1 -1
  17. package/dist/checks/manifest.js +61 -18
  18. package/dist/checks/manifest.js.map +1 -1
  19. package/dist/checks/no-brand-overrides.d.ts.map +1 -1
  20. package/dist/checks/no-brand-overrides.js +83 -14
  21. package/dist/checks/no-brand-overrides.js.map +1 -1
  22. package/dist/checks/no-env-production.d.ts.map +1 -1
  23. package/dist/checks/no-env-production.js +5 -1
  24. package/dist/checks/no-env-production.js.map +1 -1
  25. package/dist/checks/no-placeholders.d.ts.map +1 -1
  26. package/dist/checks/no-placeholders.js +16 -3
  27. package/dist/checks/no-placeholders.js.map +1 -1
  28. package/dist/checks/no-scroll.js +1 -3
  29. package/dist/checks/no-scroll.js.map +1 -1
  30. package/dist/checks/no-tracking.d.ts.map +1 -1
  31. package/dist/checks/no-tracking.js +68 -16
  32. package/dist/checks/no-tracking.js.map +1 -1
  33. package/dist/checks/pwa-meta.d.ts.map +1 -1
  34. package/dist/checks/pwa-meta.js +6 -2
  35. package/dist/checks/pwa-meta.js.map +1 -1
  36. package/dist/checks/pwa-offline.d.ts +40 -0
  37. package/dist/checks/pwa-offline.d.ts.map +1 -0
  38. package/dist/checks/pwa-offline.js +388 -0
  39. package/dist/checks/pwa-offline.js.map +1 -0
  40. package/dist/checks/store-link.js +1 -1
  41. package/dist/checks/store-link.js.map +1 -1
  42. package/dist/checks/unsafe-vh.d.ts.map +1 -1
  43. package/dist/checks/unsafe-vh.js +18 -6
  44. package/dist/checks/unsafe-vh.js.map +1 -1
  45. package/dist/checks/viewport-support.d.ts +4 -1
  46. package/dist/checks/viewport-support.d.ts.map +1 -1
  47. package/dist/checks/viewport-support.js +29 -19
  48. package/dist/checks/viewport-support.js.map +1 -1
  49. package/dist/index.d.ts +5 -4
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +6 -3
  52. package/dist/index.js.map +1 -1
  53. package/dist/lib/file-source.d.ts.map +1 -1
  54. package/dist/lib/file-source.js +9 -1
  55. package/dist/lib/file-source.js.map +1 -1
  56. package/dist/lib/inline-manifest.d.ts +32 -0
  57. package/dist/lib/inline-manifest.d.ts.map +1 -0
  58. package/dist/lib/inline-manifest.js +109 -0
  59. package/dist/lib/inline-manifest.js.map +1 -0
  60. package/dist/lib/strip.d.ts +60 -0
  61. package/dist/lib/strip.d.ts.map +1 -0
  62. package/dist/lib/strip.js +169 -0
  63. package/dist/lib/strip.js.map +1 -0
  64. package/dist/live/index.d.ts.map +1 -1
  65. package/dist/live/index.js +5 -1
  66. package/dist/live/index.js.map +1 -1
  67. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"no-scroll.js","sourceRoot":"","sources":["../../src/checks/no-scroll.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"no-scroll.js","sourceRoot":"","sources":["../../src/checks/no-scroll.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"no-tracking.d.ts","sourceRoot":"","sources":["../../src/checks/no-tracking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAiB/C,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAiC9E"}
1
+ {"version":3,"file":"no-tracking.d.ts","sourceRoot":"","sources":["../../src/checks/no-tracking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAuE/C,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CA6C9E"}
@@ -1,25 +1,77 @@
1
- // Same forbidden list the template's compliance.yml enforces — kept in
2
- // sync because both are downstream consumers of this package long-term.
3
- const FORBIDDEN = [
4
- 'google-analytics',
5
- 'gtag',
6
- 'amplitude',
7
- 'mixpanel',
8
- 'segment',
9
- 'hotjar',
10
- 'plausible',
11
- 'posthog',
1
+ import { stripCommentsForExt } from '../lib/strip.js';
2
+ function wb(name) {
3
+ // word-boundary, case-insensitive: only matches the literal token surrounded
4
+ // by non-word chars (or string start/end). Avoids `segmentation`, `collideSegment`.
5
+ return new RegExp(`(?:^|[^a-zA-Z0-9_])${escapeForRegExp(name)}(?:$|[^a-zA-Z0-9_])`, 'i');
6
+ }
7
+ const TRACKERS = [
8
+ { name: 'google-analytics', patterns: [/google-analytics/i, /googletagmanager\.com/i] },
9
+ // `gtag` as a bare token would catch nothing real, but the GA install snippet
10
+ // always calls it like `gtag('config', ...)` or `gtag('event', ...)` or
11
+ // declares `window.gtag = ...`. Match those, not the bare identifier.
12
+ { name: 'gtag', patterns: [/\bgtag\s*\(\s*['"]/i, /window\.gtag\b/i, /gtag\.js/i] },
13
+ // Amplitude SDK shows up as @amplitude/* scoped package, amplitude.com host,
14
+ // or amplitude.init/track/getInstance call. Bare `amplitude` (the math term)
15
+ // is fine.
16
+ {
17
+ name: 'amplitude',
18
+ patterns: [
19
+ /@amplitude\//i,
20
+ /amplitude\.com/i,
21
+ /\bamplitude\s*\.\s*(?:init|track|getInstance|setUserId|logEvent)\b/i,
22
+ /require\(['"]amplitude/i,
23
+ /from\s+['"]amplitude/i,
24
+ ],
25
+ },
26
+ { name: 'mixpanel', patterns: [wb('mixpanel')] },
27
+ // Segment SDK: scoped package, hostname, or window.analytics.track/identify.
28
+ // Bare `segment` (geometry term — ball-line-segment, drop-target row segment)
29
+ // is fine.
30
+ {
31
+ name: 'segment',
32
+ patterns: [
33
+ /@segment\//i,
34
+ /segment\.io/i,
35
+ /segment\.com\/analytics/i,
36
+ /cdn\.segment\.com/i,
37
+ /window\.analytics\s*\.\s*(?:track|identify|page|group)\b/i,
38
+ /\bAnalyticsBrowser\.load\b/i,
39
+ ],
40
+ },
41
+ { name: 'hotjar', patterns: [wb('hotjar')] },
42
+ { name: 'plausible', patterns: [wb('plausible')] },
43
+ { name: 'posthog', patterns: [wb('posthog')] },
12
44
  ];
13
45
  const SCAN_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.html', '.json']);
46
+ function escapeForRegExp(s) {
47
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
48
+ }
49
+ // Per-game compliance tests legitimately mention these tracker names as
50
+ // the banned list they assert NOT to find. Treating those as positives
51
+ // is the classic "check finds its own assertion" false positive.
52
+ function isSelfReferenceTestFile(path) {
53
+ return /(?:^|\/)(?:test|tests|__tests__)\//.test(path) && /compliance\.test\.[jt]sx?$/.test(path);
54
+ }
14
55
  export async function checkNoTracking(source) {
15
56
  const hits = [];
16
57
  for await (const path of source.list()) {
17
- if (!SCAN_EXTS.has(extOf(path)))
58
+ const ext = extOf(path);
59
+ if (!SCAN_EXTS.has(ext))
60
+ continue;
61
+ if (isSelfReferenceTestFile(path))
18
62
  continue;
19
- const content = await source.read(path);
20
- if (!content)
63
+ const raw = await source.read(path);
64
+ if (!raw)
21
65
  continue;
22
- const matches = FORBIDDEN.filter((sdk) => content.includes(sdk));
66
+ // Strip comments only — NOT string literals. Real tracker imports
67
+ // live inside strings (`import from "@amplitude/..."`,
68
+ // `<script src="https://www.google-analytics.com/...">`), so
69
+ // erasing string contents would erase the very evidence we need.
70
+ // A rare false positive remains if someone mentions a tracker name
71
+ // inside a non-import string literal; comment-stripping handles the
72
+ // common case (documentation comments).
73
+ const content = stripCommentsForExt(raw, ext);
74
+ const matches = TRACKERS.filter((t) => t.patterns.some((re) => re.test(content))).map((t) => t.name);
23
75
  if (matches.length > 0) {
24
76
  hits.push({ file: path, matches });
25
77
  }
@@ -28,7 +80,7 @@ export async function checkNoTracking(source) {
28
80
  return {
29
81
  name: 'No tracking SDKs',
30
82
  status: 'pass',
31
- detail: `scanned for ${FORBIDDEN.length} known trackers`,
83
+ detail: `scanned for ${TRACKERS.length} known trackers`,
32
84
  };
33
85
  }
34
86
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"no-tracking.js","sourceRoot":"","sources":["../../src/checks/no-tracking.ts"],"names":[],"mappings":"AAGA,uEAAuE;AACvE,wEAAwE;AACxE,MAAM,SAAS,GAAG;IAChB,kBAAkB;IAClB,MAAM;IACN,WAAW;IACX,UAAU;IACV,SAAS;IACT,QAAQ;IACR,WAAW;IACX,SAAS;CACV,CAAC;AAEF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAE5E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAkB;IACtD,MAAM,IAAI,GAA0C,EAAE,CAAC;IAEvD,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAAE,SAAS;QAC1C,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO;YACL,IAAI,EAAE,kBAAkB;YACxB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,eAAe,SAAS,CAAC,MAAM,iBAAiB;SACzD,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,gCAAgC,IAAI;aACvD,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;aACjD,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC5C,WAAW,EAAE;YACX,gFAAgF;YAChF,uFAAuF;SACxF;KACF,CAAC;AACJ,CAAC;AAED,SAAS,KAAK,CAAC,IAAY;IACzB,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACpC,OAAO,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1D,CAAC"}
1
+ {"version":3,"file":"no-tracking.js","sourceRoot":"","sources":["../../src/checks/no-tracking.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pwa-meta.d.ts","sourceRoot":"","sources":["../../src/checks/pwa-meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAoB3E"}
1
+ {"version":3,"file":"pwa-meta.d.ts","sourceRoot":"","sources":["../../src/checks/pwa-meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAuB3E"}
@@ -1,3 +1,4 @@
1
+ import { stripHtmlComments } from '../lib/strip.js';
1
2
  const HTML_PATH = 'web/index.html';
2
3
  /**
3
4
  * For an "Add to Home Screen" install on iOS Safari to launch the app
@@ -15,14 +16,17 @@ const HTML_PATH = 'web/index.html';
15
16
  * is specifically the iOS install hint that lives in the HTML head.
16
17
  */
17
18
  export async function checkPwaMeta(source) {
18
- const html = await source.read(HTML_PATH);
19
- if (html === null) {
19
+ const rawHtml = await source.read(HTML_PATH);
20
+ if (rawHtml === null) {
20
21
  return {
21
22
  name: 'PWA meta tags',
22
23
  status: 'fail',
23
24
  detail: `${HTML_PATH} not found`,
24
25
  };
25
26
  }
27
+ // Strip HTML comments — `<!-- <meta name="apple-mobile-web-app-capable"> -->`
28
+ // doesn't tell iOS anything.
29
+ const html = stripHtmlComments(rawHtml);
26
30
  if (/<meta[^>]*\bname\s*=\s*["'](?:apple-)?mobile-web-app-capable["']/i.test(html)) {
27
31
  return { name: 'PWA meta tags', status: 'pass', detail: 'iOS install hint present' };
28
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pwa-meta.js","sourceRoot":"","sources":["../../src/checks/pwa-meta.ts"],"names":[],"mappings":"AAGA,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAkB;IACnD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,SAAS,YAAY;SACjC,CAAC;IACJ,CAAC;IACD,IAAI,mEAAmE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACvF,CAAC;IACD,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,+DAA+D;QACvE,WAAW,EAAE;YACX,+GAA+G;SAChH;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"pwa-meta.js","sourceRoot":"","sources":["../../src/checks/pwa-meta.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGpD,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAkB;IACnD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,SAAS,YAAY;SACjC,CAAC;IACJ,CAAC;IACD,8EAA8E;IAC9E,6BAA6B;IAC7B,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,mEAAmE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACvF,CAAC;IACD,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,+DAA+D;QACvE,WAAW,EAAE;YACX,+GAA+G;SAChH;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { FileSource } from '../lib/file-source.js';
2
+ import type { CheckResult } from '../types.js';
3
+ /**
4
+ * Two things this check enforces:
5
+ *
6
+ * 1. Platform mandate — every app on freeappstore.online MUST be an
7
+ * installable PWA (service worker registered). Triggered whenever
8
+ * a project has a `web/index.html` (the universal signal of a
9
+ * Vite app destined for the storefront). Hand-rolled
10
+ * `serviceWorker.register` counts; vite-plugin-pwa counts. Anything
11
+ * else fails. Non-app repos that don't ship a web entry are
12
+ * naturally exempt — they're not subject to compliance anyway.
13
+ *
14
+ * 2. Offline-correctness quality — among PWAs that ARE configured,
15
+ * the workbox setup actually has to work from the home screen
16
+ * while offline. Apps that ship a manifest but mis-configure the
17
+ * service worker install fine, then show a blank screen offline.
18
+ *
19
+ * Three quality failure modes this catches, all observed in the wild:
20
+ *
21
+ * 1. `maximumFileSizeToCacheInBytes` left at the workbox default (2 MB).
22
+ * Any bundle chunk above 2 MB is silently dropped from the precache.
23
+ * The SW serves index.html from cache, then the JS chunk 404s offline.
24
+ *
25
+ * 2. Google Fonts loaded via render-blocking <link> in index.html with
26
+ * no `runtimeCaching` rule. Browser HTTP cache works most of the
27
+ * time, but standalone-mode launches on iOS bypass it inconsistently
28
+ * — fonts fall back to system, and on cold first offline launch the
29
+ * <link> can stall paint for hundreds of ms.
30
+ *
31
+ * 3. Assets in extensions not covered by `globPatterns`. The default
32
+ * list is js/css/html/png/svg/ico/woff2. Any wasm, json, audio, etc.
33
+ * shipped under web/public/ isn't precached → offline 404.
34
+ *
35
+ * Also catches the inverse: index.html links a manifest but the config
36
+ * has no service worker at all → "installable" PWA that always needs
37
+ * network.
38
+ */
39
+ export declare function checkPwaOffline(source: FileSource): Promise<CheckResult>;
40
+ //# sourceMappingURL=pwa-offline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pwa-offline.d.ts","sourceRoot":"","sources":["../../src/checks/pwa-offline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAwM9E"}
@@ -0,0 +1,388 @@
1
+ import { stripCommentsAndStrings, stripHtmlComments } from '../lib/strip.js';
2
+ const VITE_CONFIG = 'web/vite.config.ts';
3
+ const INDEX_HTML = 'web/index.html';
4
+ const PUBLIC_DIR = 'web/public';
5
+ /**
6
+ * Two things this check enforces:
7
+ *
8
+ * 1. Platform mandate — every app on freeappstore.online MUST be an
9
+ * installable PWA (service worker registered). Triggered whenever
10
+ * a project has a `web/index.html` (the universal signal of a
11
+ * Vite app destined for the storefront). Hand-rolled
12
+ * `serviceWorker.register` counts; vite-plugin-pwa counts. Anything
13
+ * else fails. Non-app repos that don't ship a web entry are
14
+ * naturally exempt — they're not subject to compliance anyway.
15
+ *
16
+ * 2. Offline-correctness quality — among PWAs that ARE configured,
17
+ * the workbox setup actually has to work from the home screen
18
+ * while offline. Apps that ship a manifest but mis-configure the
19
+ * service worker install fine, then show a blank screen offline.
20
+ *
21
+ * Three quality failure modes this catches, all observed in the wild:
22
+ *
23
+ * 1. `maximumFileSizeToCacheInBytes` left at the workbox default (2 MB).
24
+ * Any bundle chunk above 2 MB is silently dropped from the precache.
25
+ * The SW serves index.html from cache, then the JS chunk 404s offline.
26
+ *
27
+ * 2. Google Fonts loaded via render-blocking <link> in index.html with
28
+ * no `runtimeCaching` rule. Browser HTTP cache works most of the
29
+ * time, but standalone-mode launches on iOS bypass it inconsistently
30
+ * — fonts fall back to system, and on cold first offline launch the
31
+ * <link> can stall paint for hundreds of ms.
32
+ *
33
+ * 3. Assets in extensions not covered by `globPatterns`. The default
34
+ * list is js/css/html/png/svg/ico/woff2. Any wasm, json, audio, etc.
35
+ * shipped under web/public/ isn't precached → offline 404.
36
+ *
37
+ * Also catches the inverse: index.html links a manifest but the config
38
+ * has no service worker at all → "installable" PWA that always needs
39
+ * network.
40
+ */
41
+ export async function checkPwaOffline(source) {
42
+ const config = await source.read(VITE_CONFIG);
43
+ const html = await source.read(INDEX_HTML);
44
+ // Strip HTML comments before matching — `<!-- <link rel="manifest"> -->`
45
+ // is not a live install claim, and a commented-out fonts <link> is
46
+ // not loading anything.
47
+ const htmlCode = html === null ? null : stripHtmlComments(html);
48
+ const linksManifest = htmlCode !== null && /<link[^>]+rel\s*=\s*["']manifest["']/i.test(htmlCode);
49
+ const linksGoogleFonts = htmlCode !== null && /fonts\.(googleapis|gstatic)\.com/i.test(htmlCode);
50
+ // Strip comments and string-literal contents before any regex matching
51
+ // against the config text — otherwise `VitePWA(` in a comment or
52
+ // `"workbox: {"` in a string is treated as real code (false positive).
53
+ const configCode = config === null ? null : stripCommentsAndStrings(config);
54
+ const hasVitePwa = configCode !== null && /\bVitePWA\s*\(/.test(configCode);
55
+ // `injectManifest` strategy means the developer writes their own SW
56
+ // file (typically web/src/sw.ts). The `workbox` field doesn't apply
57
+ // — `injectManifest` config does — so the rest of our checks are
58
+ // inapplicable. Trust the dev's manual SW. (Matched against `config`
59
+ // — not `configCode` — because the string-stripping would erase
60
+ // `"injectManifest"` from the latter.)
61
+ const usesInjectManifest = config !== null && /strategies\s*:\s*["']injectManifest["']/.test(config);
62
+ // Hand-rolled SW registration (e.g. an inline <script> calling
63
+ // navigator.serviceWorker.register) is a legitimate alternative to
64
+ // vite-plugin-pwa. If it exists, we trust the dev to manage their own
65
+ // precache and limit ourselves to the install-claim check.
66
+ const hasManualSw = (html !== null && /serviceWorker\.register/.test(html)) ||
67
+ (await sourceHasSwRegistration(source));
68
+ const hasServiceWorker = hasVitePwa || hasManualSw;
69
+ // Platform mandate (freeappstore.online): every app must be an
70
+ // installable PWA. "Installable" at the static-check level means a
71
+ // service worker registers — VitePWA (which also injects the
72
+ // manifest link), an injectManifest setup, or hand-rolled register.
73
+ // Triggered by the presence of `web/index.html` (the universal signal
74
+ // of a Vite-built app). Repos that don't ship a web entry — admin
75
+ // tools, backends, agents — are naturally exempt; compliance isn't
76
+ // run against them anyway.
77
+ if (html !== null && !hasServiceWorker) {
78
+ return {
79
+ name: 'PWA offline correctness',
80
+ status: 'fail',
81
+ detail: 'platform mandate: every app on freeappstore.online must be an installable PWA, but no service worker is registered',
82
+ suggestions: [
83
+ 'Add `vite-plugin-pwa` to web/devDependencies, then wire `VitePWA({...})` into vite.config.ts plugins. Mirror the canonical config used in the canonical scaffolds, etc.',
84
+ 'Or, if you prefer to manage your own SW, register it from web/src/main.tsx with `navigator.serviceWorker.register("/sw.js")` and ship the SW yourself.',
85
+ ],
86
+ };
87
+ }
88
+ // "Installable" claim with no service worker — the worst failure mode.
89
+ // The PWA installs from the manifest but launches into a network fetch
90
+ // for `/` that 404s offline → blank screen on home screen.
91
+ if (linksManifest && !hasServiceWorker) {
92
+ return {
93
+ name: 'PWA offline correctness',
94
+ status: 'fail',
95
+ detail: 'index.html links a manifest but no service worker is registered → installable PWA that cannot load offline from home screen',
96
+ suggestions: [
97
+ 'Install vite-plugin-pwa and add VitePWA({...}) to vite.config.ts plugins.',
98
+ 'Or register a service worker manually (`navigator.serviceWorker.register("/sw.js")`).',
99
+ 'Or drop the <link rel="manifest"> from index.html if this is not meant to be installable.',
100
+ ],
101
+ };
102
+ }
103
+ // No vite.config.ts at all — can't analyze further. Either not a Vite
104
+ // project, or PWA wiring lives elsewhere; either way, nothing for us
105
+ // to assert about workbox.
106
+ if (config === null) {
107
+ return {
108
+ name: 'PWA offline correctness',
109
+ status: 'pass',
110
+ detail: hasManualSw
111
+ ? 'no vite.config.ts; hand-rolled service worker present'
112
+ : 'no web/vite.config.ts (not a Vite project)',
113
+ };
114
+ }
115
+ if (!hasVitePwa) {
116
+ return {
117
+ name: 'PWA offline correctness',
118
+ status: 'pass',
119
+ detail: hasManualSw
120
+ ? 'hand-rolled service worker; no install claim to verify'
121
+ : 'not a PWA (no VitePWA, no manifest link)',
122
+ };
123
+ }
124
+ // injectManifest: developer hand-writes the SW. Their `workbox` config
125
+ // (if any) is irrelevant; the analyzable surface lives in their SW
126
+ // source which we don't parse.
127
+ if (usesInjectManifest) {
128
+ return {
129
+ name: 'PWA offline correctness',
130
+ status: 'pass',
131
+ detail: 'VitePWA with injectManifest strategy — SW managed in src',
132
+ };
133
+ }
134
+ // Extract the workbox block. Match `workbox: { ... }` allowing nested
135
+ // braces. We pass `configCode` (comments/strings stripped) so the
136
+ // matcher can't false-positive on `"workbox: {"` inside a string, but
137
+ // we slice out of `config` so the returned substring still has its
138
+ // real string contents — otherwise downstream `globPatterns:
139
+ // ["..."]` regex would fail.
140
+ const workbox = extractBalancedBlock(config, configCode, /workbox\s*:\s*\{/);
141
+ if (workbox === null) {
142
+ return {
143
+ name: 'PWA offline correctness',
144
+ status: 'warn',
145
+ detail: 'VitePWA present but no workbox block parsed — defaults may leave assets unprecached',
146
+ suggestions: [
147
+ 'Add a `workbox: { ... }` block with globPatterns, maximumFileSizeToCacheInBytes, and runtimeCaching.',
148
+ ],
149
+ };
150
+ }
151
+ const issues = [];
152
+ const suggestions = [];
153
+ // Issue 1: bundle-size cap. Default is 2 MiB; many real bundles exceed it.
154
+ // Also catch the inverse footgun: a value *lower* than the default
155
+ // (e.g. someone copy-pasted `1024` thinking it was MB), which silently
156
+ // makes precache worse.
157
+ const capValue = parseBundleCap(workbox);
158
+ const DEFAULT_CAP = 2 * 1024 * 1024;
159
+ if (capValue === null) {
160
+ issues.push('no maximumFileSizeToCacheInBytes (defaults to 2 MB — bigger chunks silently skipped from precache)');
161
+ suggestions.push('Set `maximumFileSizeToCacheInBytes: 10 * 1024 * 1024` so the main bundle is precached.');
162
+ }
163
+ else if (capValue < DEFAULT_CAP) {
164
+ issues.push(`maximumFileSizeToCacheInBytes is ${capValue} bytes — smaller than the workbox default (2 MB); any chunk above this is dropped from precache`);
165
+ suggestions.push('Raise to at least `10 * 1024 * 1024` (10 MB) so real bundles fit.');
166
+ }
167
+ // `VitePWA({ disable: true })` literal turns the plugin off in all
168
+ // environments, so the manifest link is broken. Only flag the
169
+ // unconditional literal — `disable: process.env.NODE_ENV !==
170
+ // "production"` is a common, correct dev-only pattern that we can't
171
+ // evaluate statically.
172
+ if (configCode && /\bdisable\s*:\s*true\b/.test(configCode)) {
173
+ issues.push('VitePWA({ disable: true }) literal — service worker will never register');
174
+ suggestions.push('Drop `disable: true`, or gate it behind a non-production check.');
175
+ }
176
+ // Issue 2: Google Fonts with no runtime caching.
177
+ if (linksGoogleFonts) {
178
+ const hasGoogleApisRule = /fonts\\?\.googleapis\\?\.com/.test(workbox);
179
+ const hasGstaticRule = /fonts\\?\.gstatic\\?\.com/.test(workbox);
180
+ // Workbox also lets you target by `request.destination` — a rule
181
+ // like `({request}) => request.destination === "font"` catches
182
+ // every font request including Google Fonts. Accept that as
183
+ // covering both endpoints rather than emitting a false warn.
184
+ const hasDestinationFontRule = /\bdestination\b/.test(workbox) && /["']font["']/.test(workbox);
185
+ const fontsCovered = (hasGoogleApisRule && hasGstaticRule) || hasDestinationFontRule;
186
+ if (!fontsCovered) {
187
+ issues.push('index.html loads Google Fonts but workbox has no runtimeCaching for fonts.googleapis.com / fonts.gstatic.com');
188
+ suggestions.push('Add runtimeCaching CacheFirst rules for /^https:\\/\\/fonts\\.googleapis\\.com/ and /^https:\\/\\/fonts\\.gstatic\\.com/.');
189
+ }
190
+ }
191
+ // Issue 3: assets in public/ in extensions not covered by globPatterns.
192
+ // Workbox supports multiple patterns in the array; we union the
193
+ // extensions across all of them, otherwise a config like
194
+ // `globPatterns: ["**/*.{js,css}", "**/*.wasm"]` would look like it
195
+ // omits wasm.
196
+ const covered = extractCoveredExtensions(workbox);
197
+ if (covered !== null && source.listDir) {
198
+ const uncovered = await findUncoveredAssets(source, PUBLIC_DIR, covered);
199
+ if (uncovered.length > 0) {
200
+ const sample = uncovered.slice(0, 3).join(', ');
201
+ issues.push(`web/public/ has files in extensions not in globPatterns: ${sample}${uncovered.length > 3 ? ` (+${uncovered.length - 3} more)` : ''}`);
202
+ const newExts = [...new Set([...covered, ...uncovered.map(extOf)])].filter(Boolean).join(',');
203
+ suggestions.push(`Extend globPatterns to cover them, e.g. \`**/*.{${newExts}}\`.`);
204
+ }
205
+ }
206
+ if (issues.length === 0) {
207
+ return {
208
+ name: 'PWA offline correctness',
209
+ status: 'pass',
210
+ detail: 'workbox precaches everything, fonts cached, bundle cap raised',
211
+ };
212
+ }
213
+ return {
214
+ name: 'PWA offline correctness',
215
+ status: 'warn',
216
+ detail: issues.join('; '),
217
+ suggestions,
218
+ };
219
+ }
220
+ /**
221
+ * Parse the numeric value assigned to `maximumFileSizeToCacheInBytes`.
222
+ * Handles simple integer literals (`10485760`), underscore separators
223
+ * (`10_485_760`), and `A * B * C` arithmetic chains that workbox docs
224
+ * recommend (`10 * 1024 * 1024`). Returns null if the key isn't present
225
+ * or the value isn't a recognisable arithmetic literal.
226
+ */
227
+ function parseBundleCap(workbox) {
228
+ const m = workbox.match(/maximumFileSizeToCacheInBytes\s*:\s*([^,\n}]+)/);
229
+ if (!m)
230
+ return null;
231
+ const expr = (m[1] ?? '').trim().replace(/_/g, '');
232
+ // Strict: only digits, *, whitespace. Anything else (variable
233
+ // reference, function call) → can't evaluate, treat as unknown.
234
+ if (!/^[\d*\s]+$/.test(expr))
235
+ return null;
236
+ const parts = expr.split('*').map((s) => Number(s.trim()));
237
+ if (parts.some((n) => !Number.isFinite(n) || n < 0))
238
+ return null;
239
+ return parts.reduce((a, b) => a * b, 1);
240
+ }
241
+ /**
242
+ * Extracts the `{ ... }` body that follows the first match of `opener`,
243
+ * walking the source character-by-character to balance braces.
244
+ *
245
+ * Two source views are required:
246
+ * - `src` — the real source, used for the returned substring.
247
+ * - `code` — the stripped view (comments/strings blanked) with the
248
+ * same character offsets as `src`. Used for matching the opener and
249
+ * counting braces, so that `}` in a string doesn't close the block.
250
+ *
251
+ * Regex alone can't handle this because workbox blocks contain nested
252
+ * objects (`options: { expiration: {...} }`) and regex doesn't balance.
253
+ */
254
+ function extractBalancedBlock(src, code, opener) {
255
+ const m = code.match(opener);
256
+ if (!m || m.index === undefined)
257
+ return null;
258
+ let i = m.index + m[0].length; // start just after the `{`
259
+ let depth = 1;
260
+ const start = i;
261
+ while (i < code.length) {
262
+ const c = code[i];
263
+ if (c === '{')
264
+ depth++;
265
+ else if (c === '}') {
266
+ depth--;
267
+ if (depth === 0)
268
+ return src.slice(start, i);
269
+ }
270
+ i++;
271
+ }
272
+ return null;
273
+ }
274
+ /**
275
+ * Parses every string literal in the workbox block, finds those that
276
+ * look like glob patterns with a brace-expansion (e.g.
277
+ * `**\/*.{js,css,wasm}`), and unions their extensions. Returns null if
278
+ * no `globPatterns:` key is present at all.
279
+ *
280
+ * Bracket-balances the array body on a string-stripped view so that
281
+ * glob bracket expressions like `**\/[abc]/*` don't truncate the array
282
+ * — that `]` lives inside a string literal and isn't the array's close.
283
+ */
284
+ function extractCoveredExtensions(workbox) {
285
+ const workboxCode = stripCommentsAndStrings(workbox);
286
+ const keyMatch = workboxCode.match(/globPatterns\s*:\s*\[/);
287
+ if (!keyMatch || keyMatch.index === undefined)
288
+ return null;
289
+ // Walk the stripped view to find the matching `]` for this `[`.
290
+ let i = keyMatch.index + keyMatch[0].length;
291
+ const start = i;
292
+ let depth = 1;
293
+ while (i < workboxCode.length && depth > 0) {
294
+ const c = workboxCode[i];
295
+ if (c === '[')
296
+ depth++;
297
+ else if (c === ']')
298
+ depth--;
299
+ if (depth === 0)
300
+ break;
301
+ i++;
302
+ }
303
+ if (depth !== 0)
304
+ return null;
305
+ // Pull the real (un-stripped) array body so we still see the
306
+ // quoted patterns.
307
+ const arrayBody = workbox.slice(start, i);
308
+ const covered = new Set();
309
+ for (const m of arrayBody.matchAll(/["']([^"']+)["']/g)) {
310
+ const pattern = m[1] ?? '';
311
+ const brace = pattern.match(/\{([^}]+)\}/);
312
+ if (brace) {
313
+ for (const ext of brace[1].split(',')) {
314
+ covered.add(ext.trim().toLowerCase());
315
+ }
316
+ }
317
+ else {
318
+ // Bare pattern like "**/*.wasm" — extract the extension after the
319
+ // last `.` (or the entire pattern if it's literally `**/*.wasm`).
320
+ const ext = pattern.match(/\.([a-zA-Z0-9]+)$/);
321
+ if (ext)
322
+ covered.add(ext[1].toLowerCase());
323
+ }
324
+ }
325
+ return covered;
326
+ }
327
+ function extOf(filename) {
328
+ const dot = filename.lastIndexOf('.');
329
+ return dot === -1 ? '' : filename.slice(dot + 1).toLowerCase();
330
+ }
331
+ /**
332
+ * Walks web/public/ (one level deep is enough — workbox precache
333
+ * resolves the pattern recursively, but most public assets sit at the
334
+ * top level or one level down), returns extensions not in `covered`.
335
+ * Ignores the manifest icons and favicons we already know are listed.
336
+ */
337
+ async function findUncoveredAssets(source, dir, covered) {
338
+ if (!source.listDir)
339
+ return [];
340
+ const seen = new Set();
341
+ await walkPublic(source, dir, seen, covered, 0);
342
+ return [...seen].sort();
343
+ }
344
+ /**
345
+ * Best-effort scan of web/src/ entry points for a manual
346
+ * `serviceWorker.register` call. Doesn't recurse into the whole src
347
+ * tree — we only care about top-level entry files (main, index,
348
+ * registerSW) where this conventionally lives.
349
+ */
350
+ async function sourceHasSwRegistration(source) {
351
+ const candidates = [
352
+ 'web/src/main.tsx',
353
+ 'web/src/main.ts',
354
+ 'web/src/index.tsx',
355
+ 'web/src/index.ts',
356
+ 'web/src/registerSW.ts',
357
+ 'web/src/registerSW.js',
358
+ ];
359
+ for (const p of candidates) {
360
+ const text = await source.read(p);
361
+ if (text !== null && /serviceWorker\.register/.test(text))
362
+ return true;
363
+ }
364
+ return false;
365
+ }
366
+ async function walkPublic(source, dir, seen, covered, depth) {
367
+ if (depth > 3)
368
+ return;
369
+ if (!source.listDir)
370
+ return;
371
+ const entries = await source.listDir(dir);
372
+ if (entries === null)
373
+ return;
374
+ for (const name of entries) {
375
+ const full = `${dir}/${name}`;
376
+ if (name.includes('.')) {
377
+ const ext = extOf(name);
378
+ if (!ext || covered.has(ext))
379
+ continue;
380
+ seen.add(name);
381
+ }
382
+ else {
383
+ // Probably a subdirectory — recurse. listDir returns null if it's actually a file.
384
+ await walkPublic(source, full, seen, covered, depth + 1);
385
+ }
386
+ }
387
+ }
388
+ //# sourceMappingURL=pwa-offline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pwa-offline.js","sourceRoot":"","sources":["../../src/checks/pwa-offline.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@ export async function checkStoreLink(source) {
19
19
  if (!path.startsWith('web/src/'))
20
20
  continue;
21
21
  const content = await source.read(path);
22
- if (content && content.includes(domain)) {
22
+ if (content?.includes(domain)) {
23
23
  return { name: 'Store link', status: 'pass', detail: `${domain} referenced in ${path}` };
24
24
  }
25
25
  }