@malamute/ai-rules 1.2.0 → 1.3.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 (134) hide show
  1. package/README.md +4 -2
  2. package/bin/cli.js +1 -1
  3. package/configs/_shared/rules/conventions/npm.md +80 -0
  4. package/configs/angular/{.claude/settings.json → settings.json} +2 -0
  5. package/configs/dotnet/{.claude/settings.json → settings.json} +2 -0
  6. package/configs/fastapi/{.claude/settings.json → settings.json} +2 -0
  7. package/configs/flask/{.claude/settings.json → settings.json} +2 -0
  8. package/configs/nestjs/{.claude/settings.json → settings.json} +2 -0
  9. package/configs/nextjs/{.claude/settings.json → settings.json} +2 -0
  10. package/package.json +2 -1
  11. package/src/cli.js +5 -7
  12. package/src/config.js +52 -18
  13. package/src/index.js +4 -13
  14. package/src/installer.js +118 -65
  15. package/src/merge.js +8 -15
  16. package/src/tech-config.json +29 -13
  17. package/src/utils.js +7 -15
  18. package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +0 -362
  19. package/configs/angular/.claude/skills/signal-store/SKILL.md +0 -445
  20. /package/configs/_shared/{.claude/rules → rules}/conventions/documentation.md +0 -0
  21. /package/configs/_shared/{.claude/rules → rules}/conventions/git.md +0 -0
  22. /package/configs/_shared/{.claude/rules → rules}/conventions/performance.md +0 -0
  23. /package/configs/_shared/{.claude/rules → rules}/conventions/principles.md +0 -0
  24. /package/configs/_shared/{.claude/rules → rules}/devops/ci-cd.md +0 -0
  25. /package/configs/_shared/{.claude/rules → rules}/devops/docker.md +0 -0
  26. /package/configs/_shared/{.claude/rules → rules}/devops/nx.md +0 -0
  27. /package/configs/_shared/{.claude/rules → rules}/domain/backend/api-design.md +0 -0
  28. /package/configs/_shared/{.claude/rules → rules}/domain/frontend/accessibility.md +0 -0
  29. /package/configs/_shared/{.claude/rules → rules}/lang/csharp/async.md +0 -0
  30. /package/configs/_shared/{.claude/rules → rules}/lang/csharp/csharp.md +0 -0
  31. /package/configs/_shared/{.claude/rules → rules}/lang/csharp/linq.md +0 -0
  32. /package/configs/_shared/{.claude/rules → rules}/lang/python/async.md +0 -0
  33. /package/configs/_shared/{.claude/rules → rules}/lang/python/celery.md +0 -0
  34. /package/configs/_shared/{.claude/rules → rules}/lang/python/config.md +0 -0
  35. /package/configs/_shared/{.claude/rules → rules}/lang/python/database/sqlalchemy.md +0 -0
  36. /package/configs/_shared/{.claude/rules → rules}/lang/python/deployment.md +0 -0
  37. /package/configs/_shared/{.claude/rules → rules}/lang/python/error-handling.md +0 -0
  38. /package/configs/_shared/{.claude/rules → rules}/lang/python/migrations.md +0 -0
  39. /package/configs/_shared/{.claude/rules → rules}/lang/python/python.md +0 -0
  40. /package/configs/_shared/{.claude/rules → rules}/lang/python/repository.md +0 -0
  41. /package/configs/_shared/{.claude/rules → rules}/lang/python/testing.md +0 -0
  42. /package/configs/_shared/{.claude/rules → rules}/lang/typescript/async.md +0 -0
  43. /package/configs/_shared/{.claude/rules → rules}/lang/typescript/generics.md +0 -0
  44. /package/configs/_shared/{.claude/rules → rules}/lang/typescript/typescript.md +0 -0
  45. /package/configs/_shared/{.claude/rules → rules}/quality/error-handling.md +0 -0
  46. /package/configs/_shared/{.claude/rules → rules}/quality/logging.md +0 -0
  47. /package/configs/_shared/{.claude/rules → rules}/quality/observability.md +0 -0
  48. /package/configs/_shared/{.claude/rules → rules}/quality/testing-patterns.md +0 -0
  49. /package/configs/_shared/{.claude/rules → rules}/security/secrets-management.md +0 -0
  50. /package/configs/_shared/{.claude/rules → rules}/security/security.md +0 -0
  51. /package/configs/_shared/{.claude/skills → skills}/analysis/explore/SKILL.md +0 -0
  52. /package/configs/_shared/{.claude/skills → skills}/analysis/security-audit/SKILL.md +0 -0
  53. /package/configs/_shared/{.claude/skills → skills}/dev/api-endpoint/SKILL.md +0 -0
  54. /package/configs/_shared/{.claude/skills → skills}/dev/debug/SKILL.md +0 -0
  55. /package/configs/_shared/{.claude/skills → skills}/dev/generate-tests/SKILL.md +0 -0
  56. /package/configs/_shared/{.claude/skills → skills}/dev/learning/SKILL.md +0 -0
  57. /package/configs/_shared/{.claude/skills → skills}/dev/spec/SKILL.md +0 -0
  58. /package/configs/_shared/{.claude/skills → skills}/git/fix-issue/SKILL.md +0 -0
  59. /package/configs/_shared/{.claude/skills → skills}/git/review/SKILL.md +0 -0
  60. /package/configs/_shared/{.claude/skills → skills}/git/review-pr/SKILL.md +0 -0
  61. /package/configs/_shared/{.claude/skills → skills}/infra/deploy/SKILL.md +0 -0
  62. /package/configs/_shared/{.claude/skills → skills}/infra/docker/SKILL.md +0 -0
  63. /package/configs/_shared/{.claude/skills → skills}/infra/migration/SKILL.md +0 -0
  64. /package/configs/_shared/{.claude/skills → skills}/nx/nx-affected/SKILL.md +0 -0
  65. /package/configs/_shared/{.claude/skills → skills}/nx/nx-lib/SKILL.md +0 -0
  66. /package/configs/angular/{.claude/rules → rules}/core/components.md +0 -0
  67. /package/configs/angular/{.claude/rules → rules}/core/resource.md +0 -0
  68. /package/configs/angular/{.claude/rules → rules}/core/signals.md +0 -0
  69. /package/configs/angular/{.claude/rules → rules}/http.md +0 -0
  70. /package/configs/angular/{.claude/rules → rules}/routing.md +0 -0
  71. /package/configs/angular/{.claude/rules → rules}/ssr.md +0 -0
  72. /package/configs/angular/{.claude/rules → rules}/state/signal-store.md +0 -0
  73. /package/configs/angular/{.claude/rules → rules}/state/state.md +0 -0
  74. /package/configs/angular/{.claude/rules → rules}/testing.md +0 -0
  75. /package/configs/angular/{.claude/rules → rules}/ui/aria.md +0 -0
  76. /package/configs/angular/{.claude/rules → rules}/ui/forms.md +0 -0
  77. /package/configs/angular/{.claude/rules → rules}/ui/pipes-directives.md +0 -0
  78. /package/configs/dotnet/{.claude/rules → rules}/api.md +0 -0
  79. /package/configs/dotnet/{.claude/rules → rules}/architecture.md +0 -0
  80. /package/configs/dotnet/{.claude/rules → rules}/background-services.md +0 -0
  81. /package/configs/dotnet/{.claude/rules → rules}/configuration.md +0 -0
  82. /package/configs/dotnet/{.claude/rules → rules}/database/efcore.md +0 -0
  83. /package/configs/dotnet/{.claude/rules → rules}/ddd.md +0 -0
  84. /package/configs/dotnet/{.claude/rules → rules}/dependency-injection.md +0 -0
  85. /package/configs/dotnet/{.claude/rules → rules}/mediatr.md +0 -0
  86. /package/configs/dotnet/{.claude/rules → rules}/middleware.md +0 -0
  87. /package/configs/dotnet/{.claude/rules → rules}/result-pattern.md +0 -0
  88. /package/configs/dotnet/{.claude/rules → rules}/testing.md +0 -0
  89. /package/configs/dotnet/{.claude/rules → rules}/validation.md +0 -0
  90. /package/configs/fastapi/{.claude/rules → rules}/background-tasks.md +0 -0
  91. /package/configs/fastapi/{.claude/rules → rules}/dependencies.md +0 -0
  92. /package/configs/fastapi/{.claude/rules → rules}/fastapi.md +0 -0
  93. /package/configs/fastapi/{.claude/rules → rules}/lifespan.md +0 -0
  94. /package/configs/fastapi/{.claude/rules → rules}/middleware.md +0 -0
  95. /package/configs/fastapi/{.claude/rules → rules}/pydantic.md +0 -0
  96. /package/configs/fastapi/{.claude/rules → rules}/responses.md +0 -0
  97. /package/configs/fastapi/{.claude/rules → rules}/routers.md +0 -0
  98. /package/configs/fastapi/{.claude/rules → rules}/security.md +0 -0
  99. /package/configs/fastapi/{.claude/rules → rules}/testing.md +0 -0
  100. /package/configs/fastapi/{.claude/rules → rules}/websockets.md +0 -0
  101. /package/configs/flask/{.claude/rules → rules}/blueprints.md +0 -0
  102. /package/configs/flask/{.claude/rules → rules}/cli.md +0 -0
  103. /package/configs/flask/{.claude/rules → rules}/configuration.md +0 -0
  104. /package/configs/flask/{.claude/rules → rules}/context.md +0 -0
  105. /package/configs/flask/{.claude/rules → rules}/error-handlers.md +0 -0
  106. /package/configs/flask/{.claude/rules → rules}/extensions.md +0 -0
  107. /package/configs/flask/{.claude/rules → rules}/flask.md +0 -0
  108. /package/configs/flask/{.claude/rules → rules}/marshmallow.md +0 -0
  109. /package/configs/flask/{.claude/rules → rules}/security.md +0 -0
  110. /package/configs/flask/{.claude/rules → rules}/testing.md +0 -0
  111. /package/configs/nestjs/{.claude/rules → rules}/auth.md +0 -0
  112. /package/configs/nestjs/{.claude/rules → rules}/common-patterns.md +0 -0
  113. /package/configs/nestjs/{.claude/rules → rules}/database/prisma.md +0 -0
  114. /package/configs/nestjs/{.claude/rules → rules}/database/typeorm.md +0 -0
  115. /package/configs/nestjs/{.claude/rules → rules}/filters.md +0 -0
  116. /package/configs/nestjs/{.claude/rules → rules}/interceptors.md +0 -0
  117. /package/configs/nestjs/{.claude/rules → rules}/middleware.md +0 -0
  118. /package/configs/nestjs/{.claude/rules → rules}/modules.md +0 -0
  119. /package/configs/nestjs/{.claude/rules → rules}/pipes.md +0 -0
  120. /package/configs/nestjs/{.claude/rules → rules}/testing.md +0 -0
  121. /package/configs/nestjs/{.claude/rules → rules}/validation.md +0 -0
  122. /package/configs/nestjs/{.claude/rules → rules}/websockets.md +0 -0
  123. /package/configs/nextjs/{.claude/rules → rules}/api-routes.md +0 -0
  124. /package/configs/nextjs/{.claude/rules → rules}/authentication.md +0 -0
  125. /package/configs/nextjs/{.claude/rules → rules}/components.md +0 -0
  126. /package/configs/nextjs/{.claude/rules → rules}/data-fetching.md +0 -0
  127. /package/configs/nextjs/{.claude/rules → rules}/database.md +0 -0
  128. /package/configs/nextjs/{.claude/rules → rules}/middleware.md +0 -0
  129. /package/configs/nextjs/{.claude/rules → rules}/routing.md +0 -0
  130. /package/configs/nextjs/{.claude/rules → rules}/seo.md +0 -0
  131. /package/configs/nextjs/{.claude/rules → rules}/server-actions.md +0 -0
  132. /package/configs/nextjs/{.claude/rules → rules}/state/redux-toolkit.md +0 -0
  133. /package/configs/nextjs/{.claude/rules → rules}/state/zustand.md +0 -0
  134. /package/configs/nextjs/{.claude/rules → rules}/testing.md +0 -0
@@ -1,445 +0,0 @@
1
- ---
2
- name: signal-store
3
- description: Generate an @ngrx/signals SignalStore with state, computed, methods, and optional entities
4
- argument-hint: <name> [--entity <EntityName>] [--root]
5
- ---
6
-
7
- # Generate NgRx SignalStore
8
-
9
- Generate a modern SignalStore using `@ngrx/signals` package.
10
-
11
- ## Syntax
12
-
13
- ```
14
- /signal-store <name> [--entity <EntityName>] [--root]
15
- ```
16
-
17
- ## Options
18
-
19
- | Option | Description |
20
- |--------|-------------|
21
- | `--entity <Name>` | Add entity management with `withEntities()` |
22
- | `--root` | Make store a singleton (`providedIn: 'root'`) |
23
-
24
- ## Examples
25
-
26
- ```bash
27
- /signal-store cart
28
- /signal-store users --entity User --root
29
- /signal-store todo --entity TodoItem
30
- /signal-store auth --root
31
- ```
32
-
33
- ## Generated Structure
34
-
35
- ```
36
- libs/<domain>/data-access/src/lib/stores/
37
- ├── <name>.store.ts # SignalStore definition
38
- ├── <name>.store.spec.ts # Store tests
39
- └── index.ts # Public API
40
- ```
41
-
42
- ## File Templates
43
-
44
- ### Basic Store (`<name>.store.ts`)
45
-
46
- ```typescript
47
- import { computed, inject } from '@angular/core';
48
- import {
49
- signalStore,
50
- withState,
51
- withComputed,
52
- withMethods,
53
- withHooks,
54
- patchState,
55
- } from '@ngrx/signals';
56
- import { <Name>Service } from '../services/<name>.service';
57
- import { firstValueFrom } from 'rxjs';
58
-
59
- // State interface
60
- interface <Name>State {
61
- items: <Item>[];
62
- selectedId: string | null;
63
- loading: boolean;
64
- error: string | null;
65
- }
66
-
67
- // Initial state
68
- const initialState: <Name>State = {
69
- items: [],
70
- selectedId: null,
71
- loading: false,
72
- error: null,
73
- };
74
-
75
- export const <Name>Store = signalStore(
76
- // { providedIn: 'root' }, // Uncomment for singleton store
77
- withState(initialState),
78
-
79
- withComputed(({ items, selectedId }) => ({
80
- selectedItem: computed(() => {
81
- const id = selectedId();
82
- return id ? items().find(item => item.id === id) ?? null : null;
83
- }),
84
- itemCount: computed(() => items().length),
85
- isEmpty: computed(() => items().length === 0),
86
- })),
87
-
88
- withMethods((store, <name>Service = inject(<Name>Service)) => ({
89
- async load(): Promise<void> {
90
- patchState(store, { loading: true, error: null });
91
-
92
- try {
93
- const items = await firstValueFrom(<name>Service.getAll());
94
- patchState(store, { items, loading: false });
95
- } catch (error) {
96
- patchState(store, {
97
- loading: false,
98
- error: error instanceof Error ? error.message : 'Failed to load',
99
- });
100
- }
101
- },
102
-
103
- async add(item: Omit<<Item>, 'id'>): Promise<void> {
104
- const created = await firstValueFrom(<name>Service.create(item));
105
- patchState(store, { items: [...store.items(), created] });
106
- },
107
-
108
- async update(id: string, changes: Partial<<Item>>): Promise<void> {
109
- const updated = await firstValueFrom(<name>Service.update(id, changes));
110
- patchState(store, {
111
- items: store.items().map(item =>
112
- item.id === id ? updated : item
113
- ),
114
- });
115
- },
116
-
117
- async remove(id: string): Promise<void> {
118
- await firstValueFrom(<name>Service.delete(id));
119
- patchState(store, {
120
- items: store.items().filter(item => item.id !== id),
121
- selectedId: store.selectedId() === id ? null : store.selectedId(),
122
- });
123
- },
124
-
125
- select(id: string | null): void {
126
- patchState(store, { selectedId: id });
127
- },
128
-
129
- clearError(): void {
130
- patchState(store, { error: null });
131
- },
132
- })),
133
-
134
- withHooks({
135
- onInit(store) {
136
- // Optionally load data on init
137
- // store.load();
138
- },
139
- }),
140
- );
141
- ```
142
-
143
- ### Entity Store (`<name>.store.ts` with `--entity`)
144
-
145
- ```typescript
146
- import { computed, inject } from '@angular/core';
147
- import {
148
- signalStore,
149
- withComputed,
150
- withMethods,
151
- withHooks,
152
- patchState,
153
- } from '@ngrx/signals';
154
- import {
155
- withEntities,
156
- setAllEntities,
157
- addEntity,
158
- updateEntity,
159
- removeEntity,
160
- } from '@ngrx/signals/entities';
161
- import { <Entity>Service } from '../services/<entity>.service';
162
- import { firstValueFrom } from 'rxjs';
163
-
164
- export interface <Entity> {
165
- id: string;
166
- name: string;
167
- // Add more properties
168
- }
169
-
170
- interface <Entity>StoreState {
171
- loading: boolean;
172
- error: string | null;
173
- filter: string;
174
- }
175
-
176
- export const <Entity>Store = signalStore(
177
- { providedIn: 'root' },
178
-
179
- // Entity adapter - provides: entities(), ids(), entityMap()
180
- withEntities<<Entity>>(),
181
-
182
- // Additional state
183
- withState<<Entity>StoreState>({
184
- loading: false,
185
- error: null,
186
- filter: '',
187
- }),
188
-
189
- withComputed(({ entities, filter }) => ({
190
- filtered<Entities>: computed(() => {
191
- const filterValue = filter().toLowerCase();
192
- if (!filterValue) return entities();
193
- return entities().filter(entity =>
194
- entity.name.toLowerCase().includes(filterValue)
195
- );
196
- }),
197
- total: computed(() => entities().length),
198
- })),
199
-
200
- withMethods((store, <entity>Service = inject(<Entity>Service)) => ({
201
- async load(): Promise<void> {
202
- patchState(store, { loading: true, error: null });
203
-
204
- try {
205
- const <entities> = await firstValueFrom(<entity>Service.getAll());
206
- patchState(store, setAllEntities(<entities>), { loading: false });
207
- } catch (error) {
208
- patchState(store, {
209
- loading: false,
210
- error: error instanceof Error ? error.message : 'Failed to load',
211
- });
212
- }
213
- },
214
-
215
- async add(data: Omit<<Entity>, 'id'>): Promise<void> {
216
- const <entity> = await firstValueFrom(<entity>Service.create(data));
217
- patchState(store, addEntity(<entity>));
218
- },
219
-
220
- async update(id: string, changes: Partial<<Entity>>): Promise<void> {
221
- const <entity> = await firstValueFrom(<entity>Service.update(id, changes));
222
- patchState(store, updateEntity({ id, changes: <entity> }));
223
- },
224
-
225
- async remove(id: string): Promise<void> {
226
- await firstValueFrom(<entity>Service.delete(id));
227
- patchState(store, removeEntity(id));
228
- },
229
-
230
- setFilter(filter: string): void {
231
- patchState(store, { filter });
232
- },
233
- })),
234
-
235
- withHooks({
236
- onInit(store) {
237
- store.load();
238
- },
239
- }),
240
- );
241
- ```
242
-
243
- ### Store Tests (`<name>.store.spec.ts`)
244
-
245
- ```typescript
246
- import { TestBed } from '@angular/core/testing';
247
- import { <Name>Store } from './<name>.store';
248
- import { <Name>Service } from '../services/<name>.service';
249
- import { of, throwError } from 'rxjs';
250
-
251
- describe('<Name>Store', () => {
252
- let store: InstanceType<typeof <Name>Store>;
253
- let <name>ServiceSpy: jasmine.SpyObj<<Name>Service>;
254
-
255
- beforeEach(() => {
256
- <name>ServiceSpy = jasmine.createSpyObj<<Name>Service>('<Name>Service', [
257
- 'getAll',
258
- 'create',
259
- 'update',
260
- 'delete',
261
- ]);
262
-
263
- TestBed.configureTestingModule({
264
- providers: [
265
- <Name>Store,
266
- { provide: <Name>Service, useValue: <name>ServiceSpy },
267
- ],
268
- });
269
-
270
- store = TestBed.inject(<Name>Store);
271
- });
272
-
273
- describe('initial state', () => {
274
- it('should have empty items', () => {
275
- expect(store.items()).toEqual([]);
276
- });
277
-
278
- it('should not be loading', () => {
279
- expect(store.loading()).toBe(false);
280
- });
281
-
282
- it('should have no error', () => {
283
- expect(store.error()).toBeNull();
284
- });
285
- });
286
-
287
- describe('load', () => {
288
- it('should set loading to true while loading', async () => {
289
- <name>ServiceSpy.getAll.and.returnValue(
290
- of([{ id: '1', name: 'Item 1' }])
291
- );
292
-
293
- const loadPromise = store.load();
294
- // Note: In real tests, you'd need to check intermediate state
295
- await loadPromise;
296
-
297
- expect(store.loading()).toBe(false);
298
- });
299
-
300
- it('should populate items on success', async () => {
301
- const items = [{ id: '1', name: 'Item 1' }];
302
- <name>ServiceSpy.getAll.and.returnValue(of(items));
303
-
304
- await store.load();
305
-
306
- expect(store.items()).toEqual(items);
307
- });
308
-
309
- it('should set error on failure', async () => {
310
- <name>ServiceSpy.getAll.and.returnValue(
311
- throwError(() => new Error('Network error'))
312
- );
313
-
314
- await store.load();
315
-
316
- expect(store.error()).toBe('Network error');
317
- });
318
- });
319
-
320
- describe('add', () => {
321
- it('should add item to store', async () => {
322
- const newItem = { id: '1', name: 'New Item' };
323
- <name>ServiceSpy.create.and.returnValue(of(newItem));
324
-
325
- await store.add({ name: 'New Item' });
326
-
327
- expect(store.items()).toContain(newItem);
328
- });
329
- });
330
-
331
- describe('computed', () => {
332
- it('should compute itemCount', async () => {
333
- const items = [
334
- { id: '1', name: 'Item 1' },
335
- { id: '2', name: 'Item 2' },
336
- ];
337
- <name>ServiceSpy.getAll.and.returnValue(of(items));
338
-
339
- await store.load();
340
-
341
- expect(store.itemCount()).toBe(2);
342
- });
343
- });
344
- });
345
- ```
346
-
347
- ## Component Usage
348
-
349
- ```typescript
350
- @Component({
351
- selector: 'app-<name>-list',
352
- providers: [<Name>Store], // Component-level store (or omit for root)
353
- template: `
354
- @if (store.loading()) {
355
- <app-spinner />
356
- }
357
-
358
- @if (store.error()) {
359
- <app-error
360
- [message]="store.error()!"
361
- (retry)="store.load()"
362
- />
363
- }
364
-
365
- @for (item of store.items(); track item.id) {
366
- <app-<name>-card
367
- [item]="item"
368
- [selected]="store.selectedItem()?.id === item.id"
369
- (select)="store.select(item.id)"
370
- (delete)="store.remove(item.id)"
371
- />
372
- } @empty {
373
- <p>No items found</p>
374
- }
375
- `,
376
- })
377
- export class <Name>ListComponent {
378
- protected readonly store = inject(<Name>Store);
379
-
380
- constructor() {
381
- // Load data on component init if store is component-level
382
- this.store.load();
383
- }
384
- }
385
- ```
386
-
387
- ## Execution Steps
388
-
389
- 1. **Parse Arguments**
390
- - Extract store name
391
- - Check for `--entity` flag
392
- - Check for `--root` flag
393
-
394
- 2. **Generate Files**
395
- - Create store file with appropriate template
396
- - Create test file
397
- - Update index.ts
398
-
399
- 3. **Show Usage**
400
- - Display component usage example
401
- - Show how to inject the store
402
-
403
- ## Output Summary
404
-
405
- ```
406
- ✓ Created SignalStore: libs/<domain>/data-access/src/lib/stores/<name>.store.ts
407
-
408
- Store name: <Name>Store
409
- Type: [Basic | Entity]
410
- Scope: [Component | Root]
411
-
412
- Features:
413
- - withState (loading, error, custom state)
414
- - withComputed (derived values)
415
- - withMethods (CRUD operations)
416
- - withHooks (onInit)
417
- [- withEntities (entity adapter)]
418
-
419
- Usage:
420
- // In component
421
- protected readonly store = inject(<Name>Store);
422
-
423
- // Access state
424
- store.items()
425
- store.loading()
426
- store.selectedItem()
427
-
428
- // Call methods
429
- store.load()
430
- store.add(item)
431
- store.update(id, changes)
432
- store.remove(id)
433
- ```
434
-
435
- ## Placeholders
436
-
437
- | Placeholder | Example (product) |
438
- |-------------|-------------------|
439
- | `<name>` | product |
440
- | `<Name>` | Product |
441
- | `<entity>` | product |
442
- | `<Entity>` | Product |
443
- | `<entities>` | products |
444
- | `<Entities>` | Products |
445
- | `<Item>` | Product (or custom type) |
File without changes
File without changes
File without changes
File without changes
File without changes