@mclawnet/agent 0.6.20 → 0.6.22

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 (99) hide show
  1. package/cli.js +63 -0
  2. package/dist/__tests__/checkpoint.test.d.ts +2 -0
  3. package/dist/__tests__/checkpoint.test.d.ts.map +1 -0
  4. package/dist/__tests__/fs-handler-decode.test.d.ts +2 -0
  5. package/dist/__tests__/fs-handler-decode.test.d.ts.map +1 -0
  6. package/dist/__tests__/idle-sweeper.test.d.ts +2 -0
  7. package/dist/__tests__/idle-sweeper.test.d.ts.map +1 -0
  8. package/dist/__tests__/mcp-config.test.d.ts +2 -0
  9. package/dist/__tests__/mcp-config.test.d.ts.map +1 -0
  10. package/dist/__tests__/schedule-runtime-spawn.test.d.ts +2 -0
  11. package/dist/__tests__/schedule-runtime-spawn.test.d.ts.map +1 -0
  12. package/dist/__tests__/schedule-runtime.test.d.ts +2 -0
  13. package/dist/__tests__/schedule-runtime.test.d.ts.map +1 -0
  14. package/dist/__tests__/session-limit.test.d.ts +2 -0
  15. package/dist/__tests__/session-limit.test.d.ts.map +1 -0
  16. package/dist/__tests__/swarm-cli-client.test.d.ts +2 -0
  17. package/dist/__tests__/swarm-cli-client.test.d.ts.map +1 -0
  18. package/dist/__tests__/swarm-control-dispatch.test.d.ts +2 -0
  19. package/dist/__tests__/swarm-control-dispatch.test.d.ts.map +1 -0
  20. package/dist/__tests__/swarm-session-bridge.test.d.ts +2 -0
  21. package/dist/__tests__/swarm-session-bridge.test.d.ts.map +1 -0
  22. package/dist/backend-adapter.d.ts +43 -0
  23. package/dist/backend-adapter.d.ts.map +1 -1
  24. package/dist/checkpoint.d.ts +67 -0
  25. package/dist/checkpoint.d.ts.map +1 -0
  26. package/dist/{chunk-RIK7IXSW.js → chunk-WJWCYGLQ.js} +1130 -147
  27. package/dist/chunk-WJWCYGLQ.js.map +1 -0
  28. package/dist/errors.d.ts +40 -0
  29. package/dist/errors.d.ts.map +1 -0
  30. package/dist/fs-handler.d.ts.map +1 -1
  31. package/dist/hub-connection.d.ts +13 -0
  32. package/dist/hub-connection.d.ts.map +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/schedule-runtime.d.ts +125 -0
  35. package/dist/schedule-runtime.d.ts.map +1 -0
  36. package/dist/session-manager.d.ts +102 -0
  37. package/dist/session-manager.d.ts.map +1 -1
  38. package/dist/skill-loader.d.ts +20 -0
  39. package/dist/skill-loader.d.ts.map +1 -1
  40. package/dist/start.d.ts +2 -0
  41. package/dist/start.d.ts.map +1 -1
  42. package/dist/start.js +1 -1
  43. package/dist/swarm-cli-client.d.ts +24 -0
  44. package/dist/swarm-cli-client.d.ts.map +1 -0
  45. package/dist/swarm-cli-client.js +83 -0
  46. package/dist/swarm-cli-client.js.map +1 -0
  47. package/dist/swarm-control-dispatch.d.ts +47 -0
  48. package/dist/swarm-control-dispatch.d.ts.map +1 -0
  49. package/dist/swarm-session-bridge.d.ts +22 -0
  50. package/dist/swarm-session-bridge.d.ts.map +1 -0
  51. package/package.json +6 -4
  52. package/skills/cocos-creator-3x-cn/SKILL.md +475 -0
  53. package/skills/cocos-creator-3x-cn/references/framework/asset-management.md +322 -0
  54. package/skills/cocos-creator-3x-cn/references/framework/component-system.md +348 -0
  55. package/skills/cocos-creator-3x-cn/references/framework/event-patterns.md +410 -0
  56. package/skills/cocos-creator-3x-cn/references/framework/playable-optimization.md +257 -0
  57. package/skills/cocos-creator-3x-cn/references/language/performance.md +363 -0
  58. package/skills/cocos-creator-3x-cn/references/language/quality-hygiene.md +307 -0
  59. package/skills/cocos-creator-3x-cn/references/review/architecture-review.md +183 -0
  60. package/skills/cocos-creator-3x-cn/references/review/quality-review.md +251 -0
  61. package/skills/cocos-performance-optimizer/SKILL.md +214 -0
  62. package/skills/game-development/2d-games/SKILL.md +129 -0
  63. package/skills/game-development/3d-games/SKILL.md +145 -0
  64. package/skills/game-development/SKILL.md +175 -0
  65. package/skills/game-development/game-art/SKILL.md +195 -0
  66. package/skills/game-development/game-audio/SKILL.md +200 -0
  67. package/skills/game-development/game-design/SKILL.md +139 -0
  68. package/skills/game-development/mobile-games/SKILL.md +118 -0
  69. package/skills/game-development/multiplayer/SKILL.md +142 -0
  70. package/skills/game-development/pc-games/SKILL.md +154 -0
  71. package/skills/game-development/vr-ar/SKILL.md +133 -0
  72. package/skills/game-development/web-games/SKILL.md +160 -0
  73. package/skills/game-engine/SKILL.md +140 -0
  74. package/skills/game-engine/assets/2d-maze-game.md +528 -0
  75. package/skills/game-engine/assets/2d-platform-game.md +1855 -0
  76. package/skills/game-engine/assets/gameBase-template-repo.md +310 -0
  77. package/skills/game-engine/assets/paddle-game-template.md +1528 -0
  78. package/skills/game-engine/assets/simple-2d-engine.md +507 -0
  79. package/skills/game-engine/references/3d-web-games.md +754 -0
  80. package/skills/game-engine/references/algorithms.md +843 -0
  81. package/skills/game-engine/references/basics.md +343 -0
  82. package/skills/game-engine/references/game-control-mechanisms.md +617 -0
  83. package/skills/game-engine/references/game-engine-core-principles.md +695 -0
  84. package/skills/game-engine/references/game-publishing.md +352 -0
  85. package/skills/game-engine/references/techniques.md +894 -0
  86. package/skills/game-engine/references/terminology.md +354 -0
  87. package/skills/game-engine/references/web-apis.md +1394 -0
  88. package/skills/theone-cocos-standards/SKILL.md +557 -0
  89. package/skills/theone-cocos-standards/references/framework/component-system.md +645 -0
  90. package/skills/theone-cocos-standards/references/framework/event-patterns.md +433 -0
  91. package/skills/theone-cocos-standards/references/framework/playable-optimization.md +429 -0
  92. package/skills/theone-cocos-standards/references/framework/size-optimization.md +308 -0
  93. package/skills/theone-cocos-standards/references/language/modern-typescript.md +658 -0
  94. package/skills/theone-cocos-standards/references/language/performance.md +580 -0
  95. package/skills/theone-cocos-standards/references/language/quality-hygiene.md +582 -0
  96. package/skills/theone-cocos-standards/references/review/architecture-review.md +250 -0
  97. package/skills/theone-cocos-standards/references/review/performance-review.md +288 -0
  98. package/skills/theone-cocos-standards/references/review/quality-review.md +239 -0
  99. package/dist/chunk-RIK7IXSW.js.map +0 -1
@@ -0,0 +1,582 @@
1
+ # TypeScript Quality & Code Hygiene
2
+
3
+ ## Enable TypeScript Strict Mode
4
+
5
+ ```typescript
6
+ // ✅ GOOD: Enable strict mode in tsconfig.json
7
+ {
8
+ "compilerOptions": {
9
+ "strict": true,
10
+ "noImplicitAny": true,
11
+ "strictNullChecks": true,
12
+ "strictFunctionTypes": true,
13
+ "strictBindCallApply": true,
14
+ "strictPropertyInitialization": true,
15
+ "noImplicitThis": true,
16
+ "alwaysStrict": true
17
+ }
18
+ }
19
+
20
+ // Declare nullable explicitly
21
+ public playerName: string | null = null; // Can be null
22
+ public requiredName: string = ''; // Never null
23
+
24
+ // ❌ BAD: Ignoring nullability
25
+ public playerName: string; // Uninitialized, can be undefined
26
+ ```
27
+
28
+ ## Use Access Modifiers (public/private/protected)
29
+
30
+ ```typescript
31
+ // ✅ GOOD: Explicit access modifiers
32
+ import { _decorator, Component, Node } from 'cc';
33
+ const { ccclass, property } = _decorator;
34
+
35
+ @ccclass('GameService')
36
+ export class GameService extends Component {
37
+ // Private implementation details
38
+ private readonly playerNodes: Node[] = [];
39
+ private currentLevel: number = 1;
40
+
41
+ // Protected for subclass access
42
+ protected readonly maxHealth: number = 100;
43
+
44
+ // Public API only when necessary
45
+ public getCurrentLevel(): number {
46
+ return this.currentLevel;
47
+ }
48
+
49
+ // Private helper methods
50
+ private loadGameData(): void {
51
+ // Implementation
52
+ }
53
+ }
54
+
55
+ // ❌ BAD: No access modifiers (implicitly public)
56
+ @ccclass('GameService')
57
+ export class GameService extends Component {
58
+ playerNodes: Node[] = []; // Implicitly public
59
+ currentLevel: number = 1; // Implicitly public
60
+ }
61
+ ```
62
+
63
+ ## Enable ESLint with TypeScript Support
64
+
65
+ ```json
66
+ // ✅ GOOD: .eslintrc.json configuration
67
+ {
68
+ "parser": "@typescript-eslint/parser",
69
+ "extends": [
70
+ "eslint:recommended",
71
+ "plugin:@typescript-eslint/recommended"
72
+ ],
73
+ "plugins": ["@typescript-eslint"],
74
+ "rules": {
75
+ "@typescript-eslint/explicit-function-return-type": "error",
76
+ "@typescript-eslint/no-explicit-any": "error",
77
+ "@typescript-eslint/no-unused-vars": "error",
78
+ "@typescript-eslint/explicit-member-accessibility": "error"
79
+ }
80
+ }
81
+ ```
82
+
83
+ ## Throw Exceptions for Errors
84
+
85
+ **Critical Rule**: Throw exceptions instead of silent failures or returning undefined.
86
+
87
+ ```typescript
88
+ import { _decorator, Component, Node } from 'cc';
89
+ const { ccclass, property } = _decorator;
90
+
91
+ @ccclass('PlayerService')
92
+ export class PlayerService extends Component {
93
+ @property(Node)
94
+ private readonly playerNode: Node | null = null;
95
+
96
+ // ✅ EXCELLENT: Throw exception for errors
97
+ protected onLoad(): void {
98
+ if (!this.playerNode) {
99
+ throw new Error('PlayerService: playerNode is required');
100
+ }
101
+ }
102
+
103
+ public getPlayer(id: string): Player {
104
+ const player = this.players.get(id);
105
+ if (!player) {
106
+ // Throw exception, don't return undefined
107
+ throw new Error(`Player not found: ${id}`);
108
+ }
109
+ return player;
110
+ }
111
+
112
+ public loadLevel(levelId: number): void {
113
+ if (levelId < 1 || levelId > 100) {
114
+ throw new RangeError(`Invalid level ID: ${levelId}. Must be 1-100.`);
115
+ }
116
+
117
+ const levelData = this.loadLevelData(levelId);
118
+ if (!levelData) {
119
+ throw new Error(`Failed to load level data for level ${levelId}`);
120
+ }
121
+
122
+ this.initializeLevel(levelData);
123
+ }
124
+ }
125
+
126
+ // ❌ WRONG: Silent failure
127
+ public getPlayer(id: string): Player | undefined {
128
+ const player = this.players.get(id);
129
+ // Returning undefined - caller doesn't know why it failed
130
+ return player;
131
+ }
132
+
133
+ // ❌ WRONG: Logging error instead of throwing
134
+ public loadLevel(levelId: number): void {
135
+ if (levelId < 1) {
136
+ console.error('Invalid level ID'); // Don't just log
137
+ return; // Silent failure
138
+ }
139
+ }
140
+ ```
141
+
142
+ ## Logging: console.log for Development Only
143
+
144
+ **Logging Guidelines:**
145
+ - **console.log**: Use ONLY for development debugging
146
+ - **Remove in production**: Wrap in `CC_DEBUG` or remove entirely
147
+ - **Performance impact**: console.log can slow down playable ads
148
+ - **Bundle size**: Logging strings increase bundle size
149
+
150
+ ```typescript
151
+ import { _decorator, Component } from 'cc';
152
+ const { ccclass } = _decorator;
153
+
154
+ @ccclass('GameManager')
155
+ export class GameManager extends Component {
156
+ private currentScore: number = 0;
157
+
158
+ // ✅ EXCELLENT: Conditional logging for development
159
+ protected onLoad(): void {
160
+ if (CC_DEBUG) {
161
+ console.log('GameManager initialized');
162
+ }
163
+ }
164
+
165
+ public addScore(points: number): void {
166
+ this.currentScore += points;
167
+
168
+ // ✅ GOOD: Development-only debug logging
169
+ if (CC_DEBUG) {
170
+ console.log(`Score updated: ${this.currentScore}`);
171
+ }
172
+ }
173
+
174
+ private loadGameData(): void {
175
+ try {
176
+ const data = this.fetchData();
177
+ this.processData(data);
178
+ } catch (error) {
179
+ // ✅ GOOD: Log errors in development
180
+ if (CC_DEBUG) {
181
+ console.error('Failed to load game data:', error);
182
+ }
183
+ // Always throw for caller to handle
184
+ throw error;
185
+ }
186
+ }
187
+ }
188
+
189
+ // ❌ WRONG: Unconditional console.log in production
190
+ public addScore(points: number): void {
191
+ console.log(`Adding ${points} points`); // Will be in production build
192
+ this.currentScore += points;
193
+ }
194
+
195
+ // ❌ WRONG: Verbose logging everywhere
196
+ public update(dt: number): void {
197
+ console.log('Update called'); // Called every frame!
198
+ console.log(`Delta time: ${dt}`); // Performance impact
199
+ }
200
+
201
+ // ✅ BETTER: Remove logs in production or use build-time removal
202
+ // Configure build process to strip console.log in production builds
203
+ ```
204
+
205
+ **Production Build Configuration:**
206
+
207
+ ```javascript
208
+ // Build configuration to remove console.log in production
209
+ // rollup.config.js or webpack.config.js
210
+ export default {
211
+ plugins: [
212
+ // Remove console statements in production
213
+ terser({
214
+ compress: {
215
+ drop_console: true, // Remove all console.* calls
216
+ pure_funcs: ['console.log', 'console.debug'], // Remove specific calls
217
+ }
218
+ })
219
+ ]
220
+ };
221
+ ```
222
+
223
+ ## Use readonly for Immutable Fields
224
+
225
+ ```typescript
226
+ import { _decorator, Component, Node } from 'cc';
227
+ const { ccclass, property } = _decorator;
228
+
229
+ @ccclass('PlayerController')
230
+ export class PlayerController extends Component {
231
+ // ✅ GOOD: readonly for @property fields that aren't reassigned
232
+ @property(Node)
233
+ private readonly targetNode: Node | null = null;
234
+
235
+ @property(Number)
236
+ private readonly moveSpeed: number = 100;
237
+
238
+ // ✅ GOOD: readonly for injected dependencies
239
+ private readonly eventManager: EventManager;
240
+
241
+ // Regular mutable field
242
+ private currentHealth: number = 100;
243
+
244
+ constructor(eventManager: EventManager) {
245
+ super();
246
+ this.eventManager = eventManager;
247
+ }
248
+
249
+ // ❌ WRONG: Can't reassign readonly field
250
+ public setTarget(node: Node): void {
251
+ // this.targetNode = node; // Error: Cannot assign to 'targetNode' because it is a read-only property
252
+ }
253
+ }
254
+
255
+ // ❌ BAD: Mutable when shouldn't be
256
+ @ccclass('GameConfig')
257
+ export class GameConfig extends Component {
258
+ @property(Number)
259
+ private maxHealth: number = 100; // Should be readonly
260
+ }
261
+ ```
262
+
263
+ ## Use const for Constants
264
+
265
+ ```typescript
266
+ // ✅ GOOD: const for constants
267
+ const MAX_PLAYERS = 4;
268
+ const DEFAULT_PLAYER_NAME = 'Player';
269
+ const GAME_VERSION = '1.0.0';
270
+
271
+ // ✅ GOOD: Static readonly for class constants
272
+ @ccclass('GameRules')
273
+ export class GameRules extends Component {
274
+ private static readonly MAX_HEALTH: number = 100;
275
+ private static readonly MIN_LEVEL: number = 1;
276
+ private static readonly MAX_LEVEL: number = 50;
277
+
278
+ public static isValidLevel(level: number): boolean {
279
+ return level >= GameRules.MIN_LEVEL && level <= GameRules.MAX_LEVEL;
280
+ }
281
+ }
282
+
283
+ // ✅ GOOD: Enum for related constants
284
+ export enum GameState {
285
+ LOADING = 'loading',
286
+ PLAYING = 'playing',
287
+ PAUSED = 'paused',
288
+ GAME_OVER = 'game_over',
289
+ }
290
+
291
+ // ❌ BAD: let for constants
292
+ let maxPlayers = 4; // Should be const
293
+ let defaultPlayerName = 'Player'; // Should be const
294
+
295
+ // ❌ BAD: Magic numbers without constants
296
+ public checkHealth(): boolean {
297
+ return this.health > 0 && this.health <= 100; // What is 100?
298
+ }
299
+
300
+ // ✅ BETTER: Named constant
301
+ private static readonly MAX_HEALTH: number = 100;
302
+
303
+ public checkHealth(): boolean {
304
+ return this.health > 0 && this.health <= GameRules.MAX_HEALTH;
305
+ }
306
+ ```
307
+
308
+ ## No Inline Comments (Use Descriptive Names)
309
+
310
+ ```typescript
311
+ import { _decorator, Component, Node } from 'cc';
312
+ const { ccclass, property } = _decorator;
313
+
314
+ // ✅ EXCELLENT: Self-explanatory code, no inline comments
315
+ @ccclass('PlayerController')
316
+ export class PlayerController extends Component {
317
+ @property(Node)
318
+ private readonly healthBarNode: Node | null = null;
319
+
320
+ private currentHealth: number = 100;
321
+ private static readonly MAX_HEALTH: number = 100;
322
+ private static readonly CRITICAL_HEALTH_THRESHOLD: number = 20;
323
+
324
+ public takeDamage(amount: number): void {
325
+ this.currentHealth = Math.max(0, this.currentHealth - amount);
326
+ this.updateHealthBar();
327
+
328
+ if (this.isHealthCritical()) {
329
+ this.triggerLowHealthWarning();
330
+ }
331
+
332
+ if (this.isDead()) {
333
+ this.handlePlayerDeath();
334
+ }
335
+ }
336
+
337
+ private isHealthCritical(): boolean {
338
+ return this.currentHealth <= PlayerController.CRITICAL_HEALTH_THRESHOLD;
339
+ }
340
+
341
+ private isDead(): boolean {
342
+ return this.currentHealth === 0;
343
+ }
344
+
345
+ private triggerLowHealthWarning(): void {
346
+ // Implementation
347
+ }
348
+
349
+ private handlePlayerDeath(): void {
350
+ // Implementation
351
+ }
352
+
353
+ private updateHealthBar(): void {
354
+ if (!this.healthBarNode) return;
355
+
356
+ const healthPercentage = this.currentHealth / PlayerController.MAX_HEALTH;
357
+ this.healthBarNode.scale = new Vec3(healthPercentage, 1, 1);
358
+ }
359
+ }
360
+
361
+ // ❌ BAD: Inline comments explaining unclear code
362
+ @ccclass('PlayerController')
363
+ export class PlayerController extends Component {
364
+ private h: number = 100; // health
365
+
366
+ public td(a: number): void { // take damage
367
+ this.h = Math.max(0, this.h - a); // subtract damage but don't go below 0
368
+ this.uh(); // update health bar
369
+
370
+ if (this.h <= 20) { // if health is critical
371
+ this.tlhw(); // trigger low health warning
372
+ }
373
+
374
+ if (this.h === 0) { // if dead
375
+ this.hpd(); // handle player death
376
+ }
377
+ }
378
+ }
379
+
380
+ // ❌ BAD: Comments explaining what code does (should be obvious)
381
+ public addScore(points: number): void {
382
+ // Add points to current score
383
+ this.currentScore += points;
384
+
385
+ // Check if score exceeds maximum
386
+ if (this.currentScore > MAX_SCORE) {
387
+ // Set score to maximum
388
+ this.currentScore = MAX_SCORE;
389
+ }
390
+ }
391
+
392
+ // ✅ BETTER: Descriptive names make comments unnecessary
393
+ public addScore(points: number): void {
394
+ this.currentScore += points;
395
+ this.clampScoreToMaximum();
396
+ }
397
+
398
+ private clampScoreToMaximum(): void {
399
+ this.currentScore = Math.min(this.currentScore, MAX_SCORE);
400
+ }
401
+ ```
402
+
403
+ **When Comments ARE Appropriate:**
404
+
405
+ ```typescript
406
+ // ✅ GOOD: Documenting WHY, not WHAT
407
+ /**
408
+ * Calculates damage using quadratic formula to create smooth damage curve.
409
+ * Linear damage felt too harsh for new players during playtesting.
410
+ */
411
+ private calculateDamage(baseAmount: number, level: number): number {
412
+ return baseAmount * Math.pow(level, 0.8);
413
+ }
414
+
415
+ // ✅ GOOD: Documenting complex algorithms
416
+ /**
417
+ * A* pathfinding algorithm implementation.
418
+ * Uses Manhattan distance heuristic for grid-based movement.
419
+ * @see https://en.wikipedia.org/wiki/A*_search_algorithm
420
+ */
421
+ private findPath(start: Vec2, end: Vec2): Vec2[] {
422
+ // Implementation
423
+ }
424
+
425
+ // ✅ GOOD: Documenting workarounds
426
+ /**
427
+ * WORKAROUND: Cocos Creator 3.8.x has a bug where sprite atlas
428
+ * frames aren't properly loaded on first access. Accessing once
429
+ * in onLoad() ensures they're cached for later use.
430
+ * @see https://github.com/cocos/cocos-engine/issues/12345
431
+ */
432
+ protected onLoad(): void {
433
+ this.atlas?.getSpriteFrame('dummy');
434
+ }
435
+ ```
436
+
437
+ ## Proper Null/Undefined Handling
438
+
439
+ ```typescript
440
+ import { _decorator, Component, Node } from 'cc';
441
+ const { ccclass, property } = _decorator;
442
+
443
+ @ccclass('PlayerManager')
444
+ export class PlayerManager extends Component {
445
+ @property(Node)
446
+ private readonly playerNode: Node | null = null;
447
+
448
+ // ✅ EXCELLENT: Explicit validation and error handling
449
+ protected onLoad(): void {
450
+ if (!this.playerNode) {
451
+ throw new Error('PlayerManager: playerNode is required');
452
+ }
453
+ }
454
+
455
+ // ✅ GOOD: Optional chaining for safe access
456
+ public getPlayerName(): string {
457
+ return this.playerNode?.name ?? 'Unknown';
458
+ }
459
+
460
+ // ✅ GOOD: Nullish coalescing for default values
461
+ public getPlayerHealth(): number {
462
+ return this.playerNode?.getComponent(PlayerController)?.health ?? 0;
463
+ }
464
+
465
+ // ✅ GOOD: Type guard for type safety
466
+ private isValidPlayer(node: Node | null): node is Node {
467
+ return node !== null && node.getComponent(PlayerController) !== null;
468
+ }
469
+
470
+ public updatePlayer(): void {
471
+ if (this.isValidPlayer(this.playerNode)) {
472
+ // TypeScript knows playerNode is Node (not null)
473
+ const controller = this.playerNode.getComponent(PlayerController)!;
474
+ controller.update();
475
+ }
476
+ }
477
+ }
478
+
479
+ // ❌ BAD: No null checks
480
+ public updatePlayer(): void {
481
+ this.playerNode.position = new Vec3(0, 0, 0); // Can crash if null
482
+ }
483
+
484
+ // ❌ BAD: Unsafe type assertions
485
+ public getController(): PlayerController {
486
+ return this.playerNode!.getComponent(PlayerController)!; // Unsafe!
487
+ }
488
+ ```
489
+
490
+ ## Avoid `any` Type
491
+
492
+ ```typescript
493
+ // ✅ GOOD: Proper types and interfaces
494
+ interface PlayerData {
495
+ id: string;
496
+ name: string;
497
+ level: number;
498
+ health: number;
499
+ }
500
+
501
+ @ccclass('PlayerService')
502
+ export class PlayerService extends Component {
503
+ private readonly players: Map<string, PlayerData> = new Map();
504
+
505
+ public addPlayer(data: PlayerData): void {
506
+ this.players.set(data.id, data);
507
+ }
508
+
509
+ public getPlayer(id: string): PlayerData | undefined {
510
+ return this.players.get(id);
511
+ }
512
+ }
513
+
514
+ // ❌ BAD: Using any type
515
+ @ccclass('PlayerService')
516
+ export class PlayerService extends Component {
517
+ private players: any = {}; // Type safety lost
518
+
519
+ public addPlayer(data: any): void { // No type checking
520
+ this.players[data.id] = data;
521
+ }
522
+
523
+ public getPlayer(id: string): any { // Caller doesn't know structure
524
+ return this.players[id];
525
+ }
526
+ }
527
+
528
+ // ✅ GOOD: Use generics instead of any
529
+ class DataStore<T> {
530
+ private data: Map<string, T> = new Map();
531
+
532
+ public set(key: string, value: T): void {
533
+ this.data.set(key, value);
534
+ }
535
+
536
+ public get(key: string): T | undefined {
537
+ return this.data.get(key);
538
+ }
539
+ }
540
+
541
+ // ✅ GOOD: Use unknown for truly unknown types (safer than any)
542
+ function parseJSON(json: string): unknown {
543
+ return JSON.parse(json);
544
+ }
545
+
546
+ // Then validate and narrow the type
547
+ const result = parseJSON('{"name": "Player"}');
548
+ if (isPlayerData(result)) {
549
+ // result is now typed as PlayerData
550
+ console.log(result.name);
551
+ }
552
+
553
+ function isPlayerData(obj: unknown): obj is PlayerData {
554
+ return (
555
+ typeof obj === 'object' &&
556
+ obj !== null &&
557
+ 'id' in obj &&
558
+ 'name' in obj &&
559
+ 'level' in obj &&
560
+ 'health' in obj
561
+ );
562
+ }
563
+ ```
564
+
565
+ ## Summary: Quality Checklist
566
+
567
+ **Before committing code, verify:**
568
+
569
+ - [ ] TypeScript strict mode enabled in tsconfig.json
570
+ - [ ] ESLint configuration active and passing
571
+ - [ ] All class members have access modifiers (public/private/protected)
572
+ - [ ] Exceptions thrown for errors (no silent failures)
573
+ - [ ] console.log removed or wrapped in CC_DEBUG
574
+ - [ ] readonly used for non-reassigned fields
575
+ - [ ] const used for constants (not let)
576
+ - [ ] No inline comments (code is self-explanatory)
577
+ - [ ] Optional chaining (?.) for safe property access
578
+ - [ ] Nullish coalescing (??) for default values
579
+ - [ ] No `any` types without justification
580
+ - [ ] Required references validated in onLoad()
581
+
582
+ **Quality is the foundation of all other patterns. Get this right first.**