fragment-ts 1.0.18 → 1.0.20
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/changes/1.md +113 -64
- package/dist/cli/commands/init.command.js +1 -1
- package/dist/cli/commands/test.command.d.ts +1 -0
- package/dist/cli/commands/test.command.d.ts.map +1 -1
- package/dist/cli/commands/test.command.js +149 -173
- package/dist/cli/commands/test.command.js.map +1 -1
- package/dist/testing/runner.d.ts +14 -1
- package/dist/testing/runner.d.ts.map +1 -1
- package/dist/testing/runner.js +199 -19
- package/dist/testing/runner.js.map +1 -1
- package/dist/web/application.d.ts.map +1 -1
- package/dist/web/application.js +1 -0
- package/dist/web/application.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/init.command.ts +1 -1
- package/src/cli/commands/test.command.ts +188 -173
- package/src/testing/runner.ts +253 -25
- package/src/web/application.ts +1 -0
package/changes/1.md
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Issues Fixed
|
|
4
4
|
|
|
5
|
-
### 1.
|
|
5
|
+
### 1. Test Command Created
|
|
6
|
+
|
|
6
7
|
**Problem:** No test command existed.
|
|
7
8
|
|
|
8
9
|
**Solution:** Created `TestCommand` class with full testing support:
|
|
10
|
+
|
|
9
11
|
- Runs `.spec.ts` files
|
|
10
12
|
- Supports watch mode
|
|
11
13
|
- Auto-detects dev vs prod environment
|
|
@@ -13,6 +15,7 @@
|
|
|
13
15
|
- Shows detailed test results with pass/fail counts
|
|
14
16
|
|
|
15
17
|
**Usage:**
|
|
18
|
+
|
|
16
19
|
```bash
|
|
17
20
|
fragment test # Run all tests
|
|
18
21
|
fragment test --watch # Watch mode
|
|
@@ -21,74 +24,84 @@ fragment test --pattern "**/*.spec.ts" # Custom pattern
|
|
|
21
24
|
|
|
22
25
|
---
|
|
23
26
|
|
|
24
|
-
### 2.
|
|
27
|
+
### 2. @Autowired Decorator Fixed
|
|
28
|
+
|
|
25
29
|
**Problem:** @Autowired wasn't injecting dependencies properly.
|
|
26
30
|
|
|
27
31
|
**Root Causes:**
|
|
32
|
+
|
|
28
33
|
- Metadata wasn't being stored correctly on the prototype
|
|
29
34
|
- DI container wasn't checking the prototype chain for metadata
|
|
30
35
|
- Properties were being checked on instance instead of prototype
|
|
31
36
|
|
|
32
37
|
**Solution:**
|
|
38
|
+
|
|
33
39
|
- Fixed `Autowired()` decorator to store metadata on prototype
|
|
34
40
|
- Updated DI container to traverse prototype chain with `getAllPropertyKeys()`
|
|
35
41
|
- Added proper type checking from TypeScript's `design:type` metadata
|
|
36
42
|
- Improved error messages for missing emitDecoratorMetadata
|
|
37
43
|
|
|
38
44
|
**Now Works:**
|
|
45
|
+
|
|
39
46
|
```typescript
|
|
40
47
|
@Service()
|
|
41
48
|
export class UserService {
|
|
42
49
|
@Autowired()
|
|
43
|
-
private userRepository!: UserRepository;
|
|
44
|
-
|
|
50
|
+
private userRepository!: UserRepository; // Auto-injected
|
|
51
|
+
|
|
45
52
|
@Autowired()
|
|
46
|
-
private emailService!: EmailService;
|
|
53
|
+
private emailService!: EmailService; // Auto-injected
|
|
47
54
|
}
|
|
48
55
|
```
|
|
49
56
|
|
|
50
57
|
---
|
|
51
58
|
|
|
52
|
-
### 3.
|
|
59
|
+
### 3. Repository TypeORM Integration Fixed
|
|
60
|
+
|
|
53
61
|
**Problem:** Repositories couldn't access TypeORM repositories automatically.
|
|
54
62
|
|
|
55
63
|
**Solution:** Created `@InjectRepository(Entity)` decorator:
|
|
64
|
+
|
|
56
65
|
- New metadata key: `INJECT_REPOSITORY`
|
|
57
66
|
- DI container resolves TypeORM repositories via `TypeORMModule.getDataSource()`
|
|
58
67
|
- Automatic entity-to-repository mapping
|
|
59
68
|
|
|
60
69
|
**Now Works:**
|
|
70
|
+
|
|
61
71
|
```typescript
|
|
62
72
|
@Repository()
|
|
63
73
|
export class UserRepository {
|
|
64
74
|
@InjectRepository(User)
|
|
65
|
-
private repo!: Repository<User>;
|
|
66
|
-
|
|
75
|
+
private repo!: Repository<User>; // TypeORM repo injected
|
|
76
|
+
|
|
67
77
|
async findAll() {
|
|
68
|
-
return this.repo.find();
|
|
78
|
+
return this.repo.find(); // Direct TypeORM access
|
|
69
79
|
}
|
|
70
80
|
}
|
|
71
81
|
```
|
|
72
82
|
|
|
73
83
|
---
|
|
74
84
|
|
|
75
|
-
### 4.
|
|
85
|
+
### 4. All Injectable Decorators Working
|
|
86
|
+
|
|
76
87
|
**Previously Working:**
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
88
|
+
|
|
89
|
+
- @Controller
|
|
90
|
+
- @Service
|
|
91
|
+
- HTTP decorators (@Get, @Post, etc.)
|
|
80
92
|
|
|
81
93
|
**Now Also Working:**
|
|
82
|
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
94
|
+
|
|
95
|
+
- @Injectable - General purpose components
|
|
96
|
+
- @Repository - Data access with TypeORM integration
|
|
97
|
+
- @Autowired - Property injection by type
|
|
98
|
+
- @InjectRepository - TypeORM repository injection
|
|
99
|
+
- @Inject - Token-based injection
|
|
100
|
+
- @Value - Environment variable injection
|
|
101
|
+
- @Qualifier - Bean qualifier
|
|
102
|
+
- @PostConstruct - Lifecycle hook
|
|
103
|
+
- @PreDestroy - Cleanup hook
|
|
104
|
+
- @AutoConfiguration - Conditional beans
|
|
92
105
|
|
|
93
106
|
---
|
|
94
107
|
|
|
@@ -97,12 +110,14 @@ export class UserRepository {
|
|
|
97
110
|
### DI Container Enhancements
|
|
98
111
|
|
|
99
112
|
1. **Circular Dependency Detection**
|
|
113
|
+
|
|
100
114
|
```typescript
|
|
101
115
|
private constructing: Set<any> = new Set();
|
|
102
116
|
// Detects and throws error for circular deps
|
|
103
117
|
```
|
|
104
118
|
|
|
105
119
|
2. **Prototype Chain Traversal**
|
|
120
|
+
|
|
106
121
|
```typescript
|
|
107
122
|
private getAllPropertyKeys(obj: any): string[] {
|
|
108
123
|
// Walks entire prototype chain to find all decorated properties
|
|
@@ -110,6 +125,7 @@ export class UserRepository {
|
|
|
110
125
|
```
|
|
111
126
|
|
|
112
127
|
3. **TypeORM Repository Resolution**
|
|
128
|
+
|
|
113
129
|
```typescript
|
|
114
130
|
private resolveRepository(entity: any): any {
|
|
115
131
|
const dataSource = TypeORMModule.getDataSource();
|
|
@@ -124,7 +140,7 @@ export class UserRepository {
|
|
|
124
140
|
|
|
125
141
|
5. **Post-Construction Hooks**
|
|
126
142
|
```typescript
|
|
127
|
-
if (typeof instance.onInit ===
|
|
143
|
+
if (typeof instance.onInit === "function") {
|
|
128
144
|
instance.onInit();
|
|
129
145
|
}
|
|
130
146
|
```
|
|
@@ -134,12 +150,14 @@ export class UserRepository {
|
|
|
134
150
|
## New Decorators Added
|
|
135
151
|
|
|
136
152
|
### @InjectRepository
|
|
153
|
+
|
|
137
154
|
```typescript
|
|
138
155
|
@InjectRepository(User)
|
|
139
156
|
private userRepo!: Repository<User>;
|
|
140
157
|
```
|
|
141
158
|
|
|
142
159
|
### @Optional
|
|
160
|
+
|
|
143
161
|
```typescript
|
|
144
162
|
@Optional()
|
|
145
163
|
@Autowired()
|
|
@@ -147,6 +165,7 @@ private cache?: CacheService; // Won't throw if not found
|
|
|
147
165
|
```
|
|
148
166
|
|
|
149
167
|
### @Lazy
|
|
168
|
+
|
|
150
169
|
```typescript
|
|
151
170
|
@Lazy()
|
|
152
171
|
@Autowired()
|
|
@@ -154,6 +173,7 @@ private heavyService!: HeavyService; // Created on first access
|
|
|
154
173
|
```
|
|
155
174
|
|
|
156
175
|
### @PostConstruct
|
|
176
|
+
|
|
157
177
|
```typescript
|
|
158
178
|
@PostConstruct()
|
|
159
179
|
init() {
|
|
@@ -162,6 +182,7 @@ init() {
|
|
|
162
182
|
```
|
|
163
183
|
|
|
164
184
|
### @PreDestroy
|
|
185
|
+
|
|
165
186
|
```typescript
|
|
166
187
|
@PreDestroy()
|
|
167
188
|
cleanup() {
|
|
@@ -174,21 +195,23 @@ cleanup() {
|
|
|
174
195
|
## Testing Infrastructure
|
|
175
196
|
|
|
176
197
|
### Test Runner Features
|
|
177
|
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
198
|
+
|
|
199
|
+
- Automatic test file discovery
|
|
200
|
+
- TypeScript support (ts-node)
|
|
201
|
+
- Watch mode
|
|
202
|
+
- Detailed error reporting
|
|
203
|
+
- Test isolation (fresh DI container per test)
|
|
182
204
|
|
|
183
205
|
### Test API
|
|
206
|
+
|
|
184
207
|
```typescript
|
|
185
208
|
describe('UserService', () => {
|
|
186
209
|
let service: UserService;
|
|
187
|
-
|
|
210
|
+
|
|
188
211
|
beforeEach(() => {
|
|
189
212
|
// Setup
|
|
190
213
|
});
|
|
191
|
-
|
|
214
|
+
|
|
192
215
|
it('should create user', async () => {
|
|
193
216
|
const user = await service.createUser({...});
|
|
194
217
|
expect(user.name).toBe('Test');
|
|
@@ -197,6 +220,7 @@ describe('UserService', () => {
|
|
|
197
220
|
```
|
|
198
221
|
|
|
199
222
|
### Assertions
|
|
223
|
+
|
|
200
224
|
- `toBe()` - Strict equality
|
|
201
225
|
- `toEqual()` - Deep equality
|
|
202
226
|
- `toBeTruthy()` / `toBeFalsy()`
|
|
@@ -212,19 +236,26 @@ describe('UserService', () => {
|
|
|
212
236
|
## Complete Example
|
|
213
237
|
|
|
214
238
|
```typescript
|
|
215
|
-
import
|
|
216
|
-
import {
|
|
217
|
-
Controller,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
239
|
+
import "reflect-metadata";
|
|
240
|
+
import {
|
|
241
|
+
Controller,
|
|
242
|
+
Service,
|
|
243
|
+
Repository,
|
|
244
|
+
Get,
|
|
245
|
+
Post,
|
|
246
|
+
Body,
|
|
247
|
+
Param,
|
|
248
|
+
Autowired,
|
|
249
|
+
InjectRepository,
|
|
250
|
+
Value,
|
|
251
|
+
} from "fragment-ts";
|
|
221
252
|
|
|
222
253
|
// Entity
|
|
223
254
|
@Entity()
|
|
224
255
|
class User {
|
|
225
256
|
@PrimaryGeneratedColumn()
|
|
226
257
|
id!: number;
|
|
227
|
-
|
|
258
|
+
|
|
228
259
|
@Column()
|
|
229
260
|
name!: string;
|
|
230
261
|
}
|
|
@@ -234,7 +265,7 @@ class User {
|
|
|
234
265
|
class UserRepository {
|
|
235
266
|
@InjectRepository(User)
|
|
236
267
|
private repo!: Repository<User>;
|
|
237
|
-
|
|
268
|
+
|
|
238
269
|
async findAll() {
|
|
239
270
|
return this.repo.find();
|
|
240
271
|
}
|
|
@@ -245,22 +276,22 @@ class UserRepository {
|
|
|
245
276
|
class UserService {
|
|
246
277
|
@Autowired()
|
|
247
278
|
private userRepository!: UserRepository;
|
|
248
|
-
|
|
249
|
-
@Value(
|
|
279
|
+
|
|
280
|
+
@Value("${MAX_USERS:100}")
|
|
250
281
|
private maxUsers!: number;
|
|
251
|
-
|
|
282
|
+
|
|
252
283
|
async getUsers() {
|
|
253
284
|
return this.userRepository.findAll();
|
|
254
285
|
}
|
|
255
286
|
}
|
|
256
287
|
|
|
257
288
|
// Controller with service
|
|
258
|
-
@Controller(
|
|
289
|
+
@Controller("/api/users")
|
|
259
290
|
class UserController {
|
|
260
291
|
@Autowired()
|
|
261
292
|
private userService!: UserService;
|
|
262
|
-
|
|
263
|
-
@Get(
|
|
293
|
+
|
|
294
|
+
@Get("/")
|
|
264
295
|
async getAll() {
|
|
265
296
|
return this.userService.getUsers();
|
|
266
297
|
}
|
|
@@ -272,24 +303,27 @@ class UserController {
|
|
|
272
303
|
## Migration Guide
|
|
273
304
|
|
|
274
305
|
### Before (Broken)
|
|
306
|
+
|
|
275
307
|
```typescript
|
|
276
308
|
@Service()
|
|
277
309
|
class MyService {
|
|
278
310
|
@Autowired()
|
|
279
|
-
private repo: MyRepo;
|
|
311
|
+
private repo: MyRepo; // ❌ undefined
|
|
280
312
|
}
|
|
281
313
|
```
|
|
282
314
|
|
|
283
315
|
### After (Fixed)
|
|
316
|
+
|
|
284
317
|
```typescript
|
|
285
318
|
@Service()
|
|
286
319
|
class MyService {
|
|
287
320
|
@Autowired()
|
|
288
|
-
private repo!: MyRepo;
|
|
321
|
+
private repo!: MyRepo; // Auto-injected (note the !)
|
|
289
322
|
}
|
|
290
323
|
```
|
|
291
324
|
|
|
292
325
|
### Key Changes Required:
|
|
326
|
+
|
|
293
327
|
1. Add `!` for definite assignment: `private repo!: MyRepo`
|
|
294
328
|
2. Ensure `emitDecoratorMetadata: true` in tsconfig.json
|
|
295
329
|
3. Import `reflect-metadata` at app entry point
|
|
@@ -303,25 +337,25 @@ class MyService {
|
|
|
303
337
|
fragment-ts/
|
|
304
338
|
├── src/
|
|
305
339
|
│ ├── cli/
|
|
306
|
-
│ │ ├── cli.ts #
|
|
340
|
+
│ │ ├── cli.ts # Updated with test command
|
|
307
341
|
│ │ └── commands/
|
|
308
|
-
│ │ ├── test.command.ts #
|
|
342
|
+
│ │ ├── test.command.ts # NEW
|
|
309
343
|
│ │ ├── diagnostics.command.ts
|
|
310
344
|
│ │ └── ...
|
|
311
345
|
│ ├── core/
|
|
312
346
|
│ │ ├── container/
|
|
313
|
-
│ │ │ └── di-container.ts #
|
|
347
|
+
│ │ │ └── di-container.ts # FIXED
|
|
314
348
|
│ │ ├── decorators/
|
|
315
|
-
│ │ │ ├── injectable.decorator.ts #
|
|
349
|
+
│ │ │ ├── injectable.decorator.ts # FIXED
|
|
316
350
|
│ │ │ ├── repository.decorator.ts
|
|
317
351
|
│ │ │ └── ...
|
|
318
352
|
│ │ └── metadata/
|
|
319
|
-
│ │ └── metadata-keys.ts #
|
|
353
|
+
│ │ └── metadata-keys.ts # UPDATED
|
|
320
354
|
│ └── typeorm/
|
|
321
355
|
│ └── typeorm-module.ts
|
|
322
356
|
└── examples/
|
|
323
|
-
├── complete-app/ #
|
|
324
|
-
└── tests/ #
|
|
357
|
+
├── complete-app/ # NEW
|
|
358
|
+
└── tests/ # NEW
|
|
325
359
|
```
|
|
326
360
|
|
|
327
361
|
---
|
|
@@ -342,40 +376,52 @@ Before running tests, ensure:
|
|
|
342
376
|
## Common Issues & Solutions
|
|
343
377
|
|
|
344
378
|
### Issue: "Cannot resolve dependency"
|
|
379
|
+
|
|
345
380
|
**Solution:** Make sure the dependency is registered:
|
|
381
|
+
|
|
346
382
|
```typescript
|
|
347
383
|
container.register(MyService);
|
|
348
384
|
```
|
|
349
385
|
|
|
350
386
|
### Issue: "@Autowired returns undefined"
|
|
387
|
+
|
|
351
388
|
**Solution:** Add `!` for definite assignment and check tsconfig:
|
|
389
|
+
|
|
352
390
|
```typescript
|
|
353
391
|
@Autowired()
|
|
354
392
|
private service!: MyService; // Note the !
|
|
355
393
|
```
|
|
356
394
|
|
|
357
395
|
### Issue: "TypeORM repository is undefined"
|
|
396
|
+
|
|
358
397
|
**Solution:** Use @InjectRepository instead of @Autowired:
|
|
398
|
+
|
|
359
399
|
```typescript
|
|
360
400
|
@InjectRepository(User) // Not @Autowired()
|
|
361
401
|
private repo!: Repository<User>;
|
|
362
402
|
```
|
|
363
403
|
|
|
364
404
|
### Issue: "Circular dependency detected"
|
|
405
|
+
|
|
365
406
|
**Solution:** Refactor to break the cycle:
|
|
407
|
+
|
|
366
408
|
```typescript
|
|
367
409
|
// Before (circular)
|
|
368
|
-
class A {
|
|
369
|
-
|
|
410
|
+
class A {
|
|
411
|
+
constructor(private b: B) {}
|
|
412
|
+
}
|
|
413
|
+
class B {
|
|
414
|
+
constructor(private a: A) {}
|
|
415
|
+
}
|
|
370
416
|
|
|
371
417
|
// After (inject via property)
|
|
372
|
-
class A {
|
|
373
|
-
@Autowired()
|
|
374
|
-
private b!: B;
|
|
418
|
+
class A {
|
|
419
|
+
@Autowired()
|
|
420
|
+
private b!: B;
|
|
375
421
|
}
|
|
376
|
-
class B {
|
|
377
|
-
@Autowired()
|
|
378
|
-
private a!: A;
|
|
422
|
+
class B {
|
|
423
|
+
@Autowired()
|
|
424
|
+
private a!: A;
|
|
379
425
|
}
|
|
380
426
|
```
|
|
381
427
|
|
|
@@ -384,16 +430,19 @@ class B {
|
|
|
384
430
|
## Next Steps
|
|
385
431
|
|
|
386
432
|
1. **Run Tests**
|
|
433
|
+
|
|
387
434
|
```bash
|
|
388
435
|
fragment test
|
|
389
436
|
```
|
|
390
437
|
|
|
391
438
|
2. **Check All Beans**
|
|
439
|
+
|
|
392
440
|
```bash
|
|
393
441
|
fragment beans
|
|
394
442
|
```
|
|
395
443
|
|
|
396
444
|
3. **Verify Routes**
|
|
445
|
+
|
|
397
446
|
```bash
|
|
398
447
|
fragment routes
|
|
399
448
|
```
|
|
@@ -415,6 +464,6 @@ All requested features are now working:
|
|
|
415
464
|
✅ @InjectRepository provides TypeORM repository access
|
|
416
465
|
✅ All decorators working (@Controller, @Service, @Repository, @Injectable, etc.)
|
|
417
466
|
✅ Comprehensive examples and documentation
|
|
418
|
-
✅ Complete testing infrastructure
|
|
467
|
+
✅ Complete testing infrastructure
|
|
419
468
|
|
|
420
|
-
The framework is now fully functional and ready for development!
|
|
469
|
+
The framework is now fully functional and ready for development!
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.command.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/test.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,qBAAa,WAAW;IACtB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"test.command.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/test.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,qBAAa,WAAW;IACtB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;mBAkBlB,QAAQ;IA8I7B,OAAO,CAAC,MAAM,CAAC,oBAAoB;CAwIpC"}
|