sandly 2.0.1 → 2.1.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.
- package/README.md +90 -15
- package/dist/index.d.ts +40 -0
- package/dist/index.js +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -276,12 +276,35 @@ const dbLayer = Layer.service(Database, [], {
|
|
|
276
276
|
const ApiKeyTag = Tag.of('apiKey')<string>();
|
|
277
277
|
const configLayer = Layer.value(ApiKeyTag, process.env.API_KEY!);
|
|
278
278
|
|
|
279
|
-
// ServiceTag (pre-instantiated instances
|
|
279
|
+
// ServiceTag (pre-instantiated instances)
|
|
280
280
|
class UserService {
|
|
281
|
-
|
|
281
|
+
getUsers() {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
282
284
|
}
|
|
283
|
-
const
|
|
284
|
-
const testLayer = Layer.value(UserService,
|
|
285
|
+
const userService = new UserService();
|
|
286
|
+
const testLayer = Layer.value(UserService, userService);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Layer.mock**: Partial mocks for testing (ServiceTags only)
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
class UserService {
|
|
293
|
+
constructor(private db: Database) {}
|
|
294
|
+
getUsers() {
|
|
295
|
+
return this.db.query('SELECT * FROM users');
|
|
296
|
+
}
|
|
297
|
+
getUserById(id: number) {
|
|
298
|
+
return this.db.query(`...`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Mock only the methods you need - no constructor dependencies required
|
|
303
|
+
const testLayer = Layer.mock(UserService, {
|
|
304
|
+
getUsers: () => Promise.resolve([{ id: 1, name: 'Alice' }]),
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// TypeScript still validates the mock's method signatures
|
|
285
308
|
```
|
|
286
309
|
|
|
287
310
|
**Layer.create**: Custom factory logic
|
|
@@ -453,17 +476,18 @@ try {
|
|
|
453
476
|
|
|
454
477
|
### Layer
|
|
455
478
|
|
|
456
|
-
| Method | Description
|
|
457
|
-
| -------------------------------------- |
|
|
458
|
-
| `Layer.service(class, deps, options?)` | Create layer for a class
|
|
459
|
-
| `Layer.value(tag, value)` | Create layer for a constant value
|
|
460
|
-
| `Layer.
|
|
461
|
-
| `Layer.
|
|
462
|
-
| `Layer.
|
|
463
|
-
| `Layer.
|
|
464
|
-
| `
|
|
465
|
-
| `layer.
|
|
466
|
-
| `layer.
|
|
479
|
+
| Method | Description |
|
|
480
|
+
| -------------------------------------- | ----------------------------------------------- |
|
|
481
|
+
| `Layer.service(class, deps, options?)` | Create layer for a class |
|
|
482
|
+
| `Layer.value(tag, value)` | Create layer for a constant value |
|
|
483
|
+
| `Layer.mock(tag, implementation)` | Create layer with mock (partial for ServiceTag) |
|
|
484
|
+
| `Layer.create({ requires, apply })` | Create custom layer |
|
|
485
|
+
| `Layer.empty()` | Create empty layer |
|
|
486
|
+
| `Layer.merge(a, b)` | Merge two layers |
|
|
487
|
+
| `Layer.mergeAll(...layers)` | Merge multiple layers |
|
|
488
|
+
| `layer.provide(dep)` | Satisfy dependencies |
|
|
489
|
+
| `layer.provideMerge(dep)` | Satisfy and merge provisions |
|
|
490
|
+
| `layer.merge(other)` | Merge with another layer |
|
|
467
491
|
|
|
468
492
|
### ScopedContainer
|
|
469
493
|
|
|
@@ -483,6 +507,57 @@ try {
|
|
|
483
507
|
| `Tag.id(tag)` | Get tag's string identifier |
|
|
484
508
|
| `Tag.isTag(value)` | Check if value is a tag |
|
|
485
509
|
|
|
510
|
+
## Testing
|
|
511
|
+
|
|
512
|
+
Sandly makes testing easy with `Layer.mock()`, which allows you to create partial mocks without satisfying constructor dependencies. Import your production layers and override dependencies with mocks:
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
// Production code (e.g., src/services/user-service.ts)
|
|
516
|
+
import { Layer } from 'sandly';
|
|
517
|
+
import { ResourcesRepository } from '../repositories/resources-repository';
|
|
518
|
+
|
|
519
|
+
export class UserService {
|
|
520
|
+
constructor(private repo: ResourcesRepository) {}
|
|
521
|
+
async getUsers() {
|
|
522
|
+
return this.repo.listByCrawlId('crawl-123');
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Layer definition in the same file
|
|
527
|
+
export const userServiceLayer = Layer.service(UserService, [
|
|
528
|
+
ResourcesRepository,
|
|
529
|
+
]);
|
|
530
|
+
|
|
531
|
+
// Test file (e.g., src/services/user-service.test.ts)
|
|
532
|
+
import { Container, Layer } from 'sandly';
|
|
533
|
+
import { userServiceLayer } from './user-service';
|
|
534
|
+
import { ResourcesRepository } from '../repositories/resources-repository';
|
|
535
|
+
|
|
536
|
+
// Override production dependencies with mocks
|
|
537
|
+
const testLayer = userServiceLayer.provide(
|
|
538
|
+
Layer.mock(ResourcesRepository, {
|
|
539
|
+
listByCrawlId: async () => [
|
|
540
|
+
{ id: '1', name: 'Alice' },
|
|
541
|
+
{ id: '2', name: 'Bob' },
|
|
542
|
+
],
|
|
543
|
+
})
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
const container = Container.from(testLayer);
|
|
547
|
+
const userService = await container.resolve(UserService);
|
|
548
|
+
|
|
549
|
+
// Use the service - mock is automatically injected
|
|
550
|
+
const users = await userService.getUsers();
|
|
551
|
+
expect(users).toHaveLength(2);
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**Benefits:**
|
|
555
|
+
|
|
556
|
+
- ✅ No need to satisfy constructor dependencies for mocks
|
|
557
|
+
- ✅ TypeScript validates mock method signatures
|
|
558
|
+
- ✅ Works seamlessly with `Layer.service()` composition
|
|
559
|
+
- ✅ Clear intent: `mock()` for tests, `value()` for production
|
|
560
|
+
|
|
486
561
|
## Comparison with Alternatives
|
|
487
562
|
|
|
488
563
|
| Feature | Sandly | NestJS | InversifyJS | TSyringe |
|
package/dist/index.d.ts
CHANGED
|
@@ -352,6 +352,46 @@ declare const Layer: {
|
|
|
352
352
|
* ```
|
|
353
353
|
*/
|
|
354
354
|
value<T extends AnyTag>(tag: T, value: TagType<T>): Layer<never, T>;
|
|
355
|
+
/**
|
|
356
|
+
* Creates a layer with a mock implementation for testing.
|
|
357
|
+
*
|
|
358
|
+
* Similar to `Layer.value()`, but allows partial implementations for ServiceTags,
|
|
359
|
+
* making it easier to create test mocks without satisfying constructor dependencies.
|
|
360
|
+
*
|
|
361
|
+
* **Use this for testing only.** For production code, use `Layer.value()` or `Layer.service()`.
|
|
362
|
+
*
|
|
363
|
+
* @param tag - The tag (ServiceTag or ValueTag) to register
|
|
364
|
+
* @param implementation - The mock implementation (can be partial for ServiceTags)
|
|
365
|
+
*
|
|
366
|
+
* @example ServiceTag with partial mock
|
|
367
|
+
* ```typescript
|
|
368
|
+
* class UserService {
|
|
369
|
+
* constructor(private db: Database) {}
|
|
370
|
+
* getUsers() { return this.db.query('SELECT * FROM users'); }
|
|
371
|
+
* }
|
|
372
|
+
*
|
|
373
|
+
* // Mock only the methods you need - no need to satisfy constructor
|
|
374
|
+
* const testLayer = Layer.mock(UserService, {
|
|
375
|
+
* getUsers: () => Promise.resolve([{ id: 1, name: 'Alice' }])
|
|
376
|
+
* });
|
|
377
|
+
* ```
|
|
378
|
+
*
|
|
379
|
+
* @example ServiceTag with full mock instance
|
|
380
|
+
* ```typescript
|
|
381
|
+
* const mockUserService = {
|
|
382
|
+
* getUsers: () => Promise.resolve([])
|
|
383
|
+
* } as UserService;
|
|
384
|
+
*
|
|
385
|
+
* const testLayer = Layer.mock(UserService, mockUserService);
|
|
386
|
+
* ```
|
|
387
|
+
*
|
|
388
|
+
* @example ValueTag (works same as Layer.value)
|
|
389
|
+
* ```typescript
|
|
390
|
+
* const ConfigTag = Tag.of('config')<{ port: number }>();
|
|
391
|
+
* const testLayer = Layer.mock(ConfigTag, { port: 3000 });
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
394
|
+
mock<T extends AnyTag>(tag: T, implementation: T extends ServiceTag ? Partial<TagType<T>> | TagType<T> : TagType<T>): Layer<never, T>;
|
|
355
395
|
/**
|
|
356
396
|
* Creates a custom layer with full control over the factory logic.
|
|
357
397
|
*
|
package/dist/index.js
CHANGED
|
@@ -894,6 +894,11 @@ const Layer = {
|
|
|
894
894
|
return builder.add(tag, () => value);
|
|
895
895
|
});
|
|
896
896
|
},
|
|
897
|
+
mock(tag, implementation) {
|
|
898
|
+
return createLayer((builder) => {
|
|
899
|
+
return builder.add(tag, () => implementation);
|
|
900
|
+
});
|
|
901
|
+
},
|
|
897
902
|
create(options) {
|
|
898
903
|
const layer = {
|
|
899
904
|
apply: (builder) => {
|