-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheslint.config.js
More file actions
177 lines (163 loc) · 6.35 KB
/
Copy patheslint.config.js
File metadata and controls
177 lines (163 loc) · 6.35 KB
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// ─────────────────────────────────────────────────────────────────────────────
// ALIBI — ESLint flat config
//
// This file exists mainly for ONE rule. PRD §4b:
//
// "Nothing in the proof path, the contract, or the /ledger pane may import
// from src/lib/mock-data. Mock data is permitted only for empty-state
// screenshots and /private seeding, and that boundary must be
// grep-verifiable."
//
// LAYOUT_SPEC §1 turns that promise into a check. This config turns the check
// into a build failure, so the boundary cannot rot between now and D12.
//
// src/routes/ledger/** MUST NOT import src/lib/alibi/private-state | src/lib/mock
// src/lib/alibi/** MUST NOT import src/lib/mock
// src/lib/mock MAY be imported ONLY by
// src/components/empty/** and src/routes/private/**
//
// Belt and braces: `npm run verify:boundary` greps for the same violations plus
// the MOCK_LAYER_ID marker ('ALIBI_MOCK_PREVIEW'), so a judge can verify the
// boundary without running ESLint at all.
//
// NOTE ON PATHS: LAYOUT_SPEC §1 writes the mock layer as `src/lib/mock-data`.
// The mock package itself (its README) specifies `ui/src/lib/mock`, and that is
// where it lives. Same boundary, one directory name — `mock`.
// ─────────────────────────────────────────────────────────────────────────────
import js from '@eslint/js';
import globals from 'globals';
import tseslint from 'typescript-eslint';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
/** Import specifiers that resolve into the quarantined mock layer. */
const MOCK_PATTERNS = [
'@/lib/mock',
'@/lib/mock/*',
'@/lib/mock/**',
'**/lib/mock',
'**/lib/mock/*',
'**/lib/mock/**',
];
/** Import specifiers that resolve into LOCAL (LevelDB) private state. */
const PRIVATE_STATE_PATTERNS = [
'@/lib/alibi/private-state',
'**/lib/alibi/private-state',
];
const MOCK_MESSAGE =
'PRD §4b boundary: the mock layer is quarantined. Only src/components/empty/** and src/routes/private/** may import it. ' +
'The proof path, src/lib/alibi/** and the /ledger route read real state.';
const PRIVATE_STATE_MESSAGE =
'LAYOUT_SPEC §1: /ledger is the "what a stranger sees" route. It reads LEDGER only — no LevelDB, no private state, no keys. ' +
'Importing private state here breaks the strongest claim in the product.';
export default tseslint.config(
{
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
'contract/src/managed/**',
'**/*.d.ts',
'ui/src/lib/mock/tools/**', // plain .mjs verifier, deliberately dependency-free
],
},
js.configs.recommended,
...tseslint.configs.recommended,
// ── 1. Baseline for every TypeScript file in the repo ──────────────────────
{
files: ['**/*.{ts,tsx,mts,cts}'],
languageOptions: {
ecmaVersion: 2022,
sourceType: 'module',
globals: { ...globals.node, ...globals.browser },
},
rules: {
'@typescript-eslint/no-unused-vars': [
'warn',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
// Scaffold stubs are full of intentional `any`-free TODOs; keep this a
// warning so an unfinished route never blocks a build during the demo week.
'@typescript-eslint/no-explicit-any': 'warn',
'no-console': ['warn', { allow: ['warn', 'error', 'info'] }],
},
},
// ── 2. UI: React rules ─────────────────────────────────────────────────────
{
files: ['ui/src/**/*.{ts,tsx}'],
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
languageOptions: {
globals: globals.browser,
parserOptions: { ecmaFeatures: { jsx: true } },
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
},
// ── 3. THE BOUNDARY — default: nothing in the UI may import the mock layer ─
{
files: ['ui/src/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{ patterns: [{ group: MOCK_PATTERNS, message: MOCK_MESSAGE }] },
],
},
},
// ── 4. The only three places the mock layer is legal ───────────────────────
// (PRD §4b: "empty-state screenshots and /private seeding")
{
files: [
'ui/src/components/empty/**/*.{ts,tsx}',
'ui/src/routes/private/**/*.{ts,tsx}',
'ui/src/lib/mock/**/*.{ts,tsx}',
],
rules: {
'no-restricted-imports': 'off',
},
},
// ── 5. Hardest boundary: /ledger and the proof path ────────────────────────
// Declared LAST so it wins over anything above.
{
files: ['ui/src/routes/ledger/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{ group: MOCK_PATTERNS, message: MOCK_MESSAGE },
{ group: PRIVATE_STATE_PATTERNS, message: PRIVATE_STATE_MESSAGE },
],
},
],
},
},
{
files: ['ui/src/lib/alibi/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{ patterns: [{ group: MOCK_PATTERNS, message: MOCK_MESSAGE }] },
],
},
},
// ── 6. The mock layer itself: loud, but not blocking ───────────────────────
{
files: ['ui/src/lib/mock/**/*.ts'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'no-console': 'off',
},
},
// ── 7. Contract + api workspaces (Node, no DOM) ────────────────────────────
{
files: ['contract/**/*.ts', 'api/**/*.ts', 'tools/**/*.mjs'],
languageOptions: { globals: globals.node },
rules: {
'no-console': 'off', // CLI drivers and the test harness print on purpose
},
},
);