@freegamestore/compliance 0.7.0 → 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 (87) 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/react-strict-mode.d.ts +4 -0
  55. package/dist/checks/react-strict-mode.d.ts.map +1 -0
  56. package/dist/checks/react-strict-mode.js +43 -0
  57. package/dist/checks/react-strict-mode.js.map +1 -0
  58. package/dist/checks/sdk-version.d.ts +4 -0
  59. package/dist/checks/sdk-version.d.ts.map +1 -0
  60. package/dist/checks/sdk-version.js +80 -0
  61. package/dist/checks/sdk-version.js.map +1 -0
  62. package/dist/checks/tech-versions.d.ts +4 -0
  63. package/dist/checks/tech-versions.d.ts.map +1 -0
  64. package/dist/checks/tech-versions.js +100 -0
  65. package/dist/checks/tech-versions.js.map +1 -0
  66. package/dist/checks/typescript-strict.d.ts +14 -0
  67. package/dist/checks/typescript-strict.d.ts.map +1 -0
  68. package/dist/checks/typescript-strict.js +155 -0
  69. package/dist/checks/typescript-strict.js.map +1 -0
  70. package/dist/checks/uses-game-sdk.d.ts +16 -0
  71. package/dist/checks/uses-game-sdk.d.ts.map +1 -0
  72. package/dist/checks/uses-game-sdk.js +82 -0
  73. package/dist/checks/uses-game-sdk.js.map +1 -0
  74. package/dist/checks/uses-localstorage.d.ts +14 -0
  75. package/dist/checks/uses-localstorage.d.ts.map +1 -0
  76. package/dist/checks/uses-localstorage.js +45 -0
  77. package/dist/checks/uses-localstorage.js.map +1 -0
  78. package/dist/index.d.ts +19 -1
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +44 -2
  81. package/dist/index.js.map +1 -1
  82. package/dist/lib/strip.js +4 -3
  83. package/dist/lib/strip.js.map +1 -1
  84. package/dist/live/index.d.ts.map +1 -1
  85. package/dist/live/index.js +9 -4
  86. package/dist/live/index.js.map +1 -1
  87. package/package.json +1 -1
@@ -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":"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"}
@@ -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"}
@@ -0,0 +1,82 @@
1
+ import { stripCommentsForExt } from '../lib/strip.js';
2
+ const PACKAGE_JSON = 'web/package.json';
3
+ const SDK_PACKAGE = '@freegamestore/games';
4
+ const SCAN_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx']);
5
+ /**
6
+ * Every game on freegamestore.online must use the platform SDK's
7
+ * `GameShell` wrapper from `@freegamestore/games`. GameShell provides
8
+ * the standard chrome (back button, fullscreen, mute), lifecycle hooks,
9
+ * and platform integration (analytics, KV, rooms) that the storefront
10
+ * relies on.
11
+ *
12
+ * Two things are checked:
13
+ * 1. `@freegamestore/games` appears in `web/package.json` dependencies.
14
+ * 2. At least one source file in `web/src/` imports `GameShell` from
15
+ * the SDK.
16
+ */
17
+ export async function checkUsesGameSdk(source) {
18
+ // Check 1: SDK in package.json dependencies.
19
+ const pkgRaw = await source.read(PACKAGE_JSON);
20
+ let hasDependency = false;
21
+ if (pkgRaw !== null) {
22
+ try {
23
+ const pkg = JSON.parse(pkgRaw);
24
+ hasDependency =
25
+ (typeof pkg.dependencies === 'object' && SDK_PACKAGE in pkg.dependencies) ||
26
+ (typeof pkg.devDependencies === 'object' && SDK_PACKAGE in pkg.devDependencies);
27
+ }
28
+ catch {
29
+ // Invalid JSON — will fail the dependency check below.
30
+ }
31
+ }
32
+ // Check 2: GameShell import in source files under web/src/.
33
+ let hasGameShellImport = false;
34
+ for await (const path of source.list()) {
35
+ if (!path.startsWith('web/src/'))
36
+ continue;
37
+ const ext = extOf(path);
38
+ if (!SCAN_EXTS.has(ext))
39
+ continue;
40
+ const raw = await source.read(path);
41
+ if (!raw)
42
+ continue;
43
+ const content = stripCommentsForExt(raw, ext);
44
+ // Match: import { GameShell } from '@freegamestore/games'
45
+ // import { GameShell, ... } from '@freegamestore/games'
46
+ // import { ..., GameShell } from '@freegamestore/games'
47
+ if (/import\s+\{[^}]*\bGameShell\b[^}]*\}\s+from\s+['"]@freegamestore\/games['"]/i.test(content)) {
48
+ hasGameShellImport = true;
49
+ break;
50
+ }
51
+ }
52
+ if (hasDependency && hasGameShellImport) {
53
+ return {
54
+ name: 'Uses GameShell SDK',
55
+ status: 'pass',
56
+ detail: '@freegamestore/games dependency present and GameShell imported',
57
+ };
58
+ }
59
+ const issues = [];
60
+ const suggestions = [];
61
+ if (!hasDependency) {
62
+ issues.push(`${SDK_PACKAGE} not in web/package.json dependencies`);
63
+ suggestions.push(`Run \`cd web && npm install ${SDK_PACKAGE}\` to add the platform SDK.`);
64
+ }
65
+ if (!hasGameShellImport) {
66
+ issues.push('no GameShell import found in web/src/');
67
+ suggestions.push('Import and use GameShell in your entry point: `import { GameShell } from \'@freegamestore/games\';`');
68
+ suggestions.push('GameShell provides the standard game chrome (back, fullscreen, mute), lifecycle hooks, and platform integration.');
69
+ }
70
+ return {
71
+ name: 'Uses GameShell SDK',
72
+ status: 'fail',
73
+ detail: issues.join('; '),
74
+ suggestions,
75
+ };
76
+ }
77
+ function extOf(path) {
78
+ const dot = path.lastIndexOf('.');
79
+ const slash = path.lastIndexOf('/');
80
+ return dot > slash ? path.slice(dot).toLowerCase() : '';
81
+ }
82
+ //# sourceMappingURL=uses-game-sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uses-game-sdk.js","sourceRoot":"","sources":["../../src/checks/uses-game-sdk.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
+ * Games should save progress and/or high scores locally so players feel
5
+ * rewarded for returning. This check looks for `localStorage` usage or
6
+ * the `useHighScore` hook import (from the games SDK) anywhere under
7
+ * `web/src/`.
8
+ *
9
+ * WARN level — not every game needs persistence. Short-session games
10
+ * (Flappy Bird, reaction timers) are perfectly valid without saves. The
11
+ * guideline is "Saves progress/high scores locally," not a hard gate.
12
+ */
13
+ export declare function checkUsesLocalStorage(source: FileSource): Promise<CheckResult>;
14
+ //# sourceMappingURL=uses-localstorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uses-localstorage.d.ts","sourceRoot":"","sources":["../../src/checks/uses-localstorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAmCpF"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Games should save progress and/or high scores locally so players feel
3
+ * rewarded for returning. This check looks for `localStorage` usage or
4
+ * the `useHighScore` hook import (from the games SDK) anywhere under
5
+ * `web/src/`.
6
+ *
7
+ * WARN level — not every game needs persistence. Short-session games
8
+ * (Flappy Bird, reaction timers) are perfectly valid without saves. The
9
+ * guideline is "Saves progress/high scores locally," not a hard gate.
10
+ */
11
+ export async function checkUsesLocalStorage(source) {
12
+ for await (const path of source.list()) {
13
+ if (!path.startsWith('web/src/'))
14
+ continue;
15
+ const content = await source.read(path);
16
+ if (!content)
17
+ continue;
18
+ // Direct localStorage usage
19
+ if (/\blocalStorage\b/.test(content)) {
20
+ return {
21
+ name: 'Local storage for progress/scores',
22
+ status: 'pass',
23
+ detail: `localStorage usage found in ${path}`,
24
+ };
25
+ }
26
+ // SDK high-score hook
27
+ if (/\buseHighScore\b/.test(content)) {
28
+ return {
29
+ name: 'Local storage for progress/scores',
30
+ status: 'pass',
31
+ detail: `useHighScore hook found in ${path}`,
32
+ };
33
+ }
34
+ }
35
+ return {
36
+ name: 'Local storage for progress/scores',
37
+ status: 'warn',
38
+ detail: 'no localStorage usage or useHighScore hook found in web/src/',
39
+ suggestions: [
40
+ 'Consider saving high scores or progress with localStorage so returning players feel rewarded.',
41
+ 'The games SDK provides a useHighScore() hook that handles serialisation and leaderboard formatting.',
42
+ ],
43
+ };
44
+ }
45
+ //# sourceMappingURL=uses-localstorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uses-localstorage.js","sourceRoot":"","sources":["../../src/checks/uses-localstorage.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAAkB;IAC5D,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,SAAS;QAE3C,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,4BAA4B;QAC5B,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,OAAO;gBACL,IAAI,EAAE,mCAAmC;gBACzC,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,+BAA+B,IAAI,EAAE;aAC9C,CAAC;QACJ,CAAC;QAED,sBAAsB;QACtB,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,OAAO;gBACL,IAAI,EAAE,mCAAmC;gBACzC,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,8BAA8B,IAAI,EAAE;aAC7C,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,mCAAmC;QACzC,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,8DAA8D;QACtE,WAAW,EAAE;YACX,+FAA+F;YAC/F,qGAAqG;SACtG;KACF,CAAC;AACJ,CAAC"}