sandly 2.0.1 → 3.0.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 +116 -25
- package/dist/index.d.ts +61 -17
- package/dist/index.js +20 -35
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<h1 align="center">Sandly</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://www.npmjs.com/package/sandly"><img src="https://img.shields.io/npm/v/sandly?color=3178c6&label=npm" alt="npm version"></a>
|
|
5
|
+
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-5.0%2B-blue?logo=typescript&logoColor=white" alt="TypeScript"></a>
|
|
6
|
+
<a href="https://codecov.io/gh/borisrakovan/sandly"><img src="https://codecov.io/gh/borisrakovan/sandly/branch/main/graph/badge.svg" alt="coverage"></a>
|
|
7
|
+
<br />
|
|
8
|
+
<a href="https://github.com/borisrakovan/sandly/actions/workflows/ci.yml"><img src="https://github.com/borisrakovan/sandly/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
9
|
+
<a href="https://github.com/borisrakovan/sandly/blob/main/LICENSE"><img src="https://img.shields.io/github/license/borisrakovan/sandly" alt="license"></a>
|
|
10
|
+
<a href="https://github.com/borisrakovan/sandly"><img src="https://img.shields.io/github/stars/borisrakovan/sandly?style=social" alt="GitHub stars"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
Type-safe dependency injection for TypeScript, without decorators or reflection.
|
|
15
|
+
</p>
|
|
4
16
|
|
|
5
17
|
## Why Sandly?
|
|
6
18
|
|
|
@@ -40,6 +52,7 @@ const orders = await container.resolve(OrderService);
|
|
|
40
52
|
|
|
41
53
|
- **Compile-time safety**: TypeScript catches missing dependencies before runtime
|
|
42
54
|
- **No decorators**: Works with standard TypeScript, no experimental features
|
|
55
|
+
- **Inject anything**: Classes, objects, primitives, or functions
|
|
43
56
|
- **Async support**: Factories and cleanup functions can be async
|
|
44
57
|
- **Composable layers**: Organize dependencies into reusable modules
|
|
45
58
|
- **Scoped containers**: Hierarchical dependency management for web servers
|
|
@@ -183,19 +196,23 @@ const cacheLayer = Layer.create({
|
|
|
183
196
|
});
|
|
184
197
|
```
|
|
185
198
|
|
|
186
|
-
Compose layers with `provide()
|
|
199
|
+
Compose layers with `provide()` and `merge()`:
|
|
187
200
|
|
|
188
201
|
```typescript
|
|
189
202
|
// provide: satisfy dependencies, expose only this layer's provisions
|
|
190
203
|
const appLayer = userLayer.provide(dbLayer);
|
|
191
204
|
|
|
192
|
-
// merge: combine
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
// merge: combine layers, exposing both provisions. Internally satisfied
|
|
206
|
+
// requirements are subtracted from the result's requirements.
|
|
207
|
+
const infraLayer = dbLayer.merge(loggerLayer);
|
|
208
|
+
|
|
209
|
+
// merge wires dependencies in either direction. dbLayer requires Config and
|
|
210
|
+
// configLayer provides it - the result has no outstanding requirements.
|
|
211
|
+
const fullInfra = dbLayer.merge(configLayer);
|
|
196
212
|
|
|
197
|
-
//
|
|
198
|
-
const
|
|
213
|
+
// Static helpers for two or more layers
|
|
214
|
+
const appInfra = Layer.merge(dbLayer, loggerLayer);
|
|
215
|
+
const infra = Layer.mergeAll(dbLayer, loggerLayer, cacheLayer);
|
|
199
216
|
```
|
|
200
217
|
|
|
201
218
|
### Scoped Containers
|
|
@@ -276,12 +293,35 @@ const dbLayer = Layer.service(Database, [], {
|
|
|
276
293
|
const ApiKeyTag = Tag.of('apiKey')<string>();
|
|
277
294
|
const configLayer = Layer.value(ApiKeyTag, process.env.API_KEY!);
|
|
278
295
|
|
|
279
|
-
// ServiceTag (pre-instantiated instances
|
|
296
|
+
// ServiceTag (pre-instantiated instances)
|
|
280
297
|
class UserService {
|
|
281
|
-
|
|
298
|
+
getUsers() {
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
282
301
|
}
|
|
283
|
-
const
|
|
284
|
-
const testLayer = Layer.value(UserService,
|
|
302
|
+
const userService = new UserService();
|
|
303
|
+
const testLayer = Layer.value(UserService, userService);
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Layer.mock**: Partial mocks for testing (ServiceTags only)
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
class UserService {
|
|
310
|
+
constructor(private db: Database) {}
|
|
311
|
+
getUsers() {
|
|
312
|
+
return this.db.query('SELECT * FROM users');
|
|
313
|
+
}
|
|
314
|
+
getUserById(id: number) {
|
|
315
|
+
return this.db.query(`...`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Mock only the methods you need - no constructor dependencies required
|
|
320
|
+
const testLayer = Layer.mock(UserService, {
|
|
321
|
+
getUsers: () => Promise.resolve([{ id: 1, name: 'Alice' }]),
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// TypeScript still validates the mock's method signatures
|
|
285
325
|
```
|
|
286
326
|
|
|
287
327
|
**Layer.create**: Custom factory logic
|
|
@@ -453,17 +493,17 @@ try {
|
|
|
453
493
|
|
|
454
494
|
### Layer
|
|
455
495
|
|
|
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.merge(other)` | Merge
|
|
496
|
+
| Method | Description |
|
|
497
|
+
| -------------------------------------- | ----------------------------------------------- |
|
|
498
|
+
| `Layer.service(class, deps, options?)` | Create layer for a class |
|
|
499
|
+
| `Layer.value(tag, value)` | Create layer for a constant value |
|
|
500
|
+
| `Layer.mock(tag, implementation)` | Create layer with mock (partial for ServiceTag) |
|
|
501
|
+
| `Layer.create({ requires, apply })` | Create custom layer |
|
|
502
|
+
| `Layer.empty()` | Create empty layer |
|
|
503
|
+
| `Layer.merge(a, b)` | Merge two layers (smart subtraction) |
|
|
504
|
+
| `Layer.mergeAll(...layers)` | Merge multiple layers (smart subtraction) |
|
|
505
|
+
| `layer.provide(dep)` | Satisfy dependencies, expose only target's |
|
|
506
|
+
| `layer.merge(other)` | Merge layers, expose both, subtract satisfied |
|
|
467
507
|
|
|
468
508
|
### ScopedContainer
|
|
469
509
|
|
|
@@ -483,6 +523,57 @@ try {
|
|
|
483
523
|
| `Tag.id(tag)` | Get tag's string identifier |
|
|
484
524
|
| `Tag.isTag(value)` | Check if value is a tag |
|
|
485
525
|
|
|
526
|
+
## Testing
|
|
527
|
+
|
|
528
|
+
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:
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
// Production code (e.g., src/services/user-service.ts)
|
|
532
|
+
import { Layer } from 'sandly';
|
|
533
|
+
import { ResourcesRepository } from '../repositories/resources-repository';
|
|
534
|
+
|
|
535
|
+
export class UserService {
|
|
536
|
+
constructor(private repo: ResourcesRepository) {}
|
|
537
|
+
async getUsers() {
|
|
538
|
+
return this.repo.listByCrawlId('crawl-123');
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Layer definition in the same file
|
|
543
|
+
export const userServiceLayer = Layer.service(UserService, [
|
|
544
|
+
ResourcesRepository,
|
|
545
|
+
]);
|
|
546
|
+
|
|
547
|
+
// Test file (e.g., src/services/user-service.test.ts)
|
|
548
|
+
import { Container, Layer } from 'sandly';
|
|
549
|
+
import { userServiceLayer } from './user-service';
|
|
550
|
+
import { ResourcesRepository } from '../repositories/resources-repository';
|
|
551
|
+
|
|
552
|
+
// Override production dependencies with mocks
|
|
553
|
+
const testLayer = userServiceLayer.provide(
|
|
554
|
+
Layer.mock(ResourcesRepository, {
|
|
555
|
+
listByCrawlId: async () => [
|
|
556
|
+
{ id: '1', name: 'Alice' },
|
|
557
|
+
{ id: '2', name: 'Bob' },
|
|
558
|
+
],
|
|
559
|
+
})
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
const container = Container.from(testLayer);
|
|
563
|
+
const userService = await container.resolve(UserService);
|
|
564
|
+
|
|
565
|
+
// Use the service - mock is automatically injected
|
|
566
|
+
const users = await userService.getUsers();
|
|
567
|
+
expect(users).toHaveLength(2);
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
**Benefits:**
|
|
571
|
+
|
|
572
|
+
- ✅ No need to satisfy constructor dependencies for mocks
|
|
573
|
+
- ✅ TypeScript validates mock method signatures
|
|
574
|
+
- ✅ Works seamlessly with `Layer.service()` composition
|
|
575
|
+
- ✅ Clear intent: `mock()` for tests, `value()` for production
|
|
576
|
+
|
|
486
577
|
## Comparison with Alternatives
|
|
487
578
|
|
|
488
579
|
| Feature | Sandly | NestJS | InversifyJS | TSyringe |
|
package/dist/index.d.ts
CHANGED
|
@@ -239,6 +239,9 @@ interface Layer<TRequires extends AnyTag, TProvides extends AnyTag> {
|
|
|
239
239
|
* Provides a dependency layer to this layer, creating a pipeline.
|
|
240
240
|
* The result only exposes this layer's provisions (not the dependency's).
|
|
241
241
|
*
|
|
242
|
+
* Requirements satisfied internally (by either layer's provisions) are
|
|
243
|
+
* subtracted from the result's requirements.
|
|
244
|
+
*
|
|
242
245
|
* @example
|
|
243
246
|
* ```typescript
|
|
244
247
|
* const appLayer = apiLayer
|
|
@@ -246,28 +249,26 @@ interface Layer<TRequires extends AnyTag, TProvides extends AnyTag> {
|
|
|
246
249
|
* .provide(databaseLayer);
|
|
247
250
|
* ```
|
|
248
251
|
*/
|
|
249
|
-
provide: <TDepRequires extends AnyTag, TDepProvides extends AnyTag>(dependency: Layer<TDepRequires, TDepProvides>) => Layer<
|
|
252
|
+
provide: <TDepRequires extends AnyTag, TDepProvides extends AnyTag>(dependency: Layer<TDepRequires, TDepProvides>) => Layer<Exclude<TRequires | TDepRequires, TProvides | TDepProvides>, TProvides>;
|
|
250
253
|
/**
|
|
251
|
-
*
|
|
252
|
-
* Unlike `.provide()`, this exposes both this layer's and the dependency's provisions.
|
|
254
|
+
* Merges this layer with another layer, exposing both layers' provisions.
|
|
253
255
|
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
* ```
|
|
259
|
-
*/
|
|
260
|
-
provideMerge: <TDepRequires extends AnyTag, TDepProvides extends AnyTag>(dependency: Layer<TDepRequires, TDepProvides>) => Layer<TDepRequires | Exclude<TRequires, TDepProvides>, TProvides | TDepProvides>;
|
|
261
|
-
/**
|
|
262
|
-
* Merges this layer with another independent layer.
|
|
263
|
-
* Combines their requirements and provisions.
|
|
256
|
+
* Requirements satisfied internally (by either layer's provisions) are
|
|
257
|
+
* subtracted from the result's requirements. This means a merge of two
|
|
258
|
+
* layers where one provides what the other requires produces a layer with
|
|
259
|
+
* those requirements already satisfied.
|
|
264
260
|
*
|
|
265
261
|
* @example
|
|
266
262
|
* ```typescript
|
|
263
|
+
* // Independent layers
|
|
267
264
|
* const infraLayer = persistenceLayer.merge(loggingLayer);
|
|
265
|
+
*
|
|
266
|
+
* // Self-satisfying merge: dbLayer requires Config, configLayer provides it
|
|
267
|
+
* const fullInfra = dbLayer.merge(configLayer);
|
|
268
|
+
* // Result: requires nothing, provides Database | Config
|
|
268
269
|
* ```
|
|
269
270
|
*/
|
|
270
|
-
merge: <TOtherRequires extends AnyTag, TOtherProvides extends AnyTag>(other: Layer<TOtherRequires, TOtherProvides>) => Layer<TRequires | TOtherRequires, TProvides | TOtherProvides>;
|
|
271
|
+
merge: <TOtherRequires extends AnyTag, TOtherProvides extends AnyTag>(other: Layer<TOtherRequires, TOtherProvides>) => Layer<Exclude<TRequires | TOtherRequires, TProvides | TOtherProvides>, TProvides | TOtherProvides>;
|
|
271
272
|
}
|
|
272
273
|
/**
|
|
273
274
|
* Consolidated Layer API for creating and composing dependency layers.
|
|
@@ -352,6 +353,46 @@ declare const Layer: {
|
|
|
352
353
|
* ```
|
|
353
354
|
*/
|
|
354
355
|
value<T extends AnyTag>(tag: T, value: TagType<T>): Layer<never, T>;
|
|
356
|
+
/**
|
|
357
|
+
* Creates a layer with a mock implementation for testing.
|
|
358
|
+
*
|
|
359
|
+
* Similar to `Layer.value()`, but allows partial implementations for ServiceTags,
|
|
360
|
+
* making it easier to create test mocks without satisfying constructor dependencies.
|
|
361
|
+
*
|
|
362
|
+
* **Use this for testing only.** For production code, use `Layer.value()` or `Layer.service()`.
|
|
363
|
+
*
|
|
364
|
+
* @param tag - The tag (ServiceTag or ValueTag) to register
|
|
365
|
+
* @param implementation - The mock implementation (can be partial for ServiceTags)
|
|
366
|
+
*
|
|
367
|
+
* @example ServiceTag with partial mock
|
|
368
|
+
* ```typescript
|
|
369
|
+
* class UserService {
|
|
370
|
+
* constructor(private db: Database) {}
|
|
371
|
+
* getUsers() { return this.db.query('SELECT * FROM users'); }
|
|
372
|
+
* }
|
|
373
|
+
*
|
|
374
|
+
* // Mock only the methods you need - no need to satisfy constructor
|
|
375
|
+
* const testLayer = Layer.mock(UserService, {
|
|
376
|
+
* getUsers: () => Promise.resolve([{ id: 1, name: 'Alice' }])
|
|
377
|
+
* });
|
|
378
|
+
* ```
|
|
379
|
+
*
|
|
380
|
+
* @example ServiceTag with full mock instance
|
|
381
|
+
* ```typescript
|
|
382
|
+
* const mockUserService = {
|
|
383
|
+
* getUsers: () => Promise.resolve([])
|
|
384
|
+
* } as UserService;
|
|
385
|
+
*
|
|
386
|
+
* const testLayer = Layer.mock(UserService, mockUserService);
|
|
387
|
+
* ```
|
|
388
|
+
*
|
|
389
|
+
* @example ValueTag (works same as Layer.value)
|
|
390
|
+
* ```typescript
|
|
391
|
+
* const ConfigTag = Tag.of('config')<{ port: number }>();
|
|
392
|
+
* const testLayer = Layer.mock(ConfigTag, { port: 3000 });
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
mock<T extends AnyTag>(tag: T, implementation: T extends ServiceTag ? Partial<TagType<T>> | TagType<T> : TagType<T>): Layer<never, T>;
|
|
355
396
|
/**
|
|
356
397
|
* Creates a custom layer with full control over the factory logic.
|
|
357
398
|
*
|
|
@@ -403,8 +444,11 @@ declare const Layer: {
|
|
|
403
444
|
/**
|
|
404
445
|
* Merges multiple layers at once.
|
|
405
446
|
*
|
|
447
|
+
* Requirements satisfied internally (by any merged layer's provisions)
|
|
448
|
+
* are subtracted from the result's requirements.
|
|
449
|
+
*
|
|
406
450
|
* @param layers - At least 2 layers to merge
|
|
407
|
-
* @returns A layer combining all requirements
|
|
451
|
+
* @returns A layer combining all provisions, with internally-satisfied requirements removed
|
|
408
452
|
*
|
|
409
453
|
* @example
|
|
410
454
|
* ```typescript
|
|
@@ -415,12 +459,12 @@ declare const Layer: {
|
|
|
415
459
|
* );
|
|
416
460
|
* ```
|
|
417
461
|
*/
|
|
418
|
-
mergeAll<T extends readonly [AnyLayer, AnyLayer, ...AnyLayer[]]>(...layers: T): Layer<UnionOfRequires<T>, UnionOfProvides<T>>;
|
|
462
|
+
mergeAll<T extends readonly [AnyLayer, AnyLayer, ...AnyLayer[]]>(...layers: T): Layer<Exclude<UnionOfRequires<T>, UnionOfProvides<T>>, UnionOfProvides<T>>;
|
|
419
463
|
/**
|
|
420
464
|
* Merges exactly two layers.
|
|
421
465
|
* Equivalent to `layer1.merge(layer2)`.
|
|
422
466
|
*/
|
|
423
|
-
merge<TRequires1 extends AnyTag, TProvides1 extends AnyTag, TRequires2 extends AnyTag, TProvides2 extends AnyTag>(layer1: Layer<TRequires1, TProvides1>, layer2: Layer<TRequires2, TProvides2>): Layer<TRequires1 | TRequires2, TProvides1 | TProvides2>;
|
|
467
|
+
merge<TRequires1 extends AnyTag, TProvides1 extends AnyTag, TRequires2 extends AnyTag, TProvides2 extends AnyTag>(layer1: Layer<TRequires1, TProvides1>, layer2: Layer<TRequires2, TProvides2>): Layer<Exclude<TRequires1 | TRequires2, TProvides1 | TProvides2>, TProvides1 | TProvides2>;
|
|
424
468
|
};
|
|
425
469
|
|
|
426
470
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -793,9 +793,6 @@ function createLayer(applyFn) {
|
|
|
793
793
|
provide(dependency) {
|
|
794
794
|
return createProvidedLayer(dependency, layerImpl);
|
|
795
795
|
},
|
|
796
|
-
provideMerge(dependency) {
|
|
797
|
-
return createComposedLayer(dependency, layerImpl);
|
|
798
|
-
},
|
|
799
796
|
merge(other) {
|
|
800
797
|
return createMergedLayer(layerImpl, other);
|
|
801
798
|
}
|
|
@@ -804,52 +801,38 @@ function createLayer(applyFn) {
|
|
|
804
801
|
}
|
|
805
802
|
/**
|
|
806
803
|
* Creates a layer that only exposes the target's provisions.
|
|
804
|
+
*
|
|
805
|
+
* Runtime is identical to merge: both layers' factories are added to the
|
|
806
|
+
* builder, and resolutions across them work transparently. Only the result
|
|
807
|
+
* type narrows provisions to the target's.
|
|
807
808
|
* @internal
|
|
808
809
|
*/
|
|
809
810
|
function createProvidedLayer(dependency, target) {
|
|
810
|
-
return
|
|
811
|
-
}
|
|
812
|
-
/**
|
|
813
|
-
* Creates a composed layer that exposes both layers' provisions.
|
|
814
|
-
* @internal
|
|
815
|
-
*/
|
|
816
|
-
function createComposedLayer(dependency, target) {
|
|
817
|
-
return {
|
|
818
|
-
apply: (builder) => {
|
|
819
|
-
const withDep = dependency.apply(builder);
|
|
820
|
-
return target.apply(withDep);
|
|
821
|
-
},
|
|
822
|
-
provide(dep) {
|
|
823
|
-
return createProvidedLayer(dep, this);
|
|
824
|
-
},
|
|
825
|
-
provideMerge(dep) {
|
|
826
|
-
return createComposedLayer(dep, this);
|
|
827
|
-
},
|
|
828
|
-
merge(other) {
|
|
829
|
-
return createMergedLayer(this, other);
|
|
830
|
-
}
|
|
831
|
-
};
|
|
811
|
+
return createMergedLayer(dependency, target);
|
|
832
812
|
}
|
|
833
813
|
/**
|
|
834
|
-
* Creates a merged layer from two
|
|
814
|
+
* Creates a merged layer from two layers, exposing both layers' provisions.
|
|
815
|
+
*
|
|
816
|
+
* Requirements satisfied internally (by either layer's provisions) are
|
|
817
|
+
* subtracted from the result's requirements at the type level. The runtime
|
|
818
|
+
* behavior is unchanged - both layers' factories are added to the builder
|
|
819
|
+
* and resolutions are wired transparently in either direction.
|
|
835
820
|
* @internal
|
|
836
821
|
*/
|
|
837
822
|
function createMergedLayer(layer1, layer2) {
|
|
838
|
-
|
|
823
|
+
const merged = {
|
|
839
824
|
apply: (builder) => {
|
|
840
825
|
const with1 = layer1.apply(builder);
|
|
841
826
|
return layer2.apply(with1);
|
|
842
827
|
},
|
|
843
828
|
provide(dep) {
|
|
844
|
-
return createProvidedLayer(dep,
|
|
845
|
-
},
|
|
846
|
-
provideMerge(dep) {
|
|
847
|
-
return createComposedLayer(dep, this);
|
|
829
|
+
return createProvidedLayer(dep, merged);
|
|
848
830
|
},
|
|
849
831
|
merge(other) {
|
|
850
|
-
return createMergedLayer(
|
|
832
|
+
return createMergedLayer(merged, other);
|
|
851
833
|
}
|
|
852
834
|
};
|
|
835
|
+
return merged;
|
|
853
836
|
}
|
|
854
837
|
/**
|
|
855
838
|
* Consolidated Layer API for creating and composing dependency layers.
|
|
@@ -894,6 +877,11 @@ const Layer = {
|
|
|
894
877
|
return builder.add(tag, () => value);
|
|
895
878
|
});
|
|
896
879
|
},
|
|
880
|
+
mock(tag, implementation) {
|
|
881
|
+
return createLayer((builder) => {
|
|
882
|
+
return builder.add(tag, () => implementation);
|
|
883
|
+
});
|
|
884
|
+
},
|
|
897
885
|
create(options) {
|
|
898
886
|
const layer = {
|
|
899
887
|
apply: (builder) => {
|
|
@@ -902,9 +890,6 @@ const Layer = {
|
|
|
902
890
|
provide(dep) {
|
|
903
891
|
return createProvidedLayer(dep, layer);
|
|
904
892
|
},
|
|
905
|
-
provideMerge(dep) {
|
|
906
|
-
return createComposedLayer(dep, layer);
|
|
907
|
-
},
|
|
908
893
|
merge(other) {
|
|
909
894
|
return createMergedLayer(layer, other);
|
|
910
895
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sandly",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"typescript",
|
|
6
6
|
"sandly",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"lint:check": "eslint . --max-warnings=0",
|
|
50
50
|
"type:check": "tsc --noEmit",
|
|
51
51
|
"test": "vitest run",
|
|
52
|
+
"test:coverage": "vitest run --coverage",
|
|
52
53
|
"tag": "git tag v$(node -p \"require('./package.json').version\") && git push --tags",
|
|
53
54
|
"release": "changeset version && changeset publish"
|
|
54
55
|
},
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
"@changesets/cli": "^2.29.5",
|
|
57
58
|
"@eslint/js": "^9.26.0",
|
|
58
59
|
"@types/node": "^22.15.3",
|
|
60
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
59
61
|
"eslint": "^9.26.0",
|
|
60
62
|
"prettier": "^3.5.3",
|
|
61
63
|
"prettier-plugin-organize-imports": "^4.1.0",
|