rethocker 0.0.2 → 0.1.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.
- package/README.md +379 -3
- package/bin/rethocker-native +0 -0
- package/package.json +22 -2
- package/src/actions.ts +282 -0
- package/src/daemon.ts +244 -0
- package/src/index.test.ts +63 -0
- package/src/index.ts +8 -1
- package/src/keys.ts +346 -0
- package/src/parse-key.ts +169 -0
- package/src/register-rule.ts +186 -0
- package/src/rethocker.ts +125 -0
- package/src/rule-engine.ts +101 -0
- package/src/rule-types.ts +169 -0
- package/src/scripts/debug-keys.ts +45 -0
- package/src/scripts/example.ts +74 -0
- package/src/types.ts +298 -0
- package/AGENTS.md +0 -106
- package/bun.lock +0 -26
- package/tsconfig.json +0 -29
package/src/types.ts
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
/** Default timeout between consecutive key presses in a sequence. */
|
|
4
|
+
export const DEFAULT_SEQUENCE_TIMEOUT_MS = 5000;
|
|
5
|
+
|
|
6
|
+
// ─── Modifiers ───────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
export type Modifier =
|
|
9
|
+
| "cmd"
|
|
10
|
+
| "shift"
|
|
11
|
+
| "alt"
|
|
12
|
+
| "ctrl"
|
|
13
|
+
| "fn"
|
|
14
|
+
| "leftCmd"
|
|
15
|
+
| "rightCmd"
|
|
16
|
+
| "leftShift"
|
|
17
|
+
| "rightShift"
|
|
18
|
+
| "leftAlt"
|
|
19
|
+
| "rightAlt"
|
|
20
|
+
| "leftCtrl"
|
|
21
|
+
| "rightCtrl";
|
|
22
|
+
|
|
23
|
+
// ─── Key combo ───────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export interface KeyCombo {
|
|
26
|
+
/** macOS virtual key code (e.g. 0 = A, 36 = Return, 53 = Escape) */
|
|
27
|
+
keyCode: number;
|
|
28
|
+
modifiers?: Modifier[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ─── App conditions ──────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
export interface AppCondition {
|
|
34
|
+
/** Match by bundle ID (exact), e.g. "com.apple.Terminal" */
|
|
35
|
+
bundleID?: string;
|
|
36
|
+
/** Match by app display name (prefix, case-insensitive), e.g. "Terminal" */
|
|
37
|
+
name?: string;
|
|
38
|
+
/** If true, invert the match (i.e. "not this app") */
|
|
39
|
+
invert?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface RuleConditions {
|
|
43
|
+
/**
|
|
44
|
+
* Rule fires only when one of these apps is frontmost.
|
|
45
|
+
* Items are OR-ed; omit for any app.
|
|
46
|
+
*/
|
|
47
|
+
activeApp?: AppCondition[];
|
|
48
|
+
/**
|
|
49
|
+
* Rule fires only when one of these apps is currently running.
|
|
50
|
+
* Items are OR-ed; omit to not care.
|
|
51
|
+
*/
|
|
52
|
+
runningApps?: AppCondition[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ─── Actions ─────────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
/** Eat the keypress silently */
|
|
58
|
+
export interface SuppressAction {
|
|
59
|
+
type: "suppress";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Replace the keypress with a different key combo */
|
|
63
|
+
export interface RemapAction {
|
|
64
|
+
type: "remap";
|
|
65
|
+
keyCode: number;
|
|
66
|
+
modifiers?: Modifier[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Replace the keypress with a sequence of key combos posted in order */
|
|
70
|
+
export interface RemapSequenceAction {
|
|
71
|
+
type: "remap_sequence";
|
|
72
|
+
steps: Array<{ keyCode: number; modifiers?: Modifier[] }>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Run a shell command (key is suppressed) */
|
|
76
|
+
export interface RunAction {
|
|
77
|
+
type: "run";
|
|
78
|
+
command: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Suppress the key and emit a named event on the rethocker instance.
|
|
83
|
+
* Use this to react in TypeScript without spawning a shell.
|
|
84
|
+
*/
|
|
85
|
+
export interface EmitAction {
|
|
86
|
+
type: "emit";
|
|
87
|
+
eventID: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type RuleAction =
|
|
91
|
+
| SuppressAction
|
|
92
|
+
| RemapAction
|
|
93
|
+
| RemapSequenceAction
|
|
94
|
+
| RunAction
|
|
95
|
+
| EmitAction;
|
|
96
|
+
|
|
97
|
+
// ─── Rule options ─────────────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
export interface RuleOptions {
|
|
100
|
+
/** Unique ID. Auto-generated if omitted. */
|
|
101
|
+
id?: string;
|
|
102
|
+
conditions?: RuleConditions;
|
|
103
|
+
/** If true, fire on key-up instead of key-down (only valid for suppress/emit) */
|
|
104
|
+
onKeyUp?: boolean;
|
|
105
|
+
/** Start disabled */
|
|
106
|
+
disabled?: boolean;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface SequenceOptions {
|
|
110
|
+
/** Unique ID. Auto-generated if omitted. */
|
|
111
|
+
id?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Max milliseconds between consecutive key presses in the sequence.
|
|
114
|
+
* @default DEFAULT_SEQUENCE_TIMEOUT_MS (5000)
|
|
115
|
+
*/
|
|
116
|
+
timeoutMs?: number;
|
|
117
|
+
conditions?: Pick<RuleConditions, "activeApp">;
|
|
118
|
+
/**
|
|
119
|
+
* When true, all key events that are part of the sequence are consumed —
|
|
120
|
+
* they never reach the active app. Intermediate steps and the final key are
|
|
121
|
+
* all consumed, regardless of the action type.
|
|
122
|
+
* @default false
|
|
123
|
+
*/
|
|
124
|
+
consume?: boolean;
|
|
125
|
+
/** Start disabled */
|
|
126
|
+
disabled?: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ─── Handles (returned to callers) ───────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
/** Returned by addRule() and intercept(). */
|
|
132
|
+
export interface RuleHandle {
|
|
133
|
+
readonly id: string;
|
|
134
|
+
/** Remove the rule permanently. */
|
|
135
|
+
remove(): void;
|
|
136
|
+
/** Enable the rule (no-op if already enabled). */
|
|
137
|
+
enable(): void;
|
|
138
|
+
/** Disable the rule without removing it. */
|
|
139
|
+
disable(): void;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Returned by addSequence(). Same shape as RuleHandle. */
|
|
143
|
+
export interface SequenceHandle {
|
|
144
|
+
readonly id: string;
|
|
145
|
+
remove(): void;
|
|
146
|
+
enable(): void;
|
|
147
|
+
disable(): void;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ─── Device info ─────────────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
export interface DeviceInfo {
|
|
153
|
+
/** String used in `deviceIDs` conditions, format "vendorID:productID" */
|
|
154
|
+
id: string;
|
|
155
|
+
name?: string;
|
|
156
|
+
manufacturer?: string;
|
|
157
|
+
vendorID?: number;
|
|
158
|
+
productID?: number;
|
|
159
|
+
transport?: string;
|
|
160
|
+
locationID?: number;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─── Events ──────────────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
export interface KeyEvent {
|
|
166
|
+
type: "keydown" | "keyup" | "flags";
|
|
167
|
+
keyCode: number;
|
|
168
|
+
modifiers: Modifier[];
|
|
169
|
+
/** Set when the event was matched by a rule */
|
|
170
|
+
ruleID?: string;
|
|
171
|
+
/** Set for emit-action rules */
|
|
172
|
+
eventID?: string;
|
|
173
|
+
suppressed: boolean;
|
|
174
|
+
/** Display name of the frontmost app at the time of the event */
|
|
175
|
+
app?: string;
|
|
176
|
+
/** Bundle ID of the frontmost app at the time of the event */
|
|
177
|
+
appBundleID?: string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Map of event name → tuple of listener argument types */
|
|
181
|
+
export interface RethockerEvents {
|
|
182
|
+
/** Native daemon is ready */
|
|
183
|
+
ready: [];
|
|
184
|
+
/** Every key event when listening is active */
|
|
185
|
+
key: [event: KeyEvent];
|
|
186
|
+
/** A rule with action.type="emit" fired */
|
|
187
|
+
event: [eventID: string, ruleID: string];
|
|
188
|
+
/** A sequence rule matched */
|
|
189
|
+
sequence: [ruleID: string, eventID: string | undefined];
|
|
190
|
+
/** Error from the native daemon */
|
|
191
|
+
error: [code: string, message: string];
|
|
192
|
+
/** Accessibility permission denied */
|
|
193
|
+
accessibilityDenied: [];
|
|
194
|
+
/** List of connected keyboards/keypads */
|
|
195
|
+
devices: [devices: DeviceInfo[]];
|
|
196
|
+
/** Native process exited unexpectedly */
|
|
197
|
+
exit: [code: number | null];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ─── Public instance type ─────────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
export interface RethockerInstance {
|
|
203
|
+
// Lifecycle
|
|
204
|
+
/**
|
|
205
|
+
* Await daemon readiness. The daemon starts automatically in the background
|
|
206
|
+
* when the instance is created, so this is optional. Call it explicitly if
|
|
207
|
+
* you want to handle startup errors (e.g. Accessibility permission denied).
|
|
208
|
+
*/
|
|
209
|
+
start(): Promise<void>;
|
|
210
|
+
stop(): Promise<void>;
|
|
211
|
+
readonly ready: boolean;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Subscribe to an event. Returns an unsubscribe function.
|
|
215
|
+
*
|
|
216
|
+
* Subscribing to `"key"` automatically activates the key stream from the
|
|
217
|
+
* native daemon. When the last `"key"` listener is removed (via the returned
|
|
218
|
+
* unsubscribe function), the stream is deactivated automatically — so there
|
|
219
|
+
* is no overhead when nothing is listening.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* const off = instance.on("key", (e) => console.log(e))
|
|
223
|
+
* off() // unsubscribe — stream stops if this was the last listener
|
|
224
|
+
*/
|
|
225
|
+
on<K extends keyof RethockerEvents>(
|
|
226
|
+
event: K,
|
|
227
|
+
listener: (...args: RethockerEvents[K]) => void,
|
|
228
|
+
): () => void;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Add a key interception rule.
|
|
232
|
+
* @example
|
|
233
|
+
* const rule = instance.addRule(
|
|
234
|
+
* { keyCode: 0, modifiers: ["cmd"] },
|
|
235
|
+
* { type: "suppress" },
|
|
236
|
+
* )
|
|
237
|
+
* rule.disable() // temporarily disable
|
|
238
|
+
* rule.remove() // remove permanently
|
|
239
|
+
*/
|
|
240
|
+
addRule(
|
|
241
|
+
trigger: KeyCombo,
|
|
242
|
+
action: RuleAction,
|
|
243
|
+
options?: RuleOptions,
|
|
244
|
+
): RuleHandle;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Intercept a key combo and call a TypeScript handler.
|
|
248
|
+
* Shorthand for addRule with type:"emit" + on("event").
|
|
249
|
+
* @example
|
|
250
|
+
* const rule = instance.intercept({ keyCode: 0, modifiers: ["cmd"] }, (e) => {
|
|
251
|
+
* console.log("intercepted Cmd+A")
|
|
252
|
+
* })
|
|
253
|
+
*/
|
|
254
|
+
intercept(
|
|
255
|
+
trigger: KeyCombo,
|
|
256
|
+
handler: (event: KeyEvent) => void,
|
|
257
|
+
options?: RuleOptions,
|
|
258
|
+
): RuleHandle;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Add a key sequence rule. Fires when combos are pressed in order within the timeout.
|
|
262
|
+
* @example
|
|
263
|
+
* const seq = instance.addSequence(
|
|
264
|
+
* [{ keyCode: 38, modifiers: ["ctrl"] }, { keyCode: 40, modifiers: ["ctrl"] }],
|
|
265
|
+
* { type: "emit", eventID: "leader" },
|
|
266
|
+
* )
|
|
267
|
+
*/
|
|
268
|
+
addSequence(
|
|
269
|
+
steps: KeyCombo[],
|
|
270
|
+
action: RuleAction,
|
|
271
|
+
options?: SequenceOptions,
|
|
272
|
+
): SequenceHandle;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Allow the process to exit even while the daemon is running. By default,
|
|
276
|
+
* rethocker keeps the event loop alive (so a script with only key rules
|
|
277
|
+
* doesn't exit immediately). Call `unref()` if you want process exit to be
|
|
278
|
+
* determined by your own code, not the daemon's lifetime.
|
|
279
|
+
*
|
|
280
|
+
* The native binary cleans itself up automatically when the parent process
|
|
281
|
+
* exits, so no explicit `stop()` is needed in that case.
|
|
282
|
+
*/
|
|
283
|
+
unref(): void;
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Explicitly activate the key event stream (all keypresses emitted on `"key"`).
|
|
287
|
+
* Not needed if you use `on("key", ...)` — that activates the stream automatically.
|
|
288
|
+
* Useful for temporarily pausing the stream without removing listeners.
|
|
289
|
+
*/
|
|
290
|
+
startListening(): void;
|
|
291
|
+
stopListening(): void;
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Request the list of connected keyboards/keypads.
|
|
295
|
+
* Results are delivered via the "devices" event.
|
|
296
|
+
*/
|
|
297
|
+
listDevices(): void;
|
|
298
|
+
}
|
package/AGENTS.md
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
Default to using Bun instead of Node.js.
|
|
3
|
-
|
|
4
|
-
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
5
|
-
- Use `bun test` instead of `jest` or `vitest`
|
|
6
|
-
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
7
|
-
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
8
|
-
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
9
|
-
- Use `bunx <package> <command>` instead of `npx <package> <command>`
|
|
10
|
-
- Bun automatically loads .env, so don't use dotenv.
|
|
11
|
-
|
|
12
|
-
## APIs
|
|
13
|
-
|
|
14
|
-
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
15
|
-
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
16
|
-
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
17
|
-
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
18
|
-
- `WebSocket` is built-in. Don't use `ws`.
|
|
19
|
-
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
20
|
-
- Bun.$`ls` instead of execa.
|
|
21
|
-
|
|
22
|
-
## Testing
|
|
23
|
-
|
|
24
|
-
Use `bun test` to run tests.
|
|
25
|
-
|
|
26
|
-
```ts#index.test.ts
|
|
27
|
-
import { test, expect } from "bun:test";
|
|
28
|
-
|
|
29
|
-
test("hello world", () => {
|
|
30
|
-
expect(1).toBe(1);
|
|
31
|
-
});
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Frontend
|
|
35
|
-
|
|
36
|
-
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
37
|
-
|
|
38
|
-
Server:
|
|
39
|
-
|
|
40
|
-
```ts#index.ts
|
|
41
|
-
import index from "./index.html"
|
|
42
|
-
|
|
43
|
-
Bun.serve({
|
|
44
|
-
routes: {
|
|
45
|
-
"/": index,
|
|
46
|
-
"/api/users/:id": {
|
|
47
|
-
GET: (req) => {
|
|
48
|
-
return new Response(JSON.stringify({ id: req.params.id }));
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
// optional websocket support
|
|
53
|
-
websocket: {
|
|
54
|
-
open: (ws) => {
|
|
55
|
-
ws.send("Hello, world!");
|
|
56
|
-
},
|
|
57
|
-
message: (ws, message) => {
|
|
58
|
-
ws.send(message);
|
|
59
|
-
},
|
|
60
|
-
close: (ws) => {
|
|
61
|
-
// handle close
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
development: {
|
|
65
|
-
hmr: true,
|
|
66
|
-
console: true,
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
72
|
-
|
|
73
|
-
```html#index.html
|
|
74
|
-
<html>
|
|
75
|
-
<body>
|
|
76
|
-
<h1>Hello, world!</h1>
|
|
77
|
-
<script type="module" src="./frontend.tsx"></script>
|
|
78
|
-
</body>
|
|
79
|
-
</html>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
With the following `frontend.tsx`:
|
|
83
|
-
|
|
84
|
-
```tsx#frontend.tsx
|
|
85
|
-
import React from "react";
|
|
86
|
-
import { createRoot } from "react-dom/client";
|
|
87
|
-
|
|
88
|
-
// import .css files directly and it works
|
|
89
|
-
import './index.css';
|
|
90
|
-
|
|
91
|
-
const root = createRoot(document.body);
|
|
92
|
-
|
|
93
|
-
export default function Frontend() {
|
|
94
|
-
return <h1>Hello, world!</h1>;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
root.render(<Frontend />);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Then, run index.ts
|
|
101
|
-
|
|
102
|
-
```sh
|
|
103
|
-
bun --hot ./index.ts
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
|
package/bun.lock
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"lockfileVersion": 1,
|
|
3
|
-
"configVersion": 1,
|
|
4
|
-
"workspaces": {
|
|
5
|
-
"": {
|
|
6
|
-
"name": "thock",
|
|
7
|
-
"devDependencies": {
|
|
8
|
-
"@types/bun": "latest",
|
|
9
|
-
},
|
|
10
|
-
"peerDependencies": {
|
|
11
|
-
"typescript": "^5",
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
"packages": {
|
|
16
|
-
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
|
17
|
-
|
|
18
|
-
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
|
19
|
-
|
|
20
|
-
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
|
21
|
-
|
|
22
|
-
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
23
|
-
|
|
24
|
-
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
|
25
|
-
}
|
|
26
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
// Environment setup & latest features
|
|
4
|
-
"lib": ["ESNext"],
|
|
5
|
-
"target": "ESNext",
|
|
6
|
-
"module": "Preserve",
|
|
7
|
-
"moduleDetection": "force",
|
|
8
|
-
"jsx": "react-jsx",
|
|
9
|
-
"allowJs": true,
|
|
10
|
-
|
|
11
|
-
// Bundler mode
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"noEmit": true,
|
|
16
|
-
|
|
17
|
-
// Best practices
|
|
18
|
-
"strict": true,
|
|
19
|
-
"skipLibCheck": true,
|
|
20
|
-
"noFallthroughCasesInSwitch": true,
|
|
21
|
-
"noUncheckedIndexedAccess": true,
|
|
22
|
-
"noImplicitOverride": true,
|
|
23
|
-
|
|
24
|
-
// Some stricter flags (disabled by default)
|
|
25
|
-
"noUnusedLocals": false,
|
|
26
|
-
"noUnusedParameters": false,
|
|
27
|
-
"noPropertyAccessFromIndexSignature": false
|
|
28
|
-
}
|
|
29
|
-
}
|