@malamute/ai-rules 1.0.0 → 1.2.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 (133) hide show
  1. package/README.md +270 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/.claude/rules/conventions/documentation.md +324 -0
  4. package/configs/_shared/.claude/rules/conventions/git.md +265 -0
  5. package/configs/_shared/.claude/rules/{performance.md → conventions/performance.md} +1 -1
  6. package/configs/_shared/.claude/rules/conventions/principles.md +334 -0
  7. package/configs/_shared/.claude/rules/devops/ci-cd.md +262 -0
  8. package/configs/_shared/.claude/rules/devops/docker.md +275 -0
  9. package/configs/_shared/.claude/rules/devops/nx.md +194 -0
  10. package/configs/_shared/.claude/rules/domain/backend/api-design.md +203 -0
  11. package/configs/_shared/.claude/rules/lang/csharp/async.md +220 -0
  12. package/configs/_shared/.claude/rules/lang/csharp/csharp.md +314 -0
  13. package/configs/_shared/.claude/rules/lang/csharp/linq.md +210 -0
  14. package/configs/_shared/.claude/rules/lang/python/async.md +337 -0
  15. package/configs/_shared/.claude/rules/lang/python/celery.md +476 -0
  16. package/configs/_shared/.claude/rules/lang/python/config.md +339 -0
  17. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/database/sqlalchemy.md +6 -1
  18. package/configs/_shared/.claude/rules/lang/python/deployment.md +523 -0
  19. package/configs/_shared/.claude/rules/lang/python/error-handling.md +330 -0
  20. package/configs/_shared/.claude/rules/lang/python/migrations.md +421 -0
  21. package/configs/_shared/.claude/rules/lang/python/python.md +172 -0
  22. package/configs/_shared/.claude/rules/lang/python/repository.md +383 -0
  23. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/testing.md +2 -69
  24. package/configs/_shared/.claude/rules/lang/typescript/async.md +447 -0
  25. package/configs/_shared/.claude/rules/lang/typescript/generics.md +356 -0
  26. package/configs/_shared/.claude/rules/lang/typescript/typescript.md +212 -0
  27. package/configs/_shared/.claude/rules/quality/error-handling.md +48 -0
  28. package/configs/_shared/.claude/rules/quality/logging.md +45 -0
  29. package/configs/_shared/.claude/rules/quality/observability.md +240 -0
  30. package/configs/_shared/.claude/rules/quality/testing-patterns.md +65 -0
  31. package/configs/_shared/.claude/rules/security/secrets-management.md +222 -0
  32. package/configs/_shared/.claude/skills/analysis/explore/SKILL.md +257 -0
  33. package/configs/_shared/.claude/skills/analysis/security-audit/SKILL.md +184 -0
  34. package/configs/_shared/.claude/skills/dev/api-endpoint/SKILL.md +126 -0
  35. package/configs/_shared/.claude/{commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  36. package/configs/_shared/.claude/{commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  37. package/configs/_shared/.claude/{commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  38. package/configs/_shared/.claude/skills/infra/deploy/SKILL.md +139 -0
  39. package/configs/_shared/.claude/skills/infra/docker/SKILL.md +95 -0
  40. package/configs/_shared/.claude/skills/infra/migration/SKILL.md +158 -0
  41. package/configs/_shared/.claude/skills/nx/nx-affected/SKILL.md +72 -0
  42. package/configs/_shared/.claude/skills/nx/nx-lib/SKILL.md +375 -0
  43. package/configs/_shared/CLAUDE.md +52 -149
  44. package/configs/angular/.claude/rules/{components.md → core/components.md} +69 -15
  45. package/configs/angular/.claude/rules/core/resource.md +285 -0
  46. package/configs/angular/.claude/rules/core/signals.md +323 -0
  47. package/configs/angular/.claude/rules/http.md +338 -0
  48. package/configs/angular/.claude/rules/routing.md +291 -0
  49. package/configs/angular/.claude/rules/ssr.md +312 -0
  50. package/configs/angular/.claude/rules/state/signal-store.md +408 -0
  51. package/configs/angular/.claude/rules/{state.md → state/state.md} +2 -2
  52. package/configs/angular/.claude/rules/testing.md +7 -7
  53. package/configs/angular/.claude/rules/ui/aria.md +422 -0
  54. package/configs/angular/.claude/rules/ui/forms.md +424 -0
  55. package/configs/angular/.claude/rules/ui/pipes-directives.md +335 -0
  56. package/configs/angular/.claude/settings.json +1 -0
  57. package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +362 -0
  58. package/configs/angular/.claude/skills/signal-store/SKILL.md +445 -0
  59. package/configs/angular/CLAUDE.md +24 -216
  60. package/configs/dotnet/.claude/rules/background-services.md +552 -0
  61. package/configs/dotnet/.claude/rules/configuration.md +426 -0
  62. package/configs/dotnet/.claude/rules/ddd.md +447 -0
  63. package/configs/dotnet/.claude/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/.claude/rules/mediatr.md +320 -0
  65. package/configs/dotnet/.claude/rules/middleware.md +489 -0
  66. package/configs/dotnet/.claude/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/.claude/rules/validation.md +388 -0
  68. package/configs/dotnet/.claude/settings.json +21 -3
  69. package/configs/dotnet/CLAUDE.md +53 -286
  70. package/configs/fastapi/.claude/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/.claude/rules/dependencies.md +170 -0
  72. package/configs/{python → fastapi}/.claude/rules/fastapi.md +61 -1
  73. package/configs/fastapi/.claude/rules/lifespan.md +274 -0
  74. package/configs/fastapi/.claude/rules/middleware.md +229 -0
  75. package/configs/fastapi/.claude/rules/pydantic.md +433 -0
  76. package/configs/fastapi/.claude/rules/responses.md +251 -0
  77. package/configs/fastapi/.claude/rules/routers.md +202 -0
  78. package/configs/fastapi/.claude/rules/security.md +222 -0
  79. package/configs/fastapi/.claude/rules/testing.md +251 -0
  80. package/configs/fastapi/.claude/rules/websockets.md +298 -0
  81. package/configs/fastapi/.claude/settings.json +33 -0
  82. package/configs/fastapi/CLAUDE.md +144 -0
  83. package/configs/flask/.claude/rules/blueprints.md +208 -0
  84. package/configs/flask/.claude/rules/cli.md +285 -0
  85. package/configs/flask/.claude/rules/configuration.md +281 -0
  86. package/configs/flask/.claude/rules/context.md +238 -0
  87. package/configs/flask/.claude/rules/error-handlers.md +278 -0
  88. package/configs/flask/.claude/rules/extensions.md +278 -0
  89. package/configs/flask/.claude/rules/flask.md +171 -0
  90. package/configs/flask/.claude/rules/marshmallow.md +206 -0
  91. package/configs/flask/.claude/rules/security.md +267 -0
  92. package/configs/flask/.claude/rules/testing.md +284 -0
  93. package/configs/flask/.claude/settings.json +33 -0
  94. package/configs/flask/CLAUDE.md +166 -0
  95. package/configs/nestjs/.claude/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/.claude/rules/filters.md +376 -0
  97. package/configs/nestjs/.claude/rules/interceptors.md +317 -0
  98. package/configs/nestjs/.claude/rules/middleware.md +321 -0
  99. package/configs/nestjs/.claude/rules/modules.md +26 -0
  100. package/configs/nestjs/.claude/rules/pipes.md +351 -0
  101. package/configs/nestjs/.claude/rules/websockets.md +451 -0
  102. package/configs/nestjs/.claude/settings.json +16 -2
  103. package/configs/nestjs/CLAUDE.md +57 -215
  104. package/configs/nextjs/.claude/rules/api-routes.md +358 -0
  105. package/configs/nextjs/.claude/rules/authentication.md +355 -0
  106. package/configs/nextjs/.claude/rules/components.md +52 -0
  107. package/configs/nextjs/.claude/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/.claude/rules/database.md +400 -0
  109. package/configs/nextjs/.claude/rules/middleware.md +303 -0
  110. package/configs/nextjs/.claude/rules/routing.md +324 -0
  111. package/configs/nextjs/.claude/rules/seo.md +350 -0
  112. package/configs/nextjs/.claude/rules/server-actions.md +353 -0
  113. package/configs/nextjs/.claude/rules/state/zustand.md +6 -6
  114. package/configs/nextjs/.claude/settings.json +5 -0
  115. package/configs/nextjs/CLAUDE.md +69 -331
  116. package/package.json +23 -9
  117. package/src/cli.js +220 -0
  118. package/src/config.js +29 -0
  119. package/src/index.js +13 -0
  120. package/src/installer.js +361 -0
  121. package/src/merge.js +116 -0
  122. package/src/tech-config.json +29 -0
  123. package/src/utils.js +96 -0
  124. package/configs/python/.claude/rules/flask.md +0 -332
  125. package/configs/python/.claude/settings.json +0 -18
  126. package/configs/python/CLAUDE.md +0 -273
  127. package/src/install.js +0 -315
  128. /package/configs/_shared/.claude/rules/{accessibility.md → domain/frontend/accessibility.md} +0 -0
  129. /package/configs/_shared/.claude/rules/{security.md → security/security.md} +0 -0
  130. /package/configs/_shared/.claude/skills/{debug → dev/debug}/SKILL.md +0 -0
  131. /package/configs/_shared/.claude/skills/{learning → dev/learning}/SKILL.md +0 -0
  132. /package/configs/_shared/.claude/skills/{spec → dev/spec}/SKILL.md +0 -0
  133. /package/configs/_shared/.claude/skills/{review → git/review}/SKILL.md +0 -0
@@ -0,0 +1,408 @@
1
+ ---
2
+ paths:
3
+ - "libs/**/data-access/**/*.ts"
4
+ - "**/*.store.ts"
5
+ - "**/+state/**/*.ts"
6
+ ---
7
+
8
+ # NgRx SignalStore
9
+
10
+ The `@ngrx/signals` package provides a modern, signal-based state management solution.
11
+
12
+ ## When to Use SignalStore vs Classic NgRx
13
+
14
+ | Use SignalStore | Use Classic NgRx |
15
+ |-----------------|------------------|
16
+ | New projects | Existing NgRx codebase |
17
+ | Feature-level state | App-wide state with DevTools |
18
+ | Simpler mental model | Complex async orchestration |
19
+ | Less boilerplate | Redux pattern familiarity |
20
+
21
+ ## Basic SignalStore
22
+
23
+ ```typescript
24
+ // stores/counter.store.ts
25
+ import { signalStore, withState, withMethods, patchState } from '@ngrx/signals';
26
+
27
+ export const CounterStore = signalStore(
28
+ withState({
29
+ count: 0,
30
+ }),
31
+ withMethods((store) => ({
32
+ increment(): void {
33
+ patchState(store, { count: store.count() + 1 });
34
+ },
35
+ decrement(): void {
36
+ patchState(store, { count: store.count() - 1 });
37
+ },
38
+ reset(): void {
39
+ patchState(store, { count: 0 });
40
+ },
41
+ })),
42
+ );
43
+ ```
44
+
45
+ ```typescript
46
+ // Usage in component
47
+ @Component({
48
+ selector: 'app-counter',
49
+ providers: [CounterStore], // Provide at component level
50
+ template: `
51
+ <p>Count: {{ store.count() }}</p>
52
+ <button (click)="store.increment()">+</button>
53
+ <button (click)="store.decrement()">-</button>
54
+ `,
55
+ })
56
+ export class CounterComponent {
57
+ protected readonly store = inject(CounterStore);
58
+ }
59
+ ```
60
+
61
+ ## SignalStore with Computed
62
+
63
+ ```typescript
64
+ import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
65
+ import { computed } from '@angular/core';
66
+
67
+ interface TodoState {
68
+ todos: Todo[];
69
+ filter: 'all' | 'active' | 'completed';
70
+ }
71
+
72
+ export const TodoStore = signalStore(
73
+ withState<TodoState>({
74
+ todos: [],
75
+ filter: 'all',
76
+ }),
77
+ withComputed(({ todos, filter }) => ({
78
+ filteredTodos: computed(() => {
79
+ const allTodos = todos();
80
+ switch (filter()) {
81
+ case 'active':
82
+ return allTodos.filter(t => !t.completed);
83
+ case 'completed':
84
+ return allTodos.filter(t => t.completed);
85
+ default:
86
+ return allTodos;
87
+ }
88
+ }),
89
+ completedCount: computed(() => todos().filter(t => t.completed).length),
90
+ activeCount: computed(() => todos().filter(t => !t.completed).length),
91
+ })),
92
+ withMethods((store) => ({
93
+ addTodo(text: string): void {
94
+ patchState(store, {
95
+ todos: [...store.todos(), { id: crypto.randomUUID(), text, completed: false }],
96
+ });
97
+ },
98
+ toggleTodo(id: string): void {
99
+ patchState(store, {
100
+ todos: store.todos().map(t =>
101
+ t.id === id ? { ...t, completed: !t.completed } : t
102
+ ),
103
+ });
104
+ },
105
+ setFilter(filter: TodoState['filter']): void {
106
+ patchState(store, { filter });
107
+ },
108
+ })),
109
+ );
110
+ ```
111
+
112
+ ## SignalStore with Entity Adapter
113
+
114
+ ```typescript
115
+ import { signalStore, withMethods, patchState } from '@ngrx/signals';
116
+ import { withEntities, addEntity, updateEntity, removeEntity, setAllEntities } from '@ngrx/signals/entities';
117
+
118
+ interface User {
119
+ id: string;
120
+ name: string;
121
+ email: string;
122
+ }
123
+
124
+ export const UserStore = signalStore(
125
+ { providedIn: 'root' }, // Singleton store
126
+ withEntities<User>(),
127
+ withMethods((store) => ({
128
+ setUsers(users: User[]): void {
129
+ patchState(store, setAllEntities(users));
130
+ },
131
+ addUser(user: User): void {
132
+ patchState(store, addEntity(user));
133
+ },
134
+ updateUser(id: string, changes: Partial<User>): void {
135
+ patchState(store, updateEntity({ id, changes }));
136
+ },
137
+ removeUser(id: string): void {
138
+ patchState(store, removeEntity(id));
139
+ },
140
+ })),
141
+ );
142
+
143
+ // Exposes: store.entities(), store.ids(), store.entityMap()
144
+ ```
145
+
146
+ ## SignalStore with Async Methods
147
+
148
+ ```typescript
149
+ import { signalStore, withState, withMethods, patchState } from '@ngrx/signals';
150
+ import { inject } from '@angular/core';
151
+ import { firstValueFrom } from 'rxjs';
152
+
153
+ interface ProductState {
154
+ products: Product[];
155
+ loading: boolean;
156
+ error: string | null;
157
+ }
158
+
159
+ export const ProductStore = signalStore(
160
+ { providedIn: 'root' },
161
+ withState<ProductState>({
162
+ products: [],
163
+ loading: false,
164
+ error: null,
165
+ }),
166
+ withMethods((store, productService = inject(ProductService)) => ({
167
+ async loadProducts(): Promise<void> {
168
+ patchState(store, { loading: true, error: null });
169
+
170
+ try {
171
+ const products = await firstValueFrom(productService.getAll());
172
+ patchState(store, { products, loading: false });
173
+ } catch (error) {
174
+ patchState(store, {
175
+ loading: false,
176
+ error: error instanceof Error ? error.message : 'Failed to load products',
177
+ });
178
+ }
179
+ },
180
+
181
+ async addProduct(data: CreateProductDto): Promise<void> {
182
+ const product = await firstValueFrom(productService.create(data));
183
+ patchState(store, { products: [...store.products(), product] });
184
+ },
185
+
186
+ async deleteProduct(id: string): Promise<void> {
187
+ await firstValueFrom(productService.delete(id));
188
+ patchState(store, {
189
+ products: store.products().filter(p => p.id !== id),
190
+ });
191
+ },
192
+ })),
193
+ );
194
+ ```
195
+
196
+ ## SignalStore with Hooks
197
+
198
+ ```typescript
199
+ import { signalStore, withState, withMethods, withHooks, patchState } from '@ngrx/signals';
200
+
201
+ export const AuthStore = signalStore(
202
+ { providedIn: 'root' },
203
+ withState({
204
+ user: null as User | null,
205
+ token: null as string | null,
206
+ initialized: false,
207
+ }),
208
+ withMethods((store, authService = inject(AuthService)) => ({
209
+ async initialize(): Promise<void> {
210
+ const token = localStorage.getItem('token');
211
+ if (token) {
212
+ try {
213
+ const user = await firstValueFrom(authService.validateToken(token));
214
+ patchState(store, { user, token, initialized: true });
215
+ } catch {
216
+ localStorage.removeItem('token');
217
+ patchState(store, { initialized: true });
218
+ }
219
+ } else {
220
+ patchState(store, { initialized: true });
221
+ }
222
+ },
223
+ logout(): void {
224
+ localStorage.removeItem('token');
225
+ patchState(store, { user: null, token: null });
226
+ },
227
+ })),
228
+ withHooks({
229
+ onInit(store) {
230
+ // Called when store is first injected
231
+ store.initialize();
232
+ },
233
+ onDestroy(store) {
234
+ // Called when store is destroyed (component-level stores)
235
+ console.log('Auth store destroyed');
236
+ },
237
+ }),
238
+ );
239
+ ```
240
+
241
+ ## SignalStore with Custom Features
242
+
243
+ Create reusable store features:
244
+
245
+ ```typescript
246
+ // features/with-loading.ts
247
+ import { signalStoreFeature, withState, withMethods, patchState } from '@ngrx/signals';
248
+
249
+ export function withLoading() {
250
+ return signalStoreFeature(
251
+ withState({
252
+ loading: false,
253
+ error: null as string | null,
254
+ }),
255
+ withMethods((store) => ({
256
+ setLoading(loading: boolean): void {
257
+ patchState(store, { loading, error: null });
258
+ },
259
+ setError(error: string): void {
260
+ patchState(store, { loading: false, error });
261
+ },
262
+ })),
263
+ );
264
+ }
265
+
266
+ // Usage
267
+ export const ProductStore = signalStore(
268
+ withLoading(), // Adds loading and error state + methods
269
+ withState({ products: [] as Product[] }),
270
+ withMethods((store) => ({
271
+ async loadProducts(): Promise<void> {
272
+ store.setLoading(true);
273
+ try {
274
+ const products = await this.fetch();
275
+ patchState(store, { products });
276
+ store.setLoading(false);
277
+ } catch (e) {
278
+ store.setError('Failed to load');
279
+ }
280
+ },
281
+ })),
282
+ );
283
+ ```
284
+
285
+ ## SignalStore with RxJS
286
+
287
+ ```typescript
288
+ import { signalStore, withMethods } from '@ngrx/signals';
289
+ import { rxMethod } from '@ngrx/signals/rxjs-interop';
290
+ import { pipe, switchMap, tap } from 'rxjs';
291
+ import { tapResponse } from '@ngrx/operators';
292
+
293
+ export const SearchStore = signalStore(
294
+ withState({
295
+ results: [] as SearchResult[],
296
+ loading: false,
297
+ }),
298
+ withMethods((store, searchService = inject(SearchService)) => ({
299
+ search: rxMethod<string>(
300
+ pipe(
301
+ debounceTime(300),
302
+ distinctUntilChanged(),
303
+ tap(() => patchState(store, { loading: true })),
304
+ switchMap((query) =>
305
+ searchService.search(query).pipe(
306
+ tapResponse({
307
+ next: (results) => patchState(store, { results, loading: false }),
308
+ error: () => patchState(store, { results: [], loading: false }),
309
+ }),
310
+ ),
311
+ ),
312
+ ),
313
+ ),
314
+ })),
315
+ );
316
+
317
+ // Usage
318
+ store.search(this.query); // Can pass signal or value
319
+ store.search(this.query$); // Can pass observable
320
+ ```
321
+
322
+ ## File Structure
323
+
324
+ ```
325
+ libs/[domain]/data-access/
326
+ src/lib/
327
+ stores/
328
+ [feature].store.ts # SignalStore definition
329
+ [feature].store.spec.ts # Store tests
330
+ features/
331
+ with-loading.ts # Reusable features
332
+ with-pagination.ts
333
+ index.ts # Public API
334
+ ```
335
+
336
+ ## Component vs Root-Level Stores
337
+
338
+ ```typescript
339
+ // Root-level store (singleton)
340
+ export const AuthStore = signalStore(
341
+ { providedIn: 'root' }, // Singleton for entire app
342
+ withState({ ... }),
343
+ );
344
+
345
+ // Component-level store (instance per component)
346
+ export const FormStore = signalStore(
347
+ // No providedIn - must be added to component providers
348
+ withState({ ... }),
349
+ );
350
+
351
+ @Component({
352
+ providers: [FormStore], // New instance for each component
353
+ })
354
+ export class MyFormComponent {
355
+ private readonly store = inject(FormStore);
356
+ }
357
+ ```
358
+
359
+ ## Anti-patterns
360
+
361
+ ```typescript
362
+ // BAD: Mutating state directly
363
+ store.users().push(newUser); // Direct mutation!
364
+
365
+ // GOOD: Use patchState with new array
366
+ patchState(store, { users: [...store.users(), newUser] });
367
+
368
+
369
+ // BAD: Complex logic in computed
370
+ withComputed(({ users }) => ({
371
+ report: computed(() => {
372
+ // Complex async or side-effect logic here
373
+ fetch('/api/report'); // WRONG!
374
+ return users();
375
+ }),
376
+ })),
377
+
378
+ // GOOD: Computed should be pure, use methods for side effects
379
+ withComputed(({ users }) => ({
380
+ userCount: computed(() => users().length), // Pure derivation
381
+ })),
382
+ withMethods((store) => ({
383
+ async generateReport(): Promise<void> { ... },
384
+ })),
385
+
386
+
387
+ // BAD: Accessing store outside Angular context
388
+ const store = new ProductStore(); // Won't work!
389
+
390
+ // GOOD: Always inject
391
+ protected readonly store = inject(ProductStore);
392
+
393
+
394
+ // BAD: Providing root store at component level
395
+ export const AuthStore = signalStore({ providedIn: 'root' }, ...);
396
+
397
+ @Component({
398
+ providers: [AuthStore], // Creates duplicate instance!
399
+ })
400
+
401
+ // GOOD: Root stores should not be in component providers
402
+ @Component({
403
+ // AuthStore is providedIn: 'root', no need to provide
404
+ })
405
+ export class MyComponent {
406
+ protected readonly auth = inject(AuthStore);
407
+ }
408
+ ```
@@ -234,8 +234,8 @@ Only in smart components (feature/):
234
234
 
235
235
  ```typescript
236
236
  // Use selectSignal for signal-based selection
237
- users = this.store.selectSignal(selectAllUsers);
238
- loading = this.store.selectSignal(selectUsersLoading);
237
+ protected readonly users = this.store.selectSignal(selectAllUsers);
238
+ protected readonly loading = this.store.selectSignal(selectUsersLoading);
239
239
 
240
240
  // Dispatch actions
241
241
  this.store.dispatch(UserActions.loadUsers());
@@ -374,10 +374,10 @@ test.describe('User Management', () => {
374
374
  import { Page, Locator } from '@playwright/test';
375
375
 
376
376
  export class UserListPage {
377
- readonly page: Page;
378
- readonly searchInput: Locator;
379
- readonly userItems: Locator;
380
- readonly addUserButton: Locator;
377
+ private readonly page: Page;
378
+ private readonly searchInput: Locator;
379
+ private readonly userItems: Locator;
380
+ private readonly addUserButton: Locator;
381
381
 
382
382
  constructor(page: Page) {
383
383
  this.page = page;
@@ -386,15 +386,15 @@ export class UserListPage {
386
386
  this.addUserButton = page.getByRole('button', { name: 'Add User' });
387
387
  }
388
388
 
389
- async goto(): Promise<void> {
389
+ public async goto(): Promise<void> {
390
390
  await this.page.goto('/users');
391
391
  }
392
392
 
393
- async searchUser(name: string): Promise<void> {
393
+ public async searchUser(name: string): Promise<void> {
394
394
  await this.searchInput.fill(name);
395
395
  }
396
396
 
397
- async selectUser(index: number): Promise<void> {
397
+ public async selectUser(index: number): Promise<void> {
398
398
  await this.userItems.nth(index).click();
399
399
  }
400
400
  }