@freegamestore/compliance 0.6.1 → 0.8.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 (95) hide show
  1. package/README.md +1 -1
  2. package/dist/checks/deploy-workflow.d.ts +16 -0
  3. package/dist/checks/deploy-workflow.d.ts.map +1 -0
  4. package/dist/checks/deploy-workflow.js +33 -0
  5. package/dist/checks/deploy-workflow.js.map +1 -0
  6. package/dist/checks/game-naming.d.ts +4 -0
  7. package/dist/checks/game-naming.d.ts.map +1 -0
  8. package/dist/checks/game-naming.js +98 -0
  9. package/dist/checks/game-naming.js.map +1 -0
  10. package/dist/checks/gitignore-complete.d.ts +4 -0
  11. package/dist/checks/gitignore-complete.d.ts.map +1 -0
  12. package/dist/checks/gitignore-complete.js +51 -0
  13. package/dist/checks/gitignore-complete.js.map +1 -0
  14. package/dist/checks/no-any-types.d.ts +4 -0
  15. package/dist/checks/no-any-types.d.ts.map +1 -0
  16. package/dist/checks/no-any-types.js +72 -0
  17. package/dist/checks/no-any-types.js.map +1 -0
  18. package/dist/checks/no-console-log.d.ts +4 -0
  19. package/dist/checks/no-console-log.d.ts.map +1 -0
  20. package/dist/checks/no-console-log.js +63 -0
  21. package/dist/checks/no-console-log.js.map +1 -0
  22. package/dist/checks/no-cookies.d.ts +13 -0
  23. package/dist/checks/no-cookies.d.ts.map +1 -0
  24. package/dist/checks/no-cookies.js +64 -0
  25. package/dist/checks/no-cookies.js.map +1 -0
  26. package/dist/checks/no-excessive-inline-styles.d.ts +4 -0
  27. package/dist/checks/no-excessive-inline-styles.d.ts.map +1 -0
  28. package/dist/checks/no-excessive-inline-styles.js +82 -0
  29. package/dist/checks/no-excessive-inline-styles.js.map +1 -0
  30. package/dist/checks/no-external-fetch.d.ts +4 -0
  31. package/dist/checks/no-external-fetch.d.ts.map +1 -0
  32. package/dist/checks/no-external-fetch.js +102 -0
  33. package/dist/checks/no-external-fetch.js.map +1 -0
  34. package/dist/checks/no-external-scripts.d.ts +16 -0
  35. package/dist/checks/no-external-scripts.d.ts.map +1 -0
  36. package/dist/checks/no-external-scripts.js +91 -0
  37. package/dist/checks/no-external-scripts.js.map +1 -0
  38. package/dist/checks/no-hardcoded-colors.d.ts +4 -0
  39. package/dist/checks/no-hardcoded-colors.d.ts.map +1 -0
  40. package/dist/checks/no-hardcoded-colors.js +79 -0
  41. package/dist/checks/no-hardcoded-colors.js.map +1 -0
  42. package/dist/checks/no-payment-sdk.d.ts +13 -0
  43. package/dist/checks/no-payment-sdk.d.ts.map +1 -0
  44. package/dist/checks/no-payment-sdk.js +118 -0
  45. package/dist/checks/no-payment-sdk.js.map +1 -0
  46. package/dist/checks/no-scroll.js +1 -1
  47. package/dist/checks/no-scroll.js.map +1 -1
  48. package/dist/checks/no-tracking.js +6 -6
  49. package/dist/checks/no-tracking.js.map +1 -1
  50. package/dist/checks/pwa-icons.d.ts +15 -0
  51. package/dist/checks/pwa-icons.d.ts.map +1 -0
  52. package/dist/checks/pwa-icons.js +38 -0
  53. package/dist/checks/pwa-icons.js.map +1 -0
  54. package/dist/checks/pwa-maskable-icon.d.ts +20 -0
  55. package/dist/checks/pwa-maskable-icon.d.ts.map +1 -0
  56. package/dist/checks/pwa-maskable-icon.js +91 -0
  57. package/dist/checks/pwa-maskable-icon.js.map +1 -0
  58. package/dist/checks/react-strict-mode.d.ts +4 -0
  59. package/dist/checks/react-strict-mode.d.ts.map +1 -0
  60. package/dist/checks/react-strict-mode.js +43 -0
  61. package/dist/checks/react-strict-mode.js.map +1 -0
  62. package/dist/checks/sdk-version.d.ts +4 -0
  63. package/dist/checks/sdk-version.d.ts.map +1 -0
  64. package/dist/checks/sdk-version.js +80 -0
  65. package/dist/checks/sdk-version.js.map +1 -0
  66. package/dist/checks/tech-versions.d.ts +4 -0
  67. package/dist/checks/tech-versions.d.ts.map +1 -0
  68. package/dist/checks/tech-versions.js +100 -0
  69. package/dist/checks/tech-versions.js.map +1 -0
  70. package/dist/checks/typescript-strict.d.ts +14 -0
  71. package/dist/checks/typescript-strict.d.ts.map +1 -0
  72. package/dist/checks/typescript-strict.js +155 -0
  73. package/dist/checks/typescript-strict.js.map +1 -0
  74. package/dist/checks/uses-game-sdk.d.ts +16 -0
  75. package/dist/checks/uses-game-sdk.d.ts.map +1 -0
  76. package/dist/checks/uses-game-sdk.js +82 -0
  77. package/dist/checks/uses-game-sdk.js.map +1 -0
  78. package/dist/checks/uses-localstorage.d.ts +14 -0
  79. package/dist/checks/uses-localstorage.d.ts.map +1 -0
  80. package/dist/checks/uses-localstorage.js +45 -0
  81. package/dist/checks/uses-localstorage.js.map +1 -0
  82. package/dist/index.d.ts +20 -1
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +46 -2
  85. package/dist/index.js.map +1 -1
  86. package/dist/lib/inline-manifest.d.ts +3 -0
  87. package/dist/lib/inline-manifest.d.ts.map +1 -1
  88. package/dist/lib/inline-manifest.js +64 -0
  89. package/dist/lib/inline-manifest.js.map +1 -1
  90. package/dist/lib/strip.js +4 -3
  91. package/dist/lib/strip.js.map +1 -1
  92. package/dist/live/index.d.ts.map +1 -1
  93. package/dist/live/index.js +9 -4
  94. package/dist/live/index.js.map +1 -1
  95. package/package.json +1 -1
@@ -0,0 +1,91 @@
1
+ import { extractInlineManifest } from '../lib/inline-manifest.js';
2
+ const STATIC_MANIFEST_PATH = 'web/public/manifest.json';
3
+ const VITE_CONFIG_PATH = 'web/vite.config.ts';
4
+ /**
5
+ * Verifies the PWA manifest declares at least one icon with
6
+ * `purpose: "maskable"` (or a combined value containing "maskable" such
7
+ * as "any maskable").
8
+ *
9
+ * Why this matters: on Android, Chrome's installability heuristics
10
+ * downgrade a PWA to a launcher *shortcut* (just a bookmark with the
11
+ * site favicon) when no maskable icon is declared. With a maskable
12
+ * icon, the same install becomes a real PWA — its own task slot, app
13
+ * drawer entry, and adaptive icon shape.
14
+ *
15
+ * Detection is intentionally lenient: we look for a `purpose:` value
16
+ * containing the substring `maskable` anywhere in the icons array.
17
+ * That catches `'maskable'`, `'any maskable'`, `"maskable any"`, and
18
+ * separate-entry styles alike.
19
+ */
20
+ export async function checkMaskableIcon(source) {
21
+ const staticRaw = await source.read(STATIC_MANIFEST_PATH);
22
+ if (staticRaw !== null) {
23
+ return validateStatic(staticRaw);
24
+ }
25
+ const viteConfig = await source.read(VITE_CONFIG_PATH);
26
+ if (viteConfig !== null && /\bVitePWA\s*\(/.test(viteConfig)) {
27
+ return validateInline(viteConfig);
28
+ }
29
+ // No manifest source found at all — the `checkManifest` rule will
30
+ // already fail loudly. Pass here so we don't double-report the same
31
+ // root cause as two failures.
32
+ return {
33
+ name: 'PWA maskable icon',
34
+ status: 'pass',
35
+ detail: 'no manifest source (already reported by PWA manifest check)',
36
+ };
37
+ }
38
+ function validateStatic(raw) {
39
+ let parsed;
40
+ try {
41
+ parsed = JSON.parse(raw);
42
+ }
43
+ catch {
44
+ return {
45
+ name: 'PWA maskable icon',
46
+ status: 'fail',
47
+ detail: `${STATIC_MANIFEST_PATH} is not valid JSON`,
48
+ };
49
+ }
50
+ const icons = Array.isArray(parsed.icons) ? parsed.icons : [];
51
+ const hasMaskable = icons.some((icon) => typeof icon?.purpose === 'string' && /\bmaskable\b/.test(icon.purpose));
52
+ if (hasMaskable) {
53
+ return { name: 'PWA maskable icon', status: 'pass', detail: STATIC_MANIFEST_PATH };
54
+ }
55
+ return buildFail(STATIC_MANIFEST_PATH);
56
+ }
57
+ function validateInline(viteConfig) {
58
+ const inline = extractInlineManifest(viteConfig);
59
+ if (!inline || !inline.iconsRaw) {
60
+ return {
61
+ name: 'PWA maskable icon',
62
+ status: 'fail',
63
+ detail: `inline manifest in ${VITE_CONFIG_PATH} has no parsable icons array`,
64
+ suggestions: [
65
+ 'Add an icons array to the manifest:{...} block in VitePWA({...}).',
66
+ ],
67
+ };
68
+ }
69
+ // Lenient string-level check — see the doc-comment on `checkMaskableIcon`.
70
+ const hasMaskable = /purpose\s*:\s*['"`][^'"`]*\bmaskable\b[^'"`]*['"`]/.test(inline.iconsRaw);
71
+ if (hasMaskable) {
72
+ return {
73
+ name: 'PWA maskable icon',
74
+ status: 'pass',
75
+ detail: `inline manifest in ${VITE_CONFIG_PATH}`,
76
+ };
77
+ }
78
+ return buildFail(`inline manifest in ${VITE_CONFIG_PATH}`);
79
+ }
80
+ function buildFail(where) {
81
+ return {
82
+ name: 'PWA maskable icon',
83
+ status: 'fail',
84
+ detail: `no icon declares purpose containing "maskable" (${where})`,
85
+ suggestions: [
86
+ "Add `purpose: 'any maskable'` to each icon entry (or a dedicated 512×512 icon with `purpose: 'maskable'`).",
87
+ 'Without it, Android Chrome installs the app as a launcher shortcut, not a real PWA.',
88
+ ],
89
+ };
90
+ }
91
+ //# sourceMappingURL=pwa-maskable-icon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pwa-maskable-icon.js","sourceRoot":"","sources":["../../src/checks/pwa-maskable-icon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { FileSource } from '../lib/file-source.js';
2
+ import type { CheckResult } from '../types.js';
3
+ export declare function checkReactStrictMode(source: FileSource): Promise<CheckResult>;
4
+ //# sourceMappingURL=react-strict-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-strict-mode.d.ts","sourceRoot":"","sources":["../../src/checks/react-strict-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAkB/C,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CA8BnF"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Checks that the React app renders inside `<React.StrictMode>` or
3
+ * `<StrictMode>`. StrictMode catches common bugs: double renders in
4
+ * development, deprecated API usage, and side effects in the render
5
+ * phase.
6
+ *
7
+ * Reads `web/src/main.tsx` (or `web/src/main.ts`). If neither exists,
8
+ * the project is not a React app — pass silently.
9
+ *
10
+ * WARN level — StrictMode is a best practice, not a hard requirement.
11
+ */
12
+ const MAIN_PATHS = ['web/src/main.tsx', 'web/src/main.ts'];
13
+ const STRICT_MODE_RE = /<(?:React\.)?StrictMode\s*>/;
14
+ export async function checkReactStrictMode(source) {
15
+ for (const path of MAIN_PATHS) {
16
+ const content = await source.read(path);
17
+ if (content === null)
18
+ continue;
19
+ if (STRICT_MODE_RE.test(content)) {
20
+ return {
21
+ name: 'React StrictMode',
22
+ status: 'pass',
23
+ detail: `${path} wraps the app in <StrictMode>`,
24
+ };
25
+ }
26
+ return {
27
+ name: 'React StrictMode',
28
+ status: 'warn',
29
+ detail: `${path} does not use <React.StrictMode> or <StrictMode>`,
30
+ suggestions: [
31
+ 'Wrap the root component in <React.StrictMode> to catch common React bugs during development.',
32
+ 'Example: createRoot(el).render(<React.StrictMode><App /></React.StrictMode>)',
33
+ ],
34
+ };
35
+ }
36
+ // Neither main.tsx nor main.ts exists — not a React project.
37
+ return {
38
+ name: 'React StrictMode',
39
+ status: 'pass',
40
+ detail: 'no web/src/main.tsx found (not a React project)',
41
+ };
42
+ }
43
+ //# sourceMappingURL=react-strict-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-strict-mode.js","sourceRoot":"","sources":["../../src/checks/react-strict-mode.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,MAAM,UAAU,GAAG,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,CAAC;AAE3D,MAAM,cAAc,GAAG,6BAA6B,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAkB;IAC3D,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,OAAO,KAAK,IAAI;YAAE,SAAS;QAE/B,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACjC,OAAO;gBACL,IAAI,EAAE,kBAAkB;gBACxB,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,GAAG,IAAI,gCAAgC;aAChD,CAAC;QACJ,CAAC;QAED,OAAO;YACL,IAAI,EAAE,kBAAkB;YACxB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,IAAI,kDAAkD;YACjE,WAAW,EAAE;gBACX,8FAA8F;gBAC9F,8EAA8E;aAC/E;SACF,CAAC;IACJ,CAAC;IAED,6DAA6D;IAC7D,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,iDAAiD;KAC1D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FileSource } from '../lib/file-source.js';
2
+ import type { CheckResult } from '../types.js';
3
+ export declare function checkSdkVersion(source: FileSource): Promise<CheckResult>;
4
+ //# sourceMappingURL=sdk-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-version.d.ts","sourceRoot":"","sources":["../../src/checks/sdk-version.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA0B/C,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CA8D9E"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Checks that `@freegamestore/games` is present in `web/package.json`
3
+ * and meets the minimum version (^0.14.0). Older SDK versions lack
4
+ * newer components (GameOverScreen, GameModal, etc.) and platform
5
+ * integration hooks.
6
+ *
7
+ * If the dependency is entirely absent, this check passes with a
8
+ * "checked elsewhere" note — the `uses-game-sdk` check covers that
9
+ * case. This check only fires when the SDK IS present but outdated.
10
+ */
11
+ const SDK_PKG = '@freegamestore/games';
12
+ const MIN_MINOR = 14; // 0.14.0
13
+ /**
14
+ * Parse the minor version from a ^0.x.y range. Returns null if the
15
+ * range can't be trivially parsed or the major isn't 0.
16
+ */
17
+ function parseMinor(range) {
18
+ const cleaned = range.replace(/^[\^~>=<\s]+/, '');
19
+ const match = cleaned.match(/^0\.(\d+)/);
20
+ return match ? parseInt(match[1], 10) : null;
21
+ }
22
+ export async function checkSdkVersion(source) {
23
+ const raw = await source.read('web/package.json');
24
+ if (!raw) {
25
+ return {
26
+ name: 'Games SDK version',
27
+ status: 'pass',
28
+ detail: 'web/package.json not found — deferring to uses-game-sdk check',
29
+ };
30
+ }
31
+ let pkg;
32
+ try {
33
+ pkg = JSON.parse(raw);
34
+ }
35
+ catch {
36
+ return {
37
+ name: 'Games SDK version',
38
+ status: 'warn',
39
+ detail: 'web/package.json is not valid JSON',
40
+ };
41
+ }
42
+ const deps = {
43
+ ...(typeof pkg.dependencies === 'object' && pkg.dependencies !== null ? pkg.dependencies : {}),
44
+ ...(typeof pkg.devDependencies === 'object' && pkg.devDependencies !== null ? pkg.devDependencies : {}),
45
+ };
46
+ const range = deps[SDK_PKG];
47
+ if (!range) {
48
+ return {
49
+ name: 'Games SDK version',
50
+ status: 'pass',
51
+ detail: `${SDK_PKG} not in dependencies — deferring to uses-game-sdk check`,
52
+ };
53
+ }
54
+ const minor = parseMinor(range);
55
+ if (minor === null) {
56
+ // Can't parse — might be `latest`, `*`, a git URL, etc. Don't block.
57
+ return {
58
+ name: 'Games SDK version',
59
+ status: 'pass',
60
+ detail: `${SDK_PKG}: ${range} (unparseable range — skipping version check)`,
61
+ };
62
+ }
63
+ if (minor >= MIN_MINOR) {
64
+ return {
65
+ name: 'Games SDK version',
66
+ status: 'pass',
67
+ detail: `${SDK_PKG}: ${range}`,
68
+ };
69
+ }
70
+ return {
71
+ name: 'Games SDK version',
72
+ status: 'warn',
73
+ detail: `${SDK_PKG}: ${range} — minimum recommended is ^0.${MIN_MINOR}.0`,
74
+ suggestions: [
75
+ `Run \`npm install ${SDK_PKG}@latest\` to get the newest SDK with GameOverScreen, GameModal, and other platform components.`,
76
+ 'See the SDK changelog for what shipped since your current version.',
77
+ ],
78
+ };
79
+ }
80
+ //# sourceMappingURL=sdk-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-version.js","sourceRoot":"","sources":["../../src/checks/sdk-version.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AAEH,MAAM,OAAO,GAAG,sBAAsB,CAAC;AACvC,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC,SAAS;AAE/B;;;GAGG;AACH,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAkB;IACtD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAClD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO;YACL,IAAI,EAAE,mBAAmB;YACzB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,+DAA+D;SACxE,CAAC;IACJ,CAAC;IAED,IAAI,GAA4B,CAAC;IACjC,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,IAAI,EAAE,mBAAmB;YACzB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,oCAAoC;SAC7C,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG;QACX,GAAG,CAAC,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,GAAG,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9F,GAAG,CAAC,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,IAAI,GAAG,CAAC,eAAe,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9E,CAAC;IAE5B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,IAAI,EAAE,mBAAmB;YACzB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,OAAO,yDAAyD;SAC5E,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,qEAAqE;QACrE,OAAO;YACL,IAAI,EAAE,mBAAmB;YACzB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,OAAO,KAAK,KAAK,+CAA+C;SAC5E,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,IAAI,EAAE,mBAAmB;YACzB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,OAAO,KAAK,KAAK,EAAE;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,GAAG,OAAO,KAAK,KAAK,gCAAgC,SAAS,IAAI;QACzE,WAAW,EAAE;YACX,qBAAqB,OAAO,gGAAgG;YAC5H,oEAAoE;SACrE;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FileSource } from '../lib/file-source.js';
2
+ import type { CheckResult } from '../types.js';
3
+ export declare function checkTechVersions(source: FileSource): Promise<CheckResult>;
4
+ //# sourceMappingURL=tech-versions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tech-versions.d.ts","sourceRoot":"","sources":["../../src/checks/tech-versions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoD/C,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CA+EhF"}
@@ -0,0 +1,100 @@
1
+ const EXPECTATIONS = [
2
+ { packages: ['react'], label: 'React', minMajor: 19 },
3
+ { packages: ['vite'], label: 'Vite', minMajor: 6 },
4
+ { packages: ['typescript'], label: 'TypeScript', minMajor: 5, minMinor: 7 },
5
+ { packages: ['tailwindcss', '@tailwindcss/vite'], label: 'Tailwind CSS', minMajor: 4, optional: true },
6
+ ];
7
+ /**
8
+ * Extract the major (and optionally minor) version from a semver range
9
+ * string. Handles common range prefixes: ^, ~, >=, =, bare.
10
+ * Returns null for ranges that can't be trivially parsed (e.g. `*`, `latest`).
11
+ */
12
+ function parseMajorMinor(range) {
13
+ // Strip leading range operator
14
+ const cleaned = range.replace(/^[\^~>=<\s]+/, '');
15
+ const match = cleaned.match(/^(\d+)(?:\.(\d+))?/);
16
+ if (!match)
17
+ return null;
18
+ return {
19
+ major: parseInt(match[1], 10),
20
+ minor: match[2] !== undefined ? parseInt(match[2], 10) : 0,
21
+ };
22
+ }
23
+ export async function checkTechVersions(source) {
24
+ const raw = await source.read('web/package.json');
25
+ if (!raw) {
26
+ return {
27
+ name: 'Tech stack versions',
28
+ status: 'warn',
29
+ detail: 'web/package.json not found — cannot check dependency versions',
30
+ };
31
+ }
32
+ let pkg;
33
+ try {
34
+ pkg = JSON.parse(raw);
35
+ }
36
+ catch {
37
+ return {
38
+ name: 'Tech stack versions',
39
+ status: 'warn',
40
+ detail: 'web/package.json is not valid JSON',
41
+ };
42
+ }
43
+ const deps = {
44
+ ...(typeof pkg.dependencies === 'object' && pkg.dependencies !== null ? pkg.dependencies : {}),
45
+ ...(typeof pkg.devDependencies === 'object' && pkg.devDependencies !== null ? pkg.devDependencies : {}),
46
+ };
47
+ const outdated = [];
48
+ const missing = [];
49
+ for (const exp of EXPECTATIONS) {
50
+ let found = false;
51
+ for (const pkgName of exp.packages) {
52
+ const range = deps[pkgName];
53
+ if (!range)
54
+ continue;
55
+ found = true;
56
+ const ver = parseMajorMinor(range);
57
+ if (!ver)
58
+ continue; // unparseable range — skip silently
59
+ let isOutdated = false;
60
+ if (ver.major < exp.minMajor) {
61
+ isOutdated = true;
62
+ }
63
+ else if (ver.major === exp.minMajor && exp.minMinor !== undefined && ver.minor < exp.minMinor) {
64
+ isOutdated = true;
65
+ }
66
+ if (isOutdated) {
67
+ const minStr = exp.minMinor !== undefined
68
+ ? `^${exp.minMajor}.${exp.minMinor}`
69
+ : `^${exp.minMajor}`;
70
+ outdated.push(`${exp.label}: ${range} (expected ${minStr}+)`);
71
+ }
72
+ break; // first matching package wins
73
+ }
74
+ if (!found && !exp.optional) {
75
+ missing.push(exp.label);
76
+ }
77
+ }
78
+ if (outdated.length === 0 && missing.length === 0) {
79
+ return {
80
+ name: 'Tech stack versions',
81
+ status: 'pass',
82
+ detail: 'all checked dependencies meet minimum platform versions',
83
+ };
84
+ }
85
+ const parts = [];
86
+ if (outdated.length > 0)
87
+ parts.push(`outdated: ${outdated.join(', ')}`);
88
+ if (missing.length > 0)
89
+ parts.push(`missing: ${missing.join(', ')}`);
90
+ return {
91
+ name: 'Tech stack versions',
92
+ status: 'warn',
93
+ detail: parts.join('; '),
94
+ suggestions: [
95
+ 'Run `npm outdated` and update to the latest majors listed above.',
96
+ 'The platform templates ship with the recommended versions — check template-game-3d or template-game-canvas for reference.',
97
+ ],
98
+ };
99
+ }
100
+ //# sourceMappingURL=tech-versions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tech-versions.js","sourceRoot":"","sources":["../../src/checks/tech-versions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { FileSource } from '../lib/file-source.js';
2
+ import type { CheckResult } from '../types.js';
3
+ /**
4
+ * Every game must compile under TypeScript strict mode. This check reads
5
+ * `web/tsconfig.json` and `web/tsconfig.app.json`, follows `extends`
6
+ * references (one level deep), and verifies that `"strict": true` is set
7
+ * — either directly or via the extended base config.
8
+ *
9
+ * The individual strict flags (`strictNullChecks`, `noImplicitAny`, etc.)
10
+ * are also accepted as a collective substitute for `"strict": true`, but
11
+ * only if all of them are present.
12
+ */
13
+ export declare function checkTypescriptStrict(source: FileSource): Promise<CheckResult>;
14
+ //# sourceMappingURL=typescript-strict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript-strict.d.ts","sourceRoot":"","sources":["../../src/checks/typescript-strict.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CA2GpF"}
@@ -0,0 +1,155 @@
1
+ const TSCONFIG_PATHS = ['web/tsconfig.json', 'web/tsconfig.app.json'];
2
+ /**
3
+ * Every game must compile under TypeScript strict mode. This check reads
4
+ * `web/tsconfig.json` and `web/tsconfig.app.json`, follows `extends`
5
+ * references (one level deep), and verifies that `"strict": true` is set
6
+ * — either directly or via the extended base config.
7
+ *
8
+ * The individual strict flags (`strictNullChecks`, `noImplicitAny`, etc.)
9
+ * are also accepted as a collective substitute for `"strict": true`, but
10
+ * only if all of them are present.
11
+ */
12
+ export async function checkTypescriptStrict(source) {
13
+ // Try each candidate config in order. The first one that exists and
14
+ // has strict mode wins. tsconfig.app.json typically extends
15
+ // tsconfig.json, so check it first (it's the more specific one Vite
16
+ // actually uses).
17
+ for (const path of TSCONFIG_PATHS) {
18
+ const raw = await source.read(path);
19
+ if (raw === null)
20
+ continue;
21
+ const config = tryParseJson(raw);
22
+ if (config === null) {
23
+ return {
24
+ name: 'TypeScript strict mode',
25
+ status: 'fail',
26
+ detail: `${path} is not valid JSON`,
27
+ suggestions: ['Fix the JSON syntax errors in the tsconfig file.'],
28
+ };
29
+ }
30
+ const compilerOptions = config.compilerOptions;
31
+ // Solution-style tsconfig (only `references`, no `compilerOptions`).
32
+ // Skip to the next candidate — the actual config lives in the
33
+ // referenced files (typically tsconfig.app.json).
34
+ if (!compilerOptions && config.references) {
35
+ continue;
36
+ }
37
+ // Direct "strict": true in this file.
38
+ if (compilerOptions?.strict === true) {
39
+ return {
40
+ name: 'TypeScript strict mode',
41
+ status: 'pass',
42
+ detail: `${path} has "strict": true`,
43
+ };
44
+ }
45
+ // Check if all individual strict flags are enabled.
46
+ if (hasAllStrictFlags(compilerOptions)) {
47
+ return {
48
+ name: 'TypeScript strict mode',
49
+ status: 'pass',
50
+ detail: `${path} enables all individual strict flags`,
51
+ };
52
+ }
53
+ // Follow "extends" one level deep.
54
+ const extendsPath = config.extends;
55
+ if (typeof extendsPath === 'string') {
56
+ const basePath = resolveExtends(path, extendsPath);
57
+ const baseRaw = await source.read(basePath);
58
+ if (baseRaw !== null) {
59
+ const baseConfig = tryParseJson(baseRaw);
60
+ if (baseConfig !== null) {
61
+ const baseCompilerOptions = baseConfig.compilerOptions;
62
+ if (baseCompilerOptions?.strict === true) {
63
+ // Base has strict, but check if this config explicitly overrides it.
64
+ if (compilerOptions?.strict === false) {
65
+ return {
66
+ name: 'TypeScript strict mode',
67
+ status: 'fail',
68
+ detail: `${path} extends ${basePath} which has "strict": true, but ${path} overrides it with "strict": false`,
69
+ suggestions: ['Remove the `"strict": false` override from compilerOptions.'],
70
+ };
71
+ }
72
+ return {
73
+ name: 'TypeScript strict mode',
74
+ status: 'pass',
75
+ detail: `${path} inherits "strict": true from ${basePath}`,
76
+ };
77
+ }
78
+ if (hasAllStrictFlags(baseCompilerOptions)) {
79
+ return {
80
+ name: 'TypeScript strict mode',
81
+ status: 'pass',
82
+ detail: `${path} inherits all strict flags from ${basePath}`,
83
+ };
84
+ }
85
+ }
86
+ }
87
+ }
88
+ // Found a tsconfig but strict is not enabled.
89
+ return {
90
+ name: 'TypeScript strict mode',
91
+ status: 'fail',
92
+ detail: `${path} does not enable strict mode`,
93
+ suggestions: [
94
+ 'Add `"strict": true` to compilerOptions in your tsconfig.',
95
+ 'Strict mode catches null/undefined errors, implicit any types, and other common bugs at compile time.',
96
+ ],
97
+ };
98
+ }
99
+ // No tsconfig found at all.
100
+ return {
101
+ name: 'TypeScript strict mode',
102
+ status: 'fail',
103
+ detail: 'no web/tsconfig.json or web/tsconfig.app.json found',
104
+ suggestions: [
105
+ 'Create a tsconfig.json in web/ with `"strict": true` in compilerOptions.',
106
+ 'The game template includes a pre-configured tsconfig — run `fgs create` to scaffold one.',
107
+ ],
108
+ };
109
+ }
110
+ /** The individual flags that `"strict": true` enables. */
111
+ const STRICT_FLAGS = [
112
+ 'strictNullChecks',
113
+ 'strictFunctionTypes',
114
+ 'strictBindCallApply',
115
+ 'strictPropertyInitialization',
116
+ 'noImplicitAny',
117
+ 'noImplicitThis',
118
+ 'alwaysStrict',
119
+ ];
120
+ function hasAllStrictFlags(compilerOptions) {
121
+ if (!compilerOptions)
122
+ return false;
123
+ return STRICT_FLAGS.every((flag) => compilerOptions[flag] === true);
124
+ }
125
+ /**
126
+ * Resolve an `extends` path relative to the directory of the tsconfig
127
+ * that contains it. Handles relative paths and bare package names that
128
+ * point into node_modules.
129
+ */
130
+ function resolveExtends(tsconfigPath, extendsValue) {
131
+ const dir = tsconfigPath.slice(0, tsconfigPath.lastIndexOf('/') + 1);
132
+ if (extendsValue.startsWith('.')) {
133
+ // Relative path — resolve against the tsconfig's directory.
134
+ const combined = dir + extendsValue;
135
+ // Append .json if not already present (tsc resolves it automatically).
136
+ return combined.endsWith('.json') ? combined : `${combined}.json`;
137
+ }
138
+ // Bare specifier like "@tsconfig/strictest" — resolve into node_modules.
139
+ // Try with and without .json extension.
140
+ const candidate = `web/node_modules/${extendsValue}`;
141
+ return candidate.endsWith('.json') ? candidate : `${candidate}/tsconfig.json`;
142
+ }
143
+ function tryParseJson(raw) {
144
+ try {
145
+ // Strip JSON comments (tsconfig allows them) — simplistic removal
146
+ // of // and /* */ style comments outside strings.
147
+ const cleaned = raw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
148
+ const parsed = JSON.parse(cleaned);
149
+ return typeof parsed === 'object' && parsed !== null ? parsed : null;
150
+ }
151
+ catch {
152
+ return null;
153
+ }
154
+ }
155
+ //# sourceMappingURL=typescript-strict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript-strict.js","sourceRoot":"","sources":["../../src/checks/typescript-strict.ts"],"names":[],"mappings":"AAGA,MAAM,cAAc,GAAG,CAAC,mBAAmB,EAAE,uBAAuB,CAAC,CAAC;AAEtE;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAAkB;IAC5D,oEAAoE;IACpE,4DAA4D;IAC5D,oEAAoE;IACpE,kBAAkB;IAClB,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,IAAI;YAAE,SAAS;QAE3B,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO;gBACL,IAAI,EAAE,wBAAwB;gBAC9B,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,GAAG,IAAI,oBAAoB;gBACnC,WAAW,EAAE,CAAC,kDAAkD,CAAC;aAClE,CAAC;QACJ,CAAC;QAED,MAAM,eAAe,GAAG,MAAM,CAAC,eAAsD,CAAC;QAEtF,qEAAqE;QACrE,8DAA8D;QAC9D,kDAAkD;QAClD,IAAI,CAAC,eAAe,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YAC1C,SAAS;QACX,CAAC;QAED,sCAAsC;QACtC,IAAI,eAAe,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;YACrC,OAAO;gBACL,IAAI,EAAE,wBAAwB;gBAC9B,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,GAAG,IAAI,qBAAqB;aACrC,CAAC;QACJ,CAAC;QAED,oDAAoD;QACpD,IAAI,iBAAiB,CAAC,eAAe,CAAC,EAAE,CAAC;YACvC,OAAO;gBACL,IAAI,EAAE,wBAAwB;gBAC9B,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,GAAG,IAAI,sCAAsC;aACtD,CAAC;QACJ,CAAC;QAED,mCAAmC;QACnC,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC;QACnC,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YACnD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC5C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACrB,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;gBACzC,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;oBACxB,MAAM,mBAAmB,GAAG,UAAU,CAAC,eAE1B,CAAC;oBACd,IAAI,mBAAmB,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;wBACzC,qEAAqE;wBACrE,IAAI,eAAe,EAAE,MAAM,KAAK,KAAK,EAAE,CAAC;4BACtC,OAAO;gCACL,IAAI,EAAE,wBAAwB;gCAC9B,MAAM,EAAE,MAAM;gCACd,MAAM,EAAE,GAAG,IAAI,YAAY,QAAQ,kCAAkC,IAAI,oCAAoC;gCAC7G,WAAW,EAAE,CAAC,6DAA6D,CAAC;6BAC7E,CAAC;wBACJ,CAAC;wBACD,OAAO;4BACL,IAAI,EAAE,wBAAwB;4BAC9B,MAAM,EAAE,MAAM;4BACd,MAAM,EAAE,GAAG,IAAI,iCAAiC,QAAQ,EAAE;yBAC3D,CAAC;oBACJ,CAAC;oBAED,IAAI,iBAAiB,CAAC,mBAAmB,CAAC,EAAE,CAAC;wBAC3C,OAAO;4BACL,IAAI,EAAE,wBAAwB;4BAC9B,MAAM,EAAE,MAAM;4BACd,MAAM,EAAE,GAAG,IAAI,mCAAmC,QAAQ,EAAE;yBAC7D,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,8CAA8C;QAC9C,OAAO;YACL,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,GAAG,IAAI,8BAA8B;YAC7C,WAAW,EAAE;gBACX,2DAA2D;gBAC3D,uGAAuG;aACxG;SACF,CAAC;IACJ,CAAC;IAED,4BAA4B;IAC5B,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,qDAAqD;QAC7D,WAAW,EAAE;YACX,0EAA0E;YAC1E,0FAA0F;SAC3F;KACF,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,YAAY,GAAG;IACnB,kBAAkB;IAClB,qBAAqB;IACrB,qBAAqB;IACrB,8BAA8B;IAC9B,eAAe;IACf,gBAAgB;IAChB,cAAc;CACN,CAAC;AAEX,SAAS,iBAAiB,CAAC,eAAoD;IAC7E,IAAI,CAAC,eAAe;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,YAAoB,EAAE,YAAoB;IAChE,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACrE,IAAI,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,4DAA4D;QAC5D,MAAM,QAAQ,GAAG,GAAG,GAAG,YAAY,CAAC;QACpC,uEAAuE;QACvE,OAAO,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,OAAO,CAAC;IACpE,CAAC;IACD,yEAAyE;IACzE,wCAAwC;IACxC,MAAM,SAAS,GAAG,oBAAoB,YAAY,EAAE,CAAC;IACrD,OAAO,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,gBAAgB,CAAC;AAChF,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI,CAAC;QACH,kEAAkE;QAClE,kDAAkD;QAClD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;QAC9E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { FileSource } from '../lib/file-source.js';
2
+ import type { CheckResult } from '../types.js';
3
+ /**
4
+ * Every game on freegamestore.online must use the platform SDK's
5
+ * `GameShell` wrapper from `@freegamestore/games`. GameShell provides
6
+ * the standard chrome (back button, fullscreen, mute), lifecycle hooks,
7
+ * and platform integration (analytics, KV, rooms) that the storefront
8
+ * relies on.
9
+ *
10
+ * Two things are checked:
11
+ * 1. `@freegamestore/games` appears in `web/package.json` dependencies.
12
+ * 2. At least one source file in `web/src/` imports `GameShell` from
13
+ * the SDK.
14
+ */
15
+ export declare function checkUsesGameSdk(source: FileSource): Promise<CheckResult>;
16
+ //# sourceMappingURL=uses-game-sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uses-game-sdk.d.ts","sourceRoot":"","sources":["../../src/checks/uses-game-sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAqE/E"}