@malamute/ai-rules 1.2.0 → 1.3.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 +4 -2
- package/bin/cli.js +1 -1
- package/configs/_shared/rules/conventions/npm.md +80 -0
- package/configs/angular/{.claude/settings.json → settings.json} +2 -0
- package/configs/dotnet/{.claude/settings.json → settings.json} +2 -0
- package/configs/fastapi/{.claude/settings.json → settings.json} +2 -0
- package/configs/flask/{.claude/settings.json → settings.json} +2 -0
- package/configs/nestjs/{.claude/settings.json → settings.json} +2 -0
- package/configs/nextjs/{.claude/settings.json → settings.json} +2 -0
- package/package.json +2 -1
- package/src/cli.js +5 -7
- package/src/config.js +52 -18
- package/src/index.js +4 -13
- package/src/installer.js +121 -68
- package/src/merge.js +8 -15
- package/src/tech-config.json +29 -13
- package/src/utils.js +7 -15
- package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +0 -362
- package/configs/angular/.claude/skills/signal-store/SKILL.md +0 -445
- /package/configs/_shared/{.claude/rules → rules}/conventions/documentation.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/conventions/git.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/conventions/performance.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/conventions/principles.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/devops/ci-cd.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/devops/docker.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/devops/nx.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/domain/backend/api-design.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/domain/frontend/accessibility.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/csharp/async.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/csharp/csharp.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/csharp/linq.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/async.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/celery.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/config.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/database/sqlalchemy.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/deployment.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/error-handling.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/migrations.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/python.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/repository.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/python/testing.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/typescript/async.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/typescript/generics.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/lang/typescript/typescript.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/quality/error-handling.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/quality/logging.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/quality/observability.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/quality/testing-patterns.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/security/secrets-management.md +0 -0
- /package/configs/_shared/{.claude/rules → rules}/security/security.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/analysis/explore/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/analysis/security-audit/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/dev/api-endpoint/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/dev/debug/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/dev/generate-tests/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/dev/learning/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/dev/spec/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/git/fix-issue/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/git/review/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/git/review-pr/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/infra/deploy/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/infra/docker/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/infra/migration/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/nx/nx-affected/SKILL.md +0 -0
- /package/configs/_shared/{.claude/skills → skills}/nx/nx-lib/SKILL.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/core/components.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/core/resource.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/core/signals.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/http.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/routing.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/ssr.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/state/signal-store.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/state/state.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/testing.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/ui/aria.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/ui/forms.md +0 -0
- /package/configs/angular/{.claude/rules → rules}/ui/pipes-directives.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/api.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/architecture.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/background-services.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/configuration.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/database/efcore.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/ddd.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/dependency-injection.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/mediatr.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/middleware.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/result-pattern.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/testing.md +0 -0
- /package/configs/dotnet/{.claude/rules → rules}/validation.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/background-tasks.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/dependencies.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/fastapi.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/lifespan.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/middleware.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/pydantic.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/responses.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/routers.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/security.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/testing.md +0 -0
- /package/configs/fastapi/{.claude/rules → rules}/websockets.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/blueprints.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/cli.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/configuration.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/context.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/error-handlers.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/extensions.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/flask.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/marshmallow.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/security.md +0 -0
- /package/configs/flask/{.claude/rules → rules}/testing.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/auth.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/common-patterns.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/database/prisma.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/database/typeorm.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/filters.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/interceptors.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/middleware.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/modules.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/pipes.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/testing.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/validation.md +0 -0
- /package/configs/nestjs/{.claude/rules → rules}/websockets.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/api-routes.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/authentication.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/components.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/data-fetching.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/database.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/middleware.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/routing.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/seo.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/server-actions.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/state/redux-toolkit.md +0 -0
- /package/configs/nextjs/{.claude/rules → rules}/state/zustand.md +0 -0
- /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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|