1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
import js from '@eslint/js'
import ts from 'typescript-eslint'
import svelte from 'eslint-plugin-svelte'
import prettier from 'eslint-config-prettier'
import globals from 'globals'
const gameplayDirectories = [
'engine/**/*.{ts,js,svelte}',
'events/**/*.{ts,js,svelte}',
'content-system/**/*.{ts,js,svelte}',
'flow/**/*.{ts,js,svelte}',
'scene/**/*.{ts,js,svelte}',
'memoir/**/*.{ts,js,svelte}',
'birth/**/*.{ts,js,svelte}',
'death/**/*.{ts,js,svelte}',
'continuation/**/*.{ts,js,svelte}',
'worldgen/**/*.{ts,js,svelte}',
'persistence/**/*.{ts,js,svelte}',
'rng/**/*.{ts,js,svelte}',
'time/**/*.{ts,js,svelte}',
'loading/**/*.{ts,js,svelte}',
'ui-lib/**/*.{ts,js,svelte}',
'routes/**/*.{ts,js,svelte}',
'utils/**/*.{ts,js,svelte}'
]
export default ts.config(
{
ignores: [
'.svelte-kit/**',
'build/**',
'dist/**',
'built/**',
'node_modules/**',
'.claude/**',
'coverage/**'
]
},
js.configs.recommended,
...ts.configs.recommended,
...svelte.configs['flat/recommended'],
prettier,
...svelte.configs['flat/prettier'],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
},
{
files: ['**/*.svelte', '**/*.svelte.ts'],
languageOptions: {
parserOptions: {
projectService: true,
extraFileExtensions: ['.svelte']
}
}
},
{
files: gameplayDirectories,
rules: {
'no-restricted-syntax': [
'error',
{
selector:
"CallExpression[callee.type='MemberExpression'][callee.object.name='Math'][callee.property.name='random']",
message:
'Math.random() is forbidden in gameplay code. Use the seeded RNG from rng/ — determinism is load-bearing (ARCHITECTURE.md §26).'
},
{
selector:
"CallExpression[callee.type='MemberExpression'][callee.object.name='crypto'][callee.property.name='getRandomValues']",
message:
'crypto.getRandomValues() is forbidden in gameplay code. Use the seeded RNG from rng/.'
},
{
selector:
"CallExpression[callee.type='MemberExpression'][callee.object.name='Date'][callee.property.name='now']",
message:
'Date.now() is forbidden in gameplay code. Use GameTime from time/ for gameplay logic. Date.now() is permitted only in tests, scripts, and outside the simulation (metadata, logging, performance measurement).'
}
],
'@typescript-eslint/no-explicit-any': 'error'
}
},
{
files: ['tests/**', 'scripts/**', '**/*.{test,spec}.ts', '**/*.test.svelte'],
rules: {
'no-restricted-syntax': 'off',
'@typescript-eslint/no-explicit-any': 'off'
}
}
)
|