From 0684e61cf39a57426f2c8e96a2b2ff7ad423a3aa Mon Sep 17 00:00:00 2001 From: yousif-khalil-abdulkarim <118832973+yousif-khalil-abdulkarim@users.noreply.github.com> Date: Sun, 5 Jan 2025 01:58:27 +0100 Subject: [PATCH] Fix/docs (#63) * Enhanced the IEventBusManager * renamed eventBusTestSuite to eventBusAdapterTestSuite * Enhanced the IStorageManager * renamed storageTestSuite to storageAdapterTestSuite * Added extra documentation and updated som documentation * Added changeset file --- .changeset/ten-eagles-refuse.md | 7 + .../contracts/async-collection.contract.ts | 262 +++++++++++++++--- .../contracts/collection.contract.ts | 258 ++++++++++++++--- src/event-bus/contracts/_shared.ts | 10 + .../contracts/event-bus-manager.contract.ts | 128 ++++++++- src/event-bus/contracts/event-bus.contract.ts | 74 +++-- .../event-bus-adapter.test-suite.ts | 21 +- .../event-bus/event-bus-manager.ts | 49 ++-- .../memory-event-bus-adapter.test.ts | 4 +- .../redis-event-bus-adapter.test.ts | 4 +- .../contracts/serializer.contract.ts | 3 - .../contracts/storage-adapter.contract.ts | 23 +- .../contracts/storage-events.contract.ts | 1 + .../contracts/storage-manager.contract.ts | 106 ++++++- src/storage/contracts/storage.contract.ts | 79 +++--- .../storage-adapter.test-suite.ts | 20 +- .../memory-storage-adapter.test.ts | 4 +- .../mongodb-storage-adapter.test.ts | 4 +- .../redis-storage-adapter.test.ts | 4 +- .../libsql-storage-adapter.test.ts | 4 +- .../sqlite-storage-adapter.test.ts | 4 +- .../storage/storage-manager.ts | 54 ++-- .../storage/with-event-storage-adapter.ts | 29 +- 23 files changed, 918 insertions(+), 234 deletions(-) create mode 100644 .changeset/ten-eagles-refuse.md diff --git a/.changeset/ten-eagles-refuse.md b/.changeset/ten-eagles-refuse.md new file mode 100644 index 0000000..3497168 --- /dev/null +++ b/.changeset/ten-eagles-refuse.md @@ -0,0 +1,7 @@ +--- +"@daiso-tech/core": patch +--- + +## Changes +- Improved the IEventBusManager +- Improved the IStorageManager diff --git a/src/collection/contracts/async-collection.contract.ts b/src/collection/contracts/async-collection.contract.ts index 1131877..46eee1c 100644 --- a/src/collection/contracts/async-collection.contract.ts +++ b/src/collection/contracts/async-collection.contract.ts @@ -44,12 +44,6 @@ export type AsyncCollapse = TValue extends /** * The IAsyncCollection contract offers a fluent and efficient approach to working with {@link AsyncIterable} objects. * IAsyncCollection is immutable. - * @throws {CollectionError} {@link CollectionError} - * @throws {UnexpectedCollectionError} - * @throws {ItemNotFoundCollectionError} - * @throws {MultipleItemsFoundCollectionError} - * @throws {TypeCollectionError} - * @throws {EmptyCollectionError} * @group Contracts */ export type IAsyncCollection = AsyncIterable & { @@ -76,12 +70,14 @@ export type IAsyncCollection = AsyncIterable & { /** * The filter method filters the collection using predicateFn, keeping only those items that pass predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5, 6]); * const filtered = collection.filter(item => 2 < item && item < 5); * await filtered.toArray(); * // [3, 4] + * ``` */ filter( predicateFn: AsyncPredicate, TOutput>, @@ -90,12 +86,14 @@ export type IAsyncCollection = AsyncIterable & { /** * The reject method filters the collection using predicateFn, keeping only those items that not pass predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5, 6]); * const filtered = collection.reject(item => 2 < item && item < 5); * await filtered.toArray(); * // [1, 2, 5, 6] + * ``` */ reject( predicateFn: AsyncPredicate, TOutput>, @@ -105,12 +103,14 @@ export type IAsyncCollection = AsyncIterable & { * The map method iterates through the collection and passes each item to mapFn. * The mapFn is free to modify the item and return it, thus forming a new collection of modified items. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5]); * const mapped = collection.map(item => item * 2); * await mapped.toArray(); * // [2, 4, 6, 8, 10] + * ``` */ map( mapFn: AsyncMap, TOutput>, @@ -120,12 +120,15 @@ export type IAsyncCollection = AsyncIterable & { * The reduce method executes reduceFn function on each item of the array, passing in the return value from the calculation on the preceding item. * The final result of running the reducer across all items of the array is a single value. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3]); * await collection.reduce((sum, item) => sum + item); * // 6 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "c"]); @@ -137,6 +140,7 @@ export type IAsyncCollection = AsyncIterable & { * {} as Record * ); * // { 0: "a", 1: "b", 2: "c" } + * ``` */ reduce( reduceFn: AsyncReduce, TInput>, @@ -155,29 +159,35 @@ export type IAsyncCollection = AsyncIterable & { * The join method joins the collection's items with separator . An error will be thrown when if a none string item is encounterd. * @throws {TypeCollectionError} * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.map(item => item.toString()).join(); * // "1,2,3,4" + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.map(item => item.toString()).join("_"); * // "1_2_3_4" + * ``` */ join(separator?: string): PromiseLike>; /** * The collapse method collapses a collection of iterables into a single, flat collection. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([[1, 2], [3, 4]]); * const collapsed = collection.collapse(); * await collapsed.toArray(); * // [1, 2, 3, 4] + * ``` */ collapse(): IAsyncCollection>; @@ -185,11 +195,13 @@ export type IAsyncCollection = AsyncIterable & { * The flatMap method returns a new array formed by applying mapFn to each item of the array, and then collapses the result by one level. * It is identical to a map method followed by a collapse method. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([["a", "b"], ["c", "d"]]).flatMap(item => [item.length, ...item]); * await collection.toArray(); * // [2, "a", "b", 2, "c", "d"] + * ``` */ flatMap( mapFn: AsyncMap, Iterable>, @@ -198,12 +210,14 @@ export type IAsyncCollection = AsyncIterable & { /** * The change method changes only the items that passes predicateFn using mapFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5]); * const newCollection = collection.change(item => item % 2 === 0, item => item * 2); * await newCollection.toArray(); * // [1, 4, 3, 8, 5] + * ``` */ change( predicateFn: AsyncPredicate< @@ -217,24 +231,27 @@ export type IAsyncCollection = AsyncIterable & { /** * The page method returns a new collection containing the items that would be present on page with custom pageSize . * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5, 6, 7, 8, 9]); * const page = collection.page(2, 3); * await page.toArray(); * // [4, 5, 6] + * ``` */ page(page: number, pageSize: number): IAsyncCollection; /** * The sum method returns the sum of all items in the collection. If the collection includes other than number items an error will be thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3]); * await collection.sum(); * // 6 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -244,12 +261,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The average method returns the average of all items in the collection. If the collection includes other than number items an error will be thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3]); * await collection.average(); * // 2 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -259,12 +277,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The median method returns the median of all items in the collection. If the collection includes other than number items an error will be thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3]); * await collection.median(); * // 2 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -274,12 +293,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The min method returns the min of all items in the collection. If the collection includes other than number items an error will be thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3]); * await collection.min(); * // 1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -289,12 +309,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The max method returns the max of all items in the collection. If the collection includes other than number items an error will be thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3]); * await collection.max(); * // 3 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -304,12 +325,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The percentage method may be used to quickly determine the percentage of items in the collection that pass predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 1, 2, 2, 2, 3]); * await collection.percentage(value => value === 1); * // 33.333 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} */ @@ -320,12 +342,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The some method determines whether at least one item in the collection matches predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([0, 1, 2, 3, 4, 5]); * await collection.some(item => item === 1); * // true - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ some( @@ -335,12 +358,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The every method determines whether all items in the collection matches predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([0, 1, 2, 3, 4, 5]); * await collection.every(item => item < 6); * // true - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ every( @@ -350,31 +374,37 @@ export type IAsyncCollection = AsyncIterable & { /** * The take method takes the first limit items. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([0, 1, 2, 3, 4, 5]); * const chunk = collection.take(3); * await chunk.toArray(); * // [0, 1, 2] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([0, 1, 2, 3, 4, 5]); * const chunk = collection.take(-2); * await chunk.toArray(); * // [0, 1, 2, 3] + * ``` */ take(limit: number): IAsyncCollection; /** * The takeUntil method takes items until predicateFn returns true. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * const chunk = collection.takeUntil(item => item >= 3); * await chunk.toArray(); * // [1, 2] + * ``` */ takeUntil( predicateFn: AsyncPredicate>, @@ -383,12 +413,14 @@ export type IAsyncCollection = AsyncIterable & { /** * The takeWhile method takes items until predicateFn returns false. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * const chunk = collection.takeWhile(item => item < 4); * await chunk.toArray(); * // [1, 2, 3] + * ``` */ takeWhile( predicateFn: AsyncPredicate>, @@ -397,22 +429,26 @@ export type IAsyncCollection = AsyncIterable & { /** * The skip method skips the first offset items. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).skip(4); * await collection.toArray(); * // [5, 6, 7, 8, 9, 10] + * ``` */ skip(offset: number): IAsyncCollection; /** * The skipUntil method skips items until predicateFn returns true. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]).skipUntil(item => item >= 3); * await collection.toArray(); * // [3, 4] + * ``` */ skipUntil( predicateFn: AsyncPredicate>, @@ -421,11 +457,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The skipWhile method skips items until predicateFn returns false. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]).skipWhile(item => item <= 3); * await collection.toArray(); * // [4] + * ``` */ skipWhile( predicateFn: AsyncPredicate>, @@ -434,6 +472,7 @@ export type IAsyncCollection = AsyncIterable & { /** * The when method will execute callback when condition evaluates to true. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]) @@ -441,6 +480,7 @@ export type IAsyncCollection = AsyncIterable & { * .when(false, collection => collection.append([20])); * await collection.toArray(); * // [1, 2, 3, 4, -3] + * ``` */ when( condition: boolean, @@ -453,19 +493,23 @@ export type IAsyncCollection = AsyncIterable & { /** * The whenEmpty method will execute callback when the collection is empty. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([]) * .whenEmpty(collection => collection.append([-3])) * await collection.toArray(); * // [-3] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1]) * .whenEmpty(collection => collection.append([-3])) * await collection.toArray(); * // [1] + * ``` */ whenEmpty( callback: AsyncModifier< @@ -477,6 +521,7 @@ export type IAsyncCollection = AsyncIterable & { /** * The whenNot method will execute callback when condition evaluates to false. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]) @@ -484,6 +529,7 @@ export type IAsyncCollection = AsyncIterable & { * .whenNot(false, collection => collection.append([20])); * await collection.toArray(); * // [1, 2, 3, 4, 20] + * ``` */ whenNot( condition: boolean, @@ -496,19 +542,23 @@ export type IAsyncCollection = AsyncIterable & { /** * The whenNotEmpty method will execute callback when the collection is not empty. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([]) * .whenNotEmpty(collection => collection.append([-3])) * await collection.toArray(); * // [] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1]) * .whenNotEmpty(collection => collection.append([-3])) * await collection.toArray(); * // [1, -3] + * ``` */ whenNotEmpty( callback: AsyncModifier< @@ -521,6 +571,7 @@ export type IAsyncCollection = AsyncIterable & { * The pipe method passes the orignal collection to callback and returns the result from callback. * This method is useful when you want compose multiple smaller functions. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, "2", "a", 1, 3, {}]); @@ -537,6 +588,7 @@ export type IAsyncCollection = AsyncIterable & { * const piped = await collection.pipe(toNbrs).then(nbrToStr); * console.log(piped); * // [ 1, 2, 1, 3 ] + * ``` */ pipe( callback: AsyncTransform, TOutput>, @@ -545,6 +597,7 @@ export type IAsyncCollection = AsyncIterable & { /** * The tap method passes a copy of the original collection to callback, allowing you to do something with the items while not affecting the original collection. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = await new AsyncIterableCollection([1, 2, 3, 4, 5, 6]) @@ -555,6 +608,7 @@ export type IAsyncCollection = AsyncIterable & { * }) * .toArray(); * // [1, 2, 3, 4, 5, 6] + * ``` */ tap(callback: AsyncTap>): IAsyncCollection; @@ -562,12 +616,14 @@ export type IAsyncCollection = AsyncIterable & { * The chunk method breaks the collection into multiple, smaller collections of size chunkSize. * If chunkSize is not divisible with total number of items then the last chunk will contain the remaining items. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5, 6, 7]); * const chunks = collection.chunk(4); * await chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2, 3, 4], [5, 6, 7]] + * ``` */ chunk(chunkSize: number): IAsyncCollection>; @@ -575,6 +631,7 @@ export type IAsyncCollection = AsyncIterable & { * The chunkWhile method breaks the collection into multiple, smaller collections based on the evaluation of predicateFn. * The chunk variable passed to the predicateFn may be used to inspect the previous item. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection("AABBCCCD"); @@ -583,6 +640,7 @@ export type IAsyncCollection = AsyncIterable & { * }); * await chunks.map(chunk => chunk.toArray()).toArray(); * // [["A", "A"], ["B", "B"], ["C", "C", "C"], ["D"]] + * ``` */ chunkWhile( predicateFn: AsyncPredicate>, @@ -591,20 +649,25 @@ export type IAsyncCollection = AsyncIterable & { /** * The split method breaks a collection evenly into chunkAmount of chunks. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5]); * const chunks = collection.split(3); * await chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2], [3, 4], [5]] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5, 6]); * const chunks = collection.split(3); * await chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2], [3, 4], [5, 6]] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5, 6, 7]); @@ -617,12 +680,14 @@ export type IAsyncCollection = AsyncIterable & { /** * The partition method is used to separate items that pass predicateFn from those that do not. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5, 6]); * const chunks = collection.partition(item => item < 3); * await chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2], [3, 4, 5, 6]] + * ``` */ partition( predicateFn: AsyncPredicate>, @@ -631,12 +696,14 @@ export type IAsyncCollection = AsyncIterable & { /** * The sliding method returns a new collection of chunks representing a "sliding window" view of the items in the collection. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5]) * const chunks = collection.sliding(2); * await chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2], [2, 3], [3, 4], [4, 5]] + * ``` */ sliding( chunkSize: number, @@ -647,6 +714,7 @@ export type IAsyncCollection = AsyncIterable & { * The groupBy method groups the collection's items by selectFn . * By default the equality check occurs on the item. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "a", "a", "b", "b", "c"]); @@ -668,7 +736,9 @@ export type IAsyncCollection = AsyncIterable & { * // ["c"] * // ] * // ] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["alice@gmail.com", "bob@yahoo.com", "carlos@gmail.com"]); @@ -686,6 +756,7 @@ export type IAsyncCollection = AsyncIterable & { * // ["bob@yahoo.com"] * // ] * // ] + * ``` */ groupBy( selectFn?: AsyncMap, TOutput>, @@ -695,6 +766,7 @@ export type IAsyncCollection = AsyncIterable & { * The countBy method counts the occurrences of values in the collection by selectFn . * By default the equality check occurs on the item. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "a", "a", "b", "b", "c"]); @@ -707,7 +779,9 @@ export type IAsyncCollection = AsyncIterable & { * // ["b", 2], * // ["c", 1] * // ] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["alice@gmail.com", "bob@yahoo.com", "carlos@gmail.com"]); @@ -718,6 +792,7 @@ export type IAsyncCollection = AsyncIterable & { * // ["gmail.com", 2], * // ["yahoo.com", 1] * // ] + * ``` */ countBy( selectFn?: AsyncMap, TOutput>, @@ -727,12 +802,15 @@ export type IAsyncCollection = AsyncIterable & { * The unique method removes all duplicate values from the collection by selectFn . * By default the equality check occurs on the item. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 1, 2, 2, 3, 4, 2]); * await collection.unique().toArray(); * // [1, 2, 3, 4] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([ @@ -749,6 +827,7 @@ export type IAsyncCollection = AsyncIterable & { * // { name: "iPhone 6", brand: "Apple", type: "phone" }, * // { name: "Galaxy S6", brand: "Samsung", type: "phone" }, * // ] + * ``` */ unique( selectFn?: AsyncMap, TOutput>, @@ -758,13 +837,16 @@ export type IAsyncCollection = AsyncIterable & { * The difference method will return the values in the original collection that are not present in iterable. * By default the equality check occurs on the item. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 2, 3, 4, 5]); * const difference = collection.difference([2, 4, 6, 8]); * await difference.toArray(); * // [1, 3, 5] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([ @@ -786,6 +868,7 @@ export type IAsyncCollection = AsyncIterable & { * // { name: "iPhone 5", brand: "Apple", type: "phone" }, * // { name: "Galaxy S6", brand: "Samsung", type: "phone" }, * // ] + * ``` */ difference( iterable: AsyncIterableValue, @@ -795,12 +878,14 @@ export type IAsyncCollection = AsyncIterable & { /** * The repeat method will repeat the original collection amount times. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3]); * const newCollection = collection.repeat(3); * await newCollection.toArray(); * // [1, 2, 3, 1, 2, 3, 1, 2, 3] + * ``` */ repeat(amount: number): IAsyncCollection; @@ -808,6 +893,7 @@ export type IAsyncCollection = AsyncIterable & { * The padStart method pads this collection with fillItems until the resulting collection size reaches maxLength. * The padding is applied from the start of this collection. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * await new AsyncIterableCollection("abc").padStart(10, "foo").join(""); @@ -821,6 +907,7 @@ export type IAsyncCollection = AsyncIterable & { * * await new AsyncIterableCollection("abc").padStart(1, "_").join(""); * // "abc" + * ``` */ padStart( maxLength: number, @@ -831,6 +918,7 @@ export type IAsyncCollection = AsyncIterable & { * The padEnd method pads this collection with fillItems until the resulting collection size reaches maxLength. * The padding is applied from the end of this collection. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * await new AsyncIterableCollection("abc").padEnd(10, "foo").join(""); @@ -844,6 +932,7 @@ export type IAsyncCollection = AsyncIterable & { * * await new AsyncIterableCollection("abc").padEnd(1, "_").join(""); * // "abc" + * ``` */ padEnd( maxLength: number, @@ -854,52 +943,66 @@ export type IAsyncCollection = AsyncIterable & { * The slice method creates porition of the original collection selected from start and end * where start and end (end not included) represent the index of items in the collection. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "c", "d", "e", "f"]); * await collection.slice(3).toArray(); * // ["d", "e", "f"] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "c", "d", "e", "f"]); * await collection.slice(undefined, 2).toArray(); * // ["a", "b"] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "c", "d", "e", "f"]); * await collection.slice(2, 5).toArray(); * // ["c", "d", "e"] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "c", "d", "e", "f"]); * await collection.slice(-2).toArray(); * // ["e", "f"] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "c", "d", "e", "f"]); * await collection.slice(undefined, -2).toArray(); * // ["a", "b", "c", "d"] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "c", "d", "e", "f"]); * await collection.slice(-4, -2).toArray(); * // ["c", "d"] + * ``` */ slice(start?: number, end?: number): IAsyncCollection; /** * The prepend method adds iterable to the beginning of the collection. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5]).prepend([-1, 20]); * await collection.toArray(); * // [-1, 20, 1, 2, 3, 4, 5] + * ``` */ prepend( iterable: AsyncIterableValue, @@ -908,11 +1011,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The append method adds iterable to the end of the collection. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5]).append([-1, -2]); * await collection.toArray(); * // [1, 2, 3, 4, 5, -1, -2,] + * ``` */ append( iterable: AsyncIterableValue, @@ -921,11 +1026,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The insertBefore method adds iterable before the first item that matches predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 2, 3, 4, 5]).insertBefore(item => item === 2, [-1, 20]); * await collection.toArray(); * // [1, -1, 20, 2, 2, 3, 4, 5] + * ``` */ insertBefore( predicateFn: AsyncPredicate>, @@ -935,11 +1042,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The insertAfter method adds iterable after the first item that matches predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 2, 3, 4, 5]).insertAfter(item => item === 2, [-1, 20]); * await collection.toArray(); * // [1, 2, -1, 20, 2, 3, 4, 5] + * ``` */ insertAfter( predicateFn: AsyncPredicate>, @@ -949,6 +1058,7 @@ export type IAsyncCollection = AsyncIterable & { /** * The crossJoin method cross joins the collection's values among iterables, returning a Cartesian product with all possible permutations. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2]); @@ -960,7 +1070,9 @@ export type IAsyncCollection = AsyncIterable & { * // [2, "a"], * // [2, "b"], * // ] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2]); @@ -976,6 +1088,7 @@ export type IAsyncCollection = AsyncIterable & { * // [2, "b", "I"], * // [2, "b", "II"], * // ] + * ``` */ crossJoin( iterable: AsyncIterableValue, @@ -985,26 +1098,32 @@ export type IAsyncCollection = AsyncIterable & { * The zip method merges together the values of iterable with the values of the collection at their corresponding index. * The returned collection has size of the shortest collection. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["Chair", "Desk"]); * const zipped = collection.zip([100, 200]); * await zipped.toArray(); * // [["Chair", 100], ["Desk", 200]] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["Chair", "Desk", "Couch"]); * const zipped = collection.zip([100, 200]); * await zipped.toArray(); * // [["Chair", 100], ["Desk", 200]] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["Chair", "Desk"]); * const zipped = collection.zip([100, 200, 300]); * await zipped.toArray(); * // [["Chair", 100], ["Desk", 200]] + * ``` */ zip( iterable: AsyncIterableValue, @@ -1013,12 +1132,15 @@ export type IAsyncCollection = AsyncIterable & { /** * The sort method sorts the collection. You can provide a comparator function. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([-1, 2, 4, 3]); * await collection.sort().toArray(); * // [-1, 2, 3, 4] + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([ @@ -1034,6 +1156,7 @@ export type IAsyncCollection = AsyncIterable & { * // { name: "Hasan", age: 25 }, * // { name: "Anders", age: 30 }, * // ] + * ``` */ sort(comparator?: Comparator): IAsyncCollection; @@ -1041,11 +1164,13 @@ export type IAsyncCollection = AsyncIterable & { * The reverse method will reverse the order of the collection. * The reversing of the collection will be applied in chunks that are the size of chunkSize . * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([-1, 2, 4, 3]); * await collection.reverse().toArray(); * // [3, 4, 2, -1] + * ``` */ reverse(chunkSize?: number): IAsyncCollection; @@ -1058,24 +1183,29 @@ export type IAsyncCollection = AsyncIterable & { * The first method returns the first item in the collection that passes predicateFn . * By default it will get the first item. If the collection is empty or no items passes predicateFn than null i returned. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.first(); * // 1 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.first(item => item > 2); * // 3 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.first(item => item > 10); * // null - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * // 3 */ @@ -1087,30 +1217,37 @@ export type IAsyncCollection = AsyncIterable & { * The firstOr method returns the first item in the collection that passes predicateFn * By default it will get the first item. If the collection is empty or no items passes predicateFn than defaultValue . * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.firstOr(-1); * // 1 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.firstOr(-1, item => item > 2); * // 3 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.firstOr(-1, item => item > 10); * // -1 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.firstOr(() => -1, item => item > 10); * // -1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ firstOr( @@ -1122,24 +1259,29 @@ export type IAsyncCollection = AsyncIterable & { * The firstOrFail method returns the first item in the collection that passes predicateFn . * By default it will get the first item. If the collection is empty or no items passes predicateFn than error is thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.firstOrFail(); * // 1 * @example + * ``` + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.firstOrFail(item => item > 2); * // 3 * @example + * ``` + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.firstOrFail(item => item > 10); * // throws an error - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} */ @@ -1151,24 +1293,29 @@ export type IAsyncCollection = AsyncIterable & { * The last method returns the last item in the collection that passes predicateFn . * By default it will get the last item. If the collection is empty or no items passes predicateFn than null i returned. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.last(); * // 4 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.last(item => item < 4); * // 3 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.last(item => item > 10); * // null - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * // 3 */ @@ -1180,30 +1327,37 @@ export type IAsyncCollection = AsyncIterable & { * The lastOr method returns the last item in the collection that passes predicateFn . * By default it will get the last item. If the collection is empty or no items passes predicateFn than defaultValue . * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.lastOr(-1); * // 4 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.lastOr(-1, item => item < 4); * // 3 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.lastOr(-1, item => item > 10); * // -1 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.lastOr(() => -1, item => item > 10); * // -1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ lastOr( @@ -1215,24 +1369,29 @@ export type IAsyncCollection = AsyncIterable & { * The lastOrFail method returns the last item in the collection that passes predicateFn . * By default it will get the last item. If the collection is empty or no items passes predicateFn than error is thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.lastOrFail(); * // 4 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.lastOrFail(item => item < 4); * // 3 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.lastOrFail(item => item > 10); * // throws an error - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} */ @@ -1244,18 +1403,21 @@ export type IAsyncCollection = AsyncIterable & { * The before method returns the item that comes before the first item that matches predicateFn. * If the predicateFn does not match or matches the first item then null is returned. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.before(item => item === 2); * // 1 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.before(item => item === 1); * // null - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ before( @@ -1266,24 +1428,29 @@ export type IAsyncCollection = AsyncIterable & { * The beforeOr method returns the item that comes before the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the first item then defaultValue is returned. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.beforeOr(-1, item => item === 2); * // 1 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.beforeOr(-1, item => item === 1); * // -1 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.beforeOr(() => -1, item => item === 1); * // -1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ beforeOr( @@ -1295,18 +1462,21 @@ export type IAsyncCollection = AsyncIterable & { * The beforeOrFail method returns the item that comes before the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the first item then an error is thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.beforeOrFail(item => item === 2); * // 1 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.beforeOrFail(item => item === 1); * // error is thrown - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} */ @@ -1318,18 +1488,21 @@ export type IAsyncCollection = AsyncIterable & { * The after method returns the item that comes after the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the last item then null is returned. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.after(item => item === 2); * // 3 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.after(item => item === 4); * // null - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ after( @@ -1340,24 +1513,29 @@ export type IAsyncCollection = AsyncIterable & { * The afterOr method returns the item that comes after the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the last item then defaultValue is returned. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.afterOr(-1, item => item === 2); * // 3 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.afterOr(-1, item => item === 4); * // -1 * @example + * ``` + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.afterOr(() => -1, item => item === 4); * // -1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ afterOr( @@ -1369,18 +1547,21 @@ export type IAsyncCollection = AsyncIterable & { * The afterOrFail method returns the item that comes after the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the last item then an error is thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.afterOrFail(item => item === 2); * // 3 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4]); * await collection.afterOrFail(item => item === 4); * // error is thrown - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} */ @@ -1392,24 +1573,29 @@ export type IAsyncCollection = AsyncIterable & { * The sole method returns the first item in the collection that passes predicateFn, but only if predicateFn matches exactly one item. * If no items matches or multiple items are found an error will be thrown. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5]); * await collection.sole(item => item === 4); * // 4 + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 4, 5]); * await collection.sole(item => item === 4); * // error is thrown + * ``` * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 5]); * await collection.sole(item => item === 4); * // error is thrown - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} * @throws {MultipleItemsFoundCollectionError} {@link MultipleItemsFoundCollectionError} @@ -1421,23 +1607,26 @@ export type IAsyncCollection = AsyncIterable & { /** * The nth method creates a new collection consisting of every n-th item. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "c", "d", "e", "f"]).nth(4); * await collection.toArray(); * // ["a", "e"] + * ``` */ nth(step: number): IAsyncCollection; /** * The count method returns the total number of items in the collection that passes predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection([1, 2, 3, 4, 5, 6]); * await collection.count(value => value % 2 === 0); * // 3 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ count( @@ -1446,21 +1635,18 @@ export type IAsyncCollection = AsyncIterable & { /** * The size returns the size of the collection. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ size(): PromiseLike; /** * The isEmpty returns true if the collection is empty. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ isEmpty(): PromiseLike; /** * The isNotEmpty returns true if the collection is not empty. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ isNotEmpty(): PromiseLike; @@ -1468,12 +1654,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The searchFirst return the index of the first item that matches predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "b", "c"]); * await collection.searchFirst(item => item === "b"); * // 1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ searchFirst( @@ -1483,12 +1670,13 @@ export type IAsyncCollection = AsyncIterable & { /** * The searchLast return the index of the last item that matches predicateFn. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * const collection = new AsyncIterableCollection(["a", "b", "b", "c"]); * await collection.searchLast(item => item === "b"); * // 2 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ searchLast( @@ -1497,7 +1685,6 @@ export type IAsyncCollection = AsyncIterable & { /** * The forEach method iterates through all items in the collection. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ forEach( @@ -1506,7 +1693,6 @@ export type IAsyncCollection = AsyncIterable & { /** * The toArray method converts the collection to a new array. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ toArray(): PromiseLike; @@ -1515,6 +1701,7 @@ export type IAsyncCollection = AsyncIterable & { * The delay method will add delay between each iteration. * This method is especially useful for situations where you may be interacting with external APIs that rate limit incoming requests: * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * // An iterator that will fetch all users from a specific api @@ -1522,12 +1709,14 @@ export type IAsyncCollection = AsyncIterable & { * const apiIterator = new ApiIterator(); * const collection = new AsyncIterableCollection(apiIterator); * await collection.delay(1000).forEach(user => console.log(user)) + * ``` */ delay(time: TimeSpan): IAsyncCollection; /** * The abort method will abort iteration of the collection by passing {@link AbortSignal}. * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * // An iterator that will fetch all users from a specific api @@ -1535,6 +1724,7 @@ export type IAsyncCollection = AsyncIterable & { * const apiIterator = new ApiIterator(); * const collection = new AsyncIterableCollection(apiIterator); * await collection.delay(1000).forEach(user => console.log(user)) + * ``` */ takeUntilAbort( abortSignal: AbortSignal, @@ -1545,6 +1735,7 @@ export type IAsyncCollection = AsyncIterable & { * The timeout method returns a new collection that will iterate values until the specified time. * After that time, the collection will then stop iterating: * @example + * ```ts * import { AsyncIterableCollection } from "@daiso-tech/core";; * * class AsyncInfiniteIterable implements AsyncIterable { @@ -1559,6 +1750,7 @@ export type IAsyncCollection = AsyncIterable & { * await collection * .timeout(1000) * .forEach(nbr => console.log(nbr)) + * ``` */ takeUntilTimeout( timeInMs: TimeSpan, diff --git a/src/collection/contracts/collection.contract.ts b/src/collection/contracts/collection.contract.ts index d0dbe83..23cd740 100644 --- a/src/collection/contracts/collection.contract.ts +++ b/src/collection/contracts/collection.contract.ts @@ -3,8 +3,6 @@ */ import type { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - CollectionError, Comparator, Predicate, ForEach, @@ -38,12 +36,6 @@ export type Collapse = TValue extends /** * The ICollection contract offers a fluent and efficient approach to working with {@link Iterable} objects. * ICollection is immutable. - * @throws {CollectionError} - * @throws {UnexpectedCollectionError} - * @throws {ItemNotFoundCollectionError} - * @throws {MultipleItemsFoundCollectionError} - * @throws {TypeCollectionError} - * @throws {EmptyCollectionError} * @group Contracts */ export type ICollection = Iterable & { @@ -70,12 +62,14 @@ export type ICollection = Iterable & { /** * The filter method filters the collection using predicateFn, keeping only those items that pass predicateFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6]); * const filtered = collection.filter(item => 2 < item && item < 5); * filtered.toArray(); * // [3, 4] + * ``` */ filter( predicateFn: Predicate, TOutput>, @@ -84,12 +78,14 @@ export type ICollection = Iterable & { /** * The reject method filters the collection using predicateFn, keeping only those items that not pass predicateFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6]); * const filtered = collection.reject(item => 2 < item && item < 5); * filtered.toArray(); * // [1, 2, 5, 6] + * ``` */ reject( predicateFn: Predicate, TOutput>, @@ -99,12 +95,14 @@ export type ICollection = Iterable & { * The map method iterates through the collection and passes each item to mapFn. * The mapFn is free to modify the item and return it, thus forming a new collection of modified items. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5]); * const mapped = collection.map(item => item * 2); * mapped.toArray(); * // [2, 4, 6, 8, 10] + * ``` */ map( mapFn: Map, TOutput>, @@ -114,12 +112,15 @@ export type ICollection = Iterable & { * The reduce method executes reduceFn function on each item of the array, passing in the return value from the calculation on the preceding item. * The final result of running the reducer across all items of the array is a single value. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3]); * collection.reduce((sum, item) => sum + item); * // 6 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "c"]); @@ -131,6 +132,7 @@ export type ICollection = Iterable & { * {} as Record * ); * // { 0: "a", 1: "b", 2: "c" } + * ``` */ reduce(reduceFn: Reduce, TInput>): TInput; reduce( @@ -147,29 +149,35 @@ export type ICollection = Iterable & { * The join method joins the collection's items with separator . An error will be thrown when if a none string item is encounterd. * @throws {TypeCollectionError} * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.map(item => item.toString()).join(); * // "1,2,3,4" * @example + * ``` + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.map(item => item.toString()).join("_"); * // "1_2_3_4" + * ``` */ join(separator?: string): Extract; /** * The collapse method collapses a collection of iterables into a single, flat collection. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([[1, 2], [3, 4]]); * const collapsed = collection.collapse(); * collapsed.toArray(); * // [1, 2, 3, 4] + * ``` */ collapse(): ICollection>; @@ -177,11 +185,13 @@ export type ICollection = Iterable & { * The flatMap method returns a new array formed by applying mapFn to each item of the array, and then collapses the result by one level. * It is identical to a map method followed by a collapse method. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([["a", "b"], ["c", "d"]]).flatMap(item => [item.length, ...item]); * collection.toArray(); * // [2, "a", "b", 2, "c", "d"] + * ``` */ flatMap( mapFn: Map, Iterable>, @@ -190,12 +200,14 @@ export type ICollection = Iterable & { /** * The change method changes only the items that passes predicateFn using mapFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5]); * const newCollection = collection.change(item => item % 2 === 0, item => item * 2); * newCollection.toArray(); * // [1, 4, 3, 8, 5] + * ``` */ change( predicateFn: Predicate, TFilterOutput>, @@ -205,12 +217,14 @@ export type ICollection = Iterable & { /** * The page method returns a new collection containing the items that would be present on page with custom pageSize . * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6, 7, 8, 9]); * const page = collection.page(2, 3); * page.toArray(); * // [4, 5, 6] + * ``` */ page(page: number, pageSize: number): ICollection; @@ -218,12 +232,13 @@ export type ICollection = Iterable & { * The sum method returns the sum of all items in the collection. If the collection includes other than number items an error will be thrown. * If the collection is empty an error will also be thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3]); * collection.sum(); * // 6 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -234,12 +249,13 @@ export type ICollection = Iterable & { * The average method returns the average of all items in the collection. If the collection includes other than number items an error will be thrown. * If the collection is empty an error will also be thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3]); * collection.average(); * // 2 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -250,12 +266,13 @@ export type ICollection = Iterable & { * The median method returns the median of all items in the collection. If the collection includes other than number items an error will be thrown. * If the collection is empty an error will also be thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3]); * collection.median(); * // 2 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -266,12 +283,13 @@ export type ICollection = Iterable & { * The min method returns the min of all items in the collection. If the collection includes other than number items an error will be thrown. * If the collection is empty an error will also be thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3]); * collection.min(); * // 1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -282,12 +300,13 @@ export type ICollection = Iterable & { * The max method returns the max of all items in the collection. If the collection includes other than number items an error will be thrown. * If the collection is empty an error will also be thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3]); * collection.max(); * // 3 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {TypeCollectionError} {@link TypeCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} @@ -298,12 +317,13 @@ export type ICollection = Iterable & { * The percentage method may be used to quickly determine the percentage of items in the collection that pass predicateFn. * If the collection is empty an error will also be thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 1, 2, 2, 2, 3]); * collection.percentage(value => value === 1); * // 33.333 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {EmptyCollectionError} {@link EmptyCollectionError} */ @@ -312,12 +332,13 @@ export type ICollection = Iterable & { /** * The some method determines whether at least one item in the collection matches predicateFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([0, 1, 2, 3, 4, 5]); * collection.some(item => item === 1); * // true - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ some( @@ -327,12 +348,13 @@ export type ICollection = Iterable & { /** * The every method determines whether all items in the collection matches predicateFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([0, 1, 2, 3, 4, 5]); * collection.every(item => item < 6); * // true - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ every( @@ -342,31 +364,37 @@ export type ICollection = Iterable & { /** * The take method takes the first limit items. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([0, 1, 2, 3, 4, 5]); * const chunk = collection.take(3); * chunk.toArray(); * // [0, 1, 2] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([0, 1, 2, 3, 4, 5]); * const chunk = collection.take(-2); * chunk.toArray(); * // [0, 1, 2, 3] + * ``` */ take(limit: number): ICollection; /** * The takeUntil method takes items until predicateFn returns true. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * const chunk = collection.takeUntil(item => item >= 3); * chunk.toArray(); * // [1, 2] + * ``` */ takeUntil( predicateFn: Predicate>, @@ -375,12 +403,14 @@ export type ICollection = Iterable & { /** * The takeWhile method takes items until predicateFn returns false. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * const chunk = collection.takeWhile(item => item < 4); * chunk.toArray(); * // [1, 2, 3] + * ``` */ takeWhile( predicateFn: Predicate>, @@ -389,22 +419,26 @@ export type ICollection = Iterable & { /** * The skip method skips the first offset items. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).skip(4); * collection.toArray(); * // [5, 6, 7, 8, 9, 10] + * ``` */ skip(offset: number): ICollection; /** * The skipUntil method skips items until predicateFn returns true. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]).skipUntil(item => item >= 3); * collection.toArray(); * // [3, 4] + * ``` */ skipUntil( predicateFn: Predicate>, @@ -413,11 +447,13 @@ export type ICollection = Iterable & { /** * The skipWhile method skips items until predicateFn returns false. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]).skipWhile(item => item <= 3); * collection.toArray(); * // [4] + * ``` */ skipWhile( predicateFn: Predicate>, @@ -426,6 +462,7 @@ export type ICollection = Iterable & { /** * The when method will execute callback when condition evaluates to true. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]) @@ -433,6 +470,7 @@ export type ICollection = Iterable & { * .when(false, collection => collection.append([20])); * collection.toArray(); * // [1, 2, 3, 4, -3] + * ``` */ when( condition: boolean, @@ -442,19 +480,23 @@ export type ICollection = Iterable & { /** * The whenEmpty method will execute callback when the collection is empty. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([]) * .whenEmpty(collection => collection.append([-3])) * collection.toArray(); * // [-3] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1]) * .whenEmpty(collection => collection.append([-3])) * collection.toArray(); * // [1] + * ``` */ whenEmpty( callback: Modifier, ICollection>, @@ -463,6 +505,7 @@ export type ICollection = Iterable & { /** * The whenNot method will execute callback when condition evaluates to false. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]) @@ -470,6 +513,7 @@ export type ICollection = Iterable & { * .whenNot(false, collection => collection.append([20])); * collection.toArray(); * // [1, 2, 3, 4, 20] + * ``` */ whenNot( condition: boolean, @@ -479,19 +523,23 @@ export type ICollection = Iterable & { /** * The whenNotEmpty method will execute callback when the collection is not empty. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([]) * .whenNotEmpty(collection => collection.append([-3])) * collection.toArray(); * // [] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1]) * .whenNotEmpty(collection => collection.append([-3])) * collection.toArray(); * // [1, -3] + * ``` */ whenNotEmpty( callback: Modifier, ICollection>, @@ -501,6 +549,7 @@ export type ICollection = Iterable & { * The pipe method passes the orignal collection to callback and returns the result from callback. * This method is useful when you want compose multiple smaller functions. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, "2", "a", 1, 3, {}]); @@ -517,6 +566,7 @@ export type ICollection = Iterable & { * const piped = collection.pipe(toNbrs).pipe(nbrToStr); * console.log(piped); * // [ 1, 2, 1, 3 ] + * ``` */ pipe( callback: Transform, TOutput>, @@ -525,6 +575,7 @@ export type ICollection = Iterable & { /** * The tap method passes a copy of the original collection to callback, allowing you to do something with the items while not affecting the original collection. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6]) @@ -535,6 +586,7 @@ export type ICollection = Iterable & { * }) * .toArray(); * // [1, 2, 3, 4, 5, 6] + * ``` */ tap(callback: Tap>): ICollection; @@ -542,12 +594,14 @@ export type ICollection = Iterable & { * The chunk method breaks the collection into multiple, smaller collections of size chunkSize. * If chunkSize is not divisible with total number of items then the last chunk will contain the remaining items. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6, 7]); * const chunks = collection.chunk(4); * chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2, 3, 4], [5, 6, 7]] + * ``` */ chunk(chunkSize: number): ICollection>; @@ -555,6 +609,7 @@ export type ICollection = Iterable & { * The chunkWhile method breaks the collection into multiple, smaller collections based on the evaluation of predicateFn. * The chunk variable passed to the predicateFn may be used to inspect the previous item. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection("AABBCCCD"); @@ -563,6 +618,7 @@ export type ICollection = Iterable & { * }); * chunks.map(chunk => chunk.toArray()).toArray(); * // [["A", "A"], ["B", "B"], ["C", "C", "C"], ["D"]] + * ``` */ chunkWhile( predicateFn: Predicate>, @@ -571,38 +627,46 @@ export type ICollection = Iterable & { /** * The split method breaks a collection evenly into chunkAmount of chunks. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5]); * const chunks = collection.split(3); * chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2], [3, 4], [5]] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6]); * const chunks = collection.split(3); * chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2], [3, 4], [5, 6]] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6, 7]); * const chunks = collection.split(3); * chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2, 7], [3, 4], [5, 6]] + * ``` */ split(chunkAmount: number): ICollection>; /** * The partition method is used to separate items that pass predicateFn from those that do not. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6]); * const chunks = collection.partition(item => item < 3); * chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2], [3, 4, 5, 6]] + * ``` */ partition( predicateFn: Predicate>, @@ -611,12 +675,14 @@ export type ICollection = Iterable & { /** * The sliding method returns a new collection of chunks representing a "sliding window" view of the items in the collection. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5]) * const chunks = collection.sliding(2); * chunks.map(chunk => chunk.toArray()).toArray(); * // [[1, 2], [2, 3], [3, 4], [4, 5]] + * ``` */ sliding(chunkSize: number, step?: number): ICollection>; @@ -624,6 +690,7 @@ export type ICollection = Iterable & { * The groupBy method groups the collection's items by selectFn . * By default the equality check occurs on the item. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "a", "a", "b", "b", "c"]); @@ -645,7 +712,9 @@ export type ICollection = Iterable & { * // ["c"] * // ] * // ] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["alice@gmail.com", "bob@yahoo.com", "carlos@gmail.com"]); @@ -663,6 +732,7 @@ export type ICollection = Iterable & { * // ["bob@yahoo.com"] * // ] * // ] + * ``` */ groupBy( selectFn?: Map, TOutput>, @@ -672,6 +742,7 @@ export type ICollection = Iterable & { * The countBy method counts the occurrences of values in the collection by selectFn . * By default the equality check occurs on the item. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "a", "a", "b", "b", "c"]); @@ -684,7 +755,9 @@ export type ICollection = Iterable & { * // ["b", 2], * // ["c", 1] * // ] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["alice@gmail.com", "bob@yahoo.com", "carlos@gmail.com"]); @@ -695,6 +768,7 @@ export type ICollection = Iterable & { * // ["gmail.com", 2], * // ["yahoo.com", 1] * // ] + * ``` */ countBy( selectFn?: Map, TOutput>, @@ -704,12 +778,15 @@ export type ICollection = Iterable & { * The unique method removes all duplicate values from the collection by selectFn . * By default the equality check occurs on the item. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 1, 2, 2, 3, 4, 2]); * collection.unique().toArray(); * // [1, 2, 3, 4] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([ @@ -726,6 +803,7 @@ export type ICollection = Iterable & { * // { name: "iPhone 6", brand: "Apple", type: "phone" }, * // { name: "Galaxy S6", brand: "Samsung", type: "phone" }, * // ] + * ``` */ unique( selectFn?: Map, TOutput>, @@ -735,13 +813,16 @@ export type ICollection = Iterable & { * The difference method will return the values in the original collection that are not present in iterable. * By default the equality check occurs on the item. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 2, 3, 4, 5]); * const difference = collection.difference([2, 4, 6, 8]); * difference.toArray(); * // [1, 3, 5] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([ @@ -763,6 +844,7 @@ export type ICollection = Iterable & { * // { name: "iPhone 5", brand: "Apple", type: "phone" }, * // { name: "Galaxy S6", brand: "Samsung", type: "phone" }, * // ] + * ``` */ difference( iterable: Iterable, @@ -772,12 +854,14 @@ export type ICollection = Iterable & { /** * The repeat method will repeat the original collection amount times. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3]); * const newCollection = collection.repeat(3); * newCollection.toArray(); * // [1, 2, 3, 1, 2, 3, 1, 2, 3] + * ``` */ repeat(amount: number): ICollection; @@ -785,6 +869,7 @@ export type ICollection = Iterable & { * The padStart method pads this collection with fillItems until the resulting collection size reaches maxLength. * The padding is applied from the start of this collection. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * new ListCollection("abc").padStart(10, "foo").join(""); @@ -798,6 +883,7 @@ export type ICollection = Iterable & { * * new ListCollection("abc").padStart(1, "_").join(""); * // "abc" + * ``` */ padStart( maxLength: number, @@ -808,6 +894,7 @@ export type ICollection = Iterable & { * The padEnd method pads this collection with fillItems until the resulting collection size reaches maxLength. * The padding is applied from the end of this collection. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * new ListCollection("abc").padEnd(10, "foo").join(""); @@ -821,6 +908,7 @@ export type ICollection = Iterable & { * * new ListCollection("abc").padEnd(1, "_").join(""); * // "abc" + * ``` */ padEnd( maxLength: number, @@ -831,52 +919,66 @@ export type ICollection = Iterable & { * The slice method creates porition of the original collection selected from start and end * where start and end (end not included) represent the index of items in the collection. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "c", "d", "e", "f"]); * collection.slice(3).toArray(); * // ["d", "e", "f"] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "c", "d", "e", "f"]); * collection.slice(undefined, 2).toArray(); * // ["a", "b"] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "c", "d", "e", "f"]); * collection.slice(2, 5).toArray(); * // ["c", "d", "e"] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "c", "d", "e", "f"]); * collection.slice(-2).toArray(); * // ["e", "f"] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "c", "d", "e", "f"]); * collection.slice(undefined, -2).toArray(); * // ["a", "b", "c", "d"] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "c", "d", "e", "f"]); * collection.slice(-4, -2).toArray(); * // ["c", "d"] + * ``` */ slice(start?: number, end?: number): ICollection; /** * The prepend method adds iterable to the beginning of the collection. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5]).prepend([-1, 20]); * collection.toArray(); * // [-1, 20, 1, 2, 3, 4, 5] + * ``` */ prepend( iterable: Iterable, @@ -885,11 +987,13 @@ export type ICollection = Iterable & { /** * The append method adds iterable to the end of the collection. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5]).append([-1, -2]); * collection.toArray(); * // [1, 2, 3, 4, 5, -1, -2,] + * ``` */ append( iterable: Iterable, @@ -898,11 +1002,13 @@ export type ICollection = Iterable & { /** * The insertBefore method adds iterable before the first item that matches predicateFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 2, 3, 4, 5]).insertBefore(item => item === 2, [-1, 20]); * collection.toArray(); * // [1, -1, 20, 2, 2, 3, 4, 5] + * ``` */ insertBefore( predicateFn: Predicate>, @@ -912,11 +1018,13 @@ export type ICollection = Iterable & { /** * The insertAfter method adds iterable after the first item that matches predicateFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 2, 3, 4, 5]).insertAfter(item => item === 2, [-1, 20]); * collection.toArray(); * // [1, 2, -1, 20, 2, 3, 4, 5] + * ``` */ insertAfter( predicateFn: Predicate>, @@ -926,6 +1034,7 @@ export type ICollection = Iterable & { /** * The crossJoin method cross joins the collection's values among iterables, returning a Cartesian product with all possible permutations. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2]); @@ -937,7 +1046,9 @@ export type ICollection = Iterable & { * // [2, "a"], * // [2, "b"], * // ] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2]); @@ -953,6 +1064,7 @@ export type ICollection = Iterable & { * // [2, "b", "I"], * // [2, "b", "II"], * // ] + * ``` */ crossJoin( iterable: Iterable, @@ -962,26 +1074,32 @@ export type ICollection = Iterable & { * The zip method merges together the values of iterable with the values of the collection at their corresponding index. * The returned collection has size of the shortest collection. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["Chair", "Desk"]); * const zipped = collection.zip([100, 200]); * zipped.toArray(); * // [["Chair", 100], ["Desk", 200]] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["Chair", "Desk", "Couch"]); * const zipped = collection.zip([100, 200]); * zipped.toArray(); * // [["Chair", 100], ["Desk", 200]] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["Chair", "Desk"]); * const zipped = collection.zip([100, 200, 300]); * zipped.toArray(); * // [["Chair", 100], ["Desk", 200]] + * ``` */ zip( iterable: Iterable, @@ -990,12 +1108,15 @@ export type ICollection = Iterable & { /** * The sort method sorts the collection. You can provide a comparator function. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([-1, 2, 4, 3]); * collection.sort().toArray(); * // [-1, 2, 3, 4] + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([ @@ -1011,6 +1132,7 @@ export type ICollection = Iterable & { * // { name: "Hasan", age: 25 }, * // { name: "Anders", age: 30 }, * // ] + * ``` */ sort(comparator?: Comparator): ICollection; @@ -1018,11 +1140,13 @@ export type ICollection = Iterable & { * The reverse method will reverse the order of the collection. * The reversing of the collection will be applied in chunks that are the size of chunkSize . * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([-1, 2, 4, 3]); * collection.reverse().toArray(); * // [3, 4, 2, -1] + * ``` */ reverse(chunkSize?: number): ICollection; @@ -1035,24 +1159,28 @@ export type ICollection = Iterable & { * The first method returns the first item in the collection that passes predicateFn . * By default it will get the first item. If the collection is empty or no items passes predicateFn than null i returned. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.first(); * // 1 * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.first(item => item > 2); * // 3 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.first(item => item > 10); * // null - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * // 3 */ @@ -1064,30 +1192,37 @@ export type ICollection = Iterable & { * The firstOr method returns the first item in the collection that passes predicateFn * By default it will get the first item. If the collection is empty or no items passes predicateFn than defaultValue . * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.firstOr(-1); * // 1 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.firstOr(-1, item => item > 2); * // 3 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.firstOr(-1, item => item > 10); * // -1 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.firstOr(() => -1, item => item > 10); * // -1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ firstOr( @@ -1099,24 +1234,29 @@ export type ICollection = Iterable & { * The firstOrFail method returns the first item in the collection that passes predicateFn . * By default it will get the first item. If the collection is empty or no items passes predicateFn than error is thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.firstOrFail(); * // 1 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.firstOrFail(item => item > 2); * // 3 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.firstOrFail(item => item > 10); * // throws an error - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} */ @@ -1128,24 +1268,29 @@ export type ICollection = Iterable & { * The last method returns the last item in the collection that passes predicateFn . * By default it will get the last item. If the collection is empty or no items passes predicateFn than null i returned. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.last(); * // 4 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.last(item => item < 4); * // 3 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.last(item => item > 10); * // null - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * // 3 */ @@ -1157,30 +1302,37 @@ export type ICollection = Iterable & { * The lastOr method returns the last item in the collection that passes predicateFn . * By default it will get the last item. If the collection is empty or no items passes predicateFn than defaultValue . * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.lastOr(-1); * // 4 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.lastOr(-1, item => item < 4); * // 3 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.lastOr(-1, item => item > 10); * // -1 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.lastOr(() => -1, item => item > 10); * // -1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ lastOr( @@ -1192,24 +1344,29 @@ export type ICollection = Iterable & { * The lastOrFail method returns the last item in the collection that passes predicateFn . * By default it will get the last item. If the collection is empty or no items passes predicateFn than error is thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.lastOrFail(); * // 4 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.lastOrFail(item => item < 4); * // 3 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.lastOrFail(item => item > 10); * // throws an error - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} */ @@ -1221,18 +1378,21 @@ export type ICollection = Iterable & { * The before method returns the item that comes before the first item that matches predicateFn. * If the predicateFn does not match or matches the first item then null is returned. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.before(item => item === 2); * // 1 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.before(item => item === 1); * // null - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ before(predicateFn: Predicate>): TInput | null; @@ -1241,24 +1401,29 @@ export type ICollection = Iterable & { * The beforeOr method returns the item that comes before the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the first item then defaultValue is returned. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.beforeOr(-1, item => item === 2); * // 1 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.beforeOr(-1, item => item === 1); * // -1 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.beforeOr(() => -1, item => item === 1); * // -1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ beforeOr( @@ -1270,18 +1435,21 @@ export type ICollection = Iterable & { * The beforeOrFail method returns the item that comes before the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the first item then an error is thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.beforeOrFail(item => item === 2); * // 1 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.beforeOrFail(item => item === 1); * // error is thrown - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} */ @@ -1291,18 +1459,21 @@ export type ICollection = Iterable & { * The after method returns the item that comes after the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the last item then null is returned. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.after(item => item === 2); * // 3 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.after(item => item === 4); * // null - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ after(predicateFn: Predicate>): TInput | null; @@ -1311,24 +1482,29 @@ export type ICollection = Iterable & { * The afterOr method returns the item that comes after the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the last item then defaultValue is returned. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.afterOr(-1, item => item === 2); * // 3 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.afterOr(-1, item => item === 4); * // -1 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.afterOr(() => -1, item => item === 4); * // -1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ afterOr( @@ -1340,18 +1516,21 @@ export type ICollection = Iterable & { * The afterOrFail method returns the item that comes after the first item that matches predicateFn. * If the collection is empty or the predicateFn does not match or matches the last item then an error is thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.afterOrFail(item => item === 2); * // 3 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4]); * collection.afterOrFail(item => item === 4); * // error is thrown - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} */ @@ -1361,24 +1540,29 @@ export type ICollection = Iterable & { * The sole method returns the first item in the collection that passes predicateFn, but only if predicateFn matches exactly one item. * If no items matches or multiple items are found an error will be thrown. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5]); * collection.sole(item => item === 4); * // 4 + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 4, 5]); * collection.sole(item => item === 4); * // error is thrown + * ``` * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 5]); * collection.sole(item => item === 4); * // error is thrown - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} * @throws {ItemNotFoundCollectionError} {@link ItemNotFoundCollectionError} * @throws {MultipleItemsFoundCollectionError} {@link MultipleItemsFoundCollectionError} @@ -1390,44 +1574,44 @@ export type ICollection = Iterable & { /** * The nth method creates a new collection consisting of every n-th item. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "c", "d", "e", "f"]).nth(4); * collection.toArray(); * // ["a", "e"] + * ``` */ nth(step: number): ICollection; /** * The count method returns the total number of items in the collection that passes predicateFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection([1, 2, 3, 4, 5, 6]); * collection.count(value => value % 2 === 0); * // 3 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ count(predicateFn: Predicate>): number; /** * The size returns the size of the collection. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ size(): number; /** * The isEmpty returns true if the collection is empty. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ isEmpty(): boolean; /** * The isNotEmpty returns true if the collection is not empty. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ isNotEmpty(): boolean; @@ -1435,12 +1619,13 @@ export type ICollection = Iterable & { /** * The searchFirst return the index of the first item that matches predicateFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "b", "c"]); * collection.searchFirst(item => item === "b"); * // 1 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ searchFirst(predicateFn: Predicate>): number; @@ -1448,26 +1633,25 @@ export type ICollection = Iterable & { /** * The searchLast return the index of the last item that matches predicateFn. * @example + * ```ts * import { ListCollection } from "@daiso-tech/core";; * * const collection = new ListCollection(["a", "b", "b", "c"]); * collection.searchLast(item => item === "b"); * // 2 - * @throws {CollectionError} {@link CollectionError} + * ``` * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ searchLast(predicateFn: Predicate>): number; /** * The forEach method iterates through all items in the collection. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ forEach(callback: ForEach>): void; /** * The toArray method converts the collection to a new array. - * @throws {CollectionError} {@link CollectionError} * @throws {UnexpectedCollectionError} {@link UnexpectedCollectionError} */ toArray(): TInput[]; diff --git a/src/event-bus/contracts/_shared.ts b/src/event-bus/contracts/_shared.ts index ff3567b..9a881e0 100644 --- a/src/event-bus/contracts/_shared.ts +++ b/src/event-bus/contracts/_shared.ts @@ -21,6 +21,16 @@ export class EventBusError extends Error { } } +/** + * @group Errors + */ +export class UnexpectedEventBusError extends EventBusError { + constructor(message: string, cause?: unknown) { + super(message, { cause }); + this.name = UnexpectedEventBusError.name; + } +} + /** * @group Errors */ diff --git a/src/event-bus/contracts/event-bus-manager.contract.ts b/src/event-bus/contracts/event-bus-manager.contract.ts index 0f1d990..49874f4 100644 --- a/src/event-bus/contracts/event-bus-manager.contract.ts +++ b/src/event-bus/contracts/event-bus-manager.contract.ts @@ -2,29 +2,137 @@ * @module EventBus */ -import type { Validator } from "@/utilities/_module"; +import { type Validator } from "@/utilities/_module"; import type { INamespacedEventBus } from "@/event-bus/contracts/event-bus.contract"; // eslint-disable-next-line @typescript-eslint/no-unused-vars import type { IEventBusAdapter } from "@/event-bus/contracts/event-bus-adapter.contract"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars import type { IBaseEvent } from "@/event-bus/contracts/_shared"; /** + * The IEventBusFactory contract makes it easy to switch between different {@link IEventBusAdapter | event adapters} dynamically. * @group Contracts */ -export type EventBusManagerUseSettings< - TType, +export type IEventBusFactory< TAdapters extends string = string, + TEvents extends IBaseEvent = IBaseEvent, > = { - adapter?: TAdapters; - validator?: Validator; + /** + * @example + * ```ts + * import { EventBusManager, MemoryEventBusAdapter, RedisEventBusAdapter } from "@daiso-tech/core"; + * import Redis from "ioredis"; + * + * const eventBusManager = new EventBusManager({ + * adapters: { + * memory: new MemoryEventBusAdapter(), + * redis: new RedisEventBusAdapter({ + * dispatcherClient: new Redis(); + * listenerClient: new Redis(); + * }), + * }, + * defaultAdapter: "memory", + * rootNamespace: "@storage/" + * }); + * + * (async () => { + * // Will dispatch envent using the default adapter which is the memory addapter + * await eventBusManager + * .use() + * .dispatch({ type: "add", a: 1, b: 2 }); + * // Will dispatch envent using the redis addapter + * await eventBusManager + * .use("redis") + * .dispatch({ type: "add", a: 1, b: 2 }); + * })(); + * ``` + */ + use(adapter?: TAdapters): INamespacedEventBus; }; /** - * The IEventBusManager contract makes it easy to switch between different {@link IEventBusAdapter | event bus adapters} dynamically. + * The IEventBusManager contract makes it easy to configure and switch between different {@link IEventBusAdapter | event adapters} dynamically. * @group Contracts */ -export type IEventBusManager = { - use( - settings?: EventBusManagerUseSettings, - ): INamespacedEventBus; +export type IEventBusManager< + TAdapters extends string = string, + TEvents extends IBaseEvent = IBaseEvent, +> = IEventBusFactory & { + /** + * The withValidation method is used to set a validator, which validates the event during runtime. + * The type is inferred from the provided validator. + * @example + * ```ts + * import { EventBusManager, MemoryEventBusAdapter, RedisEventBusAdapter, zodValidator } from "@daiso-tech/core"; + * import Redis from "ioredis"; + * import { z } from "zod" + * + * const eventBusManager = new EventBusManager({ + * adapters: { + * memory: new MemoryEventBusAdapter(), + * redis: new RedisEventBusAdapter({ + * dispatcherClient: new Redis(); + * listenerClient: new Redis(); + * }), + * }, + * defaultAdapter: "memory", + * rootNamespace: "@storage/" + * }); + * + * (async () => { + * const addEventSchema = z.zod({ + * type: z.literal("add"), + * a: z.number(), + * b: z.number(), + * }); + * await eventBusManager + * .withValidation(zodValidator(addEventSchema)) + * .use() + * // You will se an typescript error and get runtime erorr + * .dispatch({ type: "add" }); + * })(); + * ``` + */ + withValidation( + validator: Validator, + ): IEventBusFactory; + + /** + * The withTypes method is used to set the event types of the {@link IEventBus}. + * @example + * ```ts + * import { EventBusManager, MemoryEventBusAdapter, RedisEventBusAdapter, zodValidator } from "@daiso-tech/core"; + * import Redis from "ioredis"; + * import { z } from "zod" + * + * const eventBusManager = new EventBusManager({ + * adapters: { + * memory: new MemoryEventBusAdapter(), + * redis: new RedisEventBusAdapter({ + * dispatcherClient: new Redis(); + * listenerClient: new Redis(); + * }), + * }, + * defaultAdapter: "memory", + * rootNamespace: "@storage/" + * }); + * + * (async () => { + * type AddEvent = { + * type: "add"; + * a: number; + * b: number; + * }; + * await eventBusManager + * .withTypes() + * .use() + * // You will se an typescript error + * .dispatch({ type: "add" }); + * })(); + * ``` + */ + withType(): IEventBusFactory< + TAdapters, + TOutput + >; }; diff --git a/src/event-bus/contracts/event-bus.contract.ts b/src/event-bus/contracts/event-bus.contract.ts index c66551b..95a7cfd 100644 --- a/src/event-bus/contracts/event-bus.contract.ts +++ b/src/event-bus/contracts/event-bus.contract.ts @@ -26,16 +26,12 @@ export type SelectEvent< export type Unsubscribe = () => PromiseLike; /** * The IListenable contract defines a way listening to events independent of underlying technology - * @throws {EventBusError} {@link EventBusError} - * @throws {AddListenerEventBusError} {@link AddListenerEventBusError} - * @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError} * @group Contracts */ export type IListenable = { /** * The addListener method is used for adding {@link Listener | listener} for certain event. * A listener can only be added once for a specific event. Adding the same listener multiple times will have no effect and nothing will occur. - * @throws {EventBusError} {@link EventBusError} * @throws {AddListenerEventBusError} {@link AddListenerEventBusError} */ addListener( @@ -46,7 +42,6 @@ export type IListenable = { /** * The addListenerMany method is used for adding multiple {@link Listener | listeners} for certain events. * A listener can only be added once for a specific event. Adding the same listener multiple times will have no effect and nothing will occur. - * @throws {EventBusError} {@link EventBusError} * @throws {AddListenerEventBusError} {@link AddListenerEventBusError} */ addListenerMany( @@ -57,7 +52,6 @@ export type IListenable = { /** * The removeListener method is used for removing {@link Listener | listener} for certain event. * Removing unadded listener will have no effect and nothing will occur. - * @throws {EventBusError} {@link EventBusError} * @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError} */ removeListener( @@ -68,7 +62,6 @@ export type IListenable = { /** * The removeListener method is used for removing multiple {@link Listener | listeners} for certain event. * Removing unadded listener will have no effect and nothing will occur. - * @throws {EventBusError} {@link EventBusError} * @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError} */ removeListenerMany( @@ -79,14 +72,16 @@ export type IListenable = { /** * The subscribe method is used for adding {@link Listener | listener} for certain event. * A listener can only be added once for a specific event. Adding the same listener multiple times will have no effect and nothing will occur. - * @throws {EventBusError} {@link EventBusError} - * @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError} */ subscribe( event: TEventType, listener: Listener>, ): PromiseLike; + /** + * The subscribeMany method is used for adding {@link Listener | listener} for multiple events. + * A listener can only be added once for a specific event. Adding the same listener multiple times will have no effect and nothing will occur. + */ subscribeMany( events: TEventType[], listener: Listener>, @@ -96,29 +91,40 @@ export type IListenable = { /** * The IEventBus contract defines a way for dispatching and listening to events independent of underlying technology. * It commes with more convient methods compared to IEventBusAdapter. - * @throws {EventBusError} {@link EventBusError} - * @throws {DispatchEventBusError} {@link DispatchEventBusError} - * @throws {AddListenerEventBusError} {@link AddListenerEventBusError} - * @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError} * @group Contracts */ export type IEventBus = IListenable & { /** * The dispatch method is used for dispatching one or multiple events. - * @throws {EventBusError} {@link EventBusError} + * @throws {DispatchEventBusError} {@link DispatchEventBusError} */ dispatch(events: OneOrMore): PromiseLike; + + /** + * The getNamespace method return the complete namespace. + * @example + * ```ts + * const eventBus = new EventBus(new MemoryEventBusAdapter(), { + * rootNamespace: "@root/" + * }); + * + * // Will be "@root/" + * console.log(eventBus.getNamespace()) + * + * const eventBusA = eventBus.withNamespace("a/"); + * + * // Will be "@root/a/" + * console.log(eventBusA.getNamespace()) + * ``` + */ + getNamespace(): string; }; /** * The INamespacedEventBus contract defines a way for dispatching and listening to events independent of underlying technology. * It commes with one extra method which is useful for multitennat applications compared to IEventBus. - * @throws {EventBusError} {@link EventBusError} - * @throws {DispatchEventBusError} {@link DispatchEventBusError} - * @throws {AddListenerEventBusError} {@link AddListenerEventBusError} - * @throws {RemoveListenerEventBusError} {@link RemoveListenerEventBusError} * @group Contracts */ export type INamespacedEventBus = @@ -126,11 +132,33 @@ export type INamespacedEventBus = /** * The withNamespace method returns new instance of {@link IEventBus} where all the events names will be prefixed with a given namespace. * This useful for multitennat applications. + * @example + * ```ts + * import { EventBus, MemoryEventBusAdapter } from "@daiso-tech/core"; + * + * (async () => { + * const eventBus = new EventBus(new MemoryEventBusAdapter()); + * + * const eventBusA = eventBus.withNamespace("a"); + * await eventBusA.subscribe("add", (event) => { + * // This will be logged + * console.log("eventBusA:", event); + * }); + * + * const eventBusB = eventBus.withNamespace("b"); + * await eventBusB.subscribe("add", (event) => { + * // This will never be logged + * console.log("eventBusB:", event); + * }); + * + * await eventBusA.dispatch({ + * type: "add", + * a: 1, + * b: 2 + * }) + * })(); + * + * ``` */ withNamespace(namespace: string): IEventBus; - - /** - * The getNamespace method return the complete namespace. - */ - getNamespace(): string; }; diff --git a/src/event-bus/implementations/_shared/test-utilities/event-bus-adapter.test-suite.ts b/src/event-bus/implementations/_shared/test-utilities/event-bus-adapter.test-suite.ts index 1091ad2..ac8b3b5 100644 --- a/src/event-bus/implementations/_shared/test-utilities/event-bus-adapter.test-suite.ts +++ b/src/event-bus/implementations/_shared/test-utilities/event-bus-adapter.test-suite.ts @@ -26,10 +26,29 @@ export type EventBusTestSuiteSettings = { beforeEach: typeof beforeEach; createAdapter: () => Promisable; }; + /** + * The eventBusAdapterTestSuite function simplifies the process of testing your custom implementation of {@link IEventBusAdapter} with vitest. + * @example + * ```ts + * import { eventBusAdapterTestSuite, MemoryEventBusAdapter } from "@daiso-tech/core"; + * import { expext, test, describe, beforeEach } from "vitest"; + * + * describe("class: MemoryEventBusAdapter", () => { + * eventBusAdapterTestSuite({ + * createAdapter: () => new MemoryEventBusAdapter(), + * test, + * beforeEach, + * expect, + * describe, + * }); + * }); + * ``` * @group Utilities */ -export function eventBusTestSuite(settings: EventBusTestSuiteSettings): void { +export function eventBusAdapterTestSuite( + settings: EventBusTestSuiteSettings, +): void { const { expect, test, createAdapter, beforeEach } = settings; // eslint-disable-next-line @typescript-eslint/no-explicit-any let eventBusAdapter: IEventBusAdapter; diff --git a/src/event-bus/implementations/event-bus/event-bus-manager.ts b/src/event-bus/implementations/event-bus/event-bus-manager.ts index 8525828..35a20b6 100644 --- a/src/event-bus/implementations/event-bus/event-bus-manager.ts +++ b/src/event-bus/implementations/event-bus/event-bus-manager.ts @@ -2,17 +2,15 @@ * @module EventBus */ +import { type Validator } from "@/utilities/_module"; import type { INamespacedEventBus, IEventBusAdapter, IEventBusManager, - EventBusManagerUseSettings, IBaseEvent, + IEventBusFactory, } from "@/event-bus/contracts/_module"; -import { - EventBus, - type EventBusSettings, -} from "@/event-bus/implementations/event-bus/event-bus"; +import { EventBus } from "@/event-bus/implementations/event-bus/event-bus"; /** * @group Derivables @@ -20,35 +18,50 @@ import { export type EventBusManagerSettings = { adapters: Record; defaultAdapter: NoInfer; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - adapterSettings?: Omit, "validator">; + rootNamespace: string; }; /** * @group Derivables */ -export class EventBusManager - implements IEventBusManager +export class EventBusManager< + TAdapters extends string = string, + TEvent extends IBaseEvent = IBaseEvent, +> implements IEventBusManager { private readonly adapters: Record; private readonly defaultAdapter: TAdapters; + private readonly rootNamespace: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any - private readonly adapterSettings: Omit, "validator">; + private validator: Validator = (value) => value; constructor(settings: EventBusManagerSettings) { - const { adapters, defaultAdapter, adapterSettings = {} } = settings; + const { adapters, defaultAdapter, rootNamespace } = settings; this.adapters = adapters; this.defaultAdapter = defaultAdapter; - this.adapterSettings = adapterSettings; + this.rootNamespace = rootNamespace; } - use( - adapterSettings: EventBusManagerUseSettings, - ): INamespacedEventBus { - const { adapter = this.defaultAdapter, validator } = adapterSettings; + use(adapter: TAdapters = this.defaultAdapter): INamespacedEventBus { return new EventBus(this.adapters[adapter], { - ...this.adapterSettings, - validator, + rootNamespace: this.rootNamespace, + validator: this.validator, }); } + + withValidation( + validator: Validator, + ): IEventBusFactory { + this.validator = validator; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any + return this as any; + } + + withType(): IEventBusFactory< + TAdapters, + TOutput + > { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any + return this as any; + } } diff --git a/src/event-bus/implementations/memory-event-bus-adapter/memory-event-bus-adapter.test.ts b/src/event-bus/implementations/memory-event-bus-adapter/memory-event-bus-adapter.test.ts index c43553d..e388e35 100644 --- a/src/event-bus/implementations/memory-event-bus-adapter/memory-event-bus-adapter.test.ts +++ b/src/event-bus/implementations/memory-event-bus-adapter/memory-event-bus-adapter.test.ts @@ -1,9 +1,9 @@ import { describe, test, beforeEach, expect } from "vitest"; -import { eventBusTestSuite } from "@/event-bus/implementations/_shared/test-utilities/event-bus-adapter.test-suite"; +import { eventBusAdapterTestSuite } from "@/event-bus/implementations/_shared/test-utilities/event-bus-adapter.test-suite"; import { MemoryEventBusAdapter } from "@/event-bus/implementations/memory-event-bus-adapter/memory-event-bus-adapter"; describe("class: MemoryEventBusAdapter", () => { - eventBusTestSuite({ + eventBusAdapterTestSuite({ createAdapter: () => new MemoryEventBusAdapter(), test, beforeEach, diff --git a/src/event-bus/implementations/redis-event-bus-adapter/redis-event-bus-adapter.test.ts b/src/event-bus/implementations/redis-event-bus-adapter/redis-event-bus-adapter.test.ts index 02f741c..9b4b6ed 100644 --- a/src/event-bus/implementations/redis-event-bus-adapter/redis-event-bus-adapter.test.ts +++ b/src/event-bus/implementations/redis-event-bus-adapter/redis-event-bus-adapter.test.ts @@ -1,5 +1,5 @@ import { describe, test, beforeEach, expect, afterEach } from "vitest"; -import { eventBusTestSuite } from "@/event-bus/implementations/_shared/test-utilities/event-bus-adapter.test-suite"; +import { eventBusAdapterTestSuite } from "@/event-bus/implementations/_shared/test-utilities/event-bus-adapter.test-suite"; import { RedisEventBusAdapter } from "@/event-bus/implementations/redis-event-bus-adapter/redis-event-bus-adapter"; import { TimeSpan } from "@/utilities/_module"; import type { StartedRedisContainer } from "@testcontainers/redis"; @@ -21,7 +21,7 @@ describe("class: RedisEventBusAdapter", () => { await listenerClient.quit(); await startedContainer.stop(); }, timeout.toMilliseconds()); - eventBusTestSuite({ + eventBusAdapterTestSuite({ createAdapter: () => new RedisEventBusAdapter({ dispatcherClient, diff --git a/src/serializer/contracts/serializer.contract.ts b/src/serializer/contracts/serializer.contract.ts index 9044e40..a806859 100644 --- a/src/serializer/contracts/serializer.contract.ts +++ b/src/serializer/contracts/serializer.contract.ts @@ -34,9 +34,6 @@ export class DeserializationError extends SerializerError { /** * @group Contracts - * @throws {SerializerError} {@link SerializerError} - * @throws {SerializationError} {@link SerializationError} - * @throws {DeserializationError} {@link DeserializationError} */ export type ISerializer = { /** diff --git a/src/storage/contracts/storage-adapter.contract.ts b/src/storage/contracts/storage-adapter.contract.ts index 32bf1bf..5f075fc 100644 --- a/src/storage/contracts/storage-adapter.contract.ts +++ b/src/storage/contracts/storage-adapter.contract.ts @@ -3,27 +3,18 @@ */ import { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - type StorageError, // eslint-disable-next-line @typescript-eslint/no-unused-vars type TypeStorageError, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - type UnexpectedStorageError, } from "@/storage/contracts/_shared"; /** * The IStorageAdapter contract defines a way for storing data as key-value pairs independent of storage. * This interface is not meant to be used directly, instead you should use IStorage - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} - * @throws {TypeStorageError} {@link TypeStorageError} * @group Contracts */ -export type IStorageAdapter = { +export type IStorageAdapter = { /** * The getMany returns the value for the keys that are found otherwise null will be returned. - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ getMany( keys: TKeys[], @@ -31,8 +22,6 @@ export type IStorageAdapter = { /** * The addMany method adds the keys that doesn't exists. Returns true for the keys that doesn't exists otherwise false will be returned. - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ addMany( values: Record, @@ -40,8 +29,6 @@ export type IStorageAdapter = { /** * The updateMany method updates the given keys. Returns true for the keys that exists otherwise false will be returned. - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ updateMany( values: Record, @@ -49,8 +36,6 @@ export type IStorageAdapter = { /** * The putMany method replaces the keys that are found. Adds keys that are not found. Returns true for all the keys that are found otherwise false is returned. - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ putMany( values: Record, @@ -58,8 +43,6 @@ export type IStorageAdapter = { /** * The removesMany method removes the keys that are found. Returns true for the keys that are found otherwise false is returned. - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ removeMany( keys: TKeys[], @@ -69,16 +52,12 @@ export type IStorageAdapter = { * The increment method will increment the given key if found otherwise nonthing will occur. * Returns true if key exists otherwise false will be returned. * An error will thrown if the key is not a number. - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} * @throws {TypeStorageError} {@link TypeStorageError} */ increment(key: string, value: number): PromiseLike; /** * The clear method removes all keys that starts prefix. - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ clear(prefix: string): PromiseLike; }; diff --git a/src/storage/contracts/storage-events.contract.ts b/src/storage/contracts/storage-events.contract.ts index bfdac17..5e7e46a 100644 --- a/src/storage/contracts/storage-events.contract.ts +++ b/src/storage/contracts/storage-events.contract.ts @@ -95,6 +95,7 @@ export type KeysRemovedStorageEvent = StorageEvent & { }; /** + * This event will be triggered when storage is cleared. * @group Events */ export type KeysClearedStorageEvent = StorageEvent & { diff --git a/src/storage/contracts/storage-manager.contract.ts b/src/storage/contracts/storage-manager.contract.ts index 1ff7605..b43a3ea 100644 --- a/src/storage/contracts/storage-manager.contract.ts +++ b/src/storage/contracts/storage-manager.contract.ts @@ -8,22 +8,110 @@ import type { INamespacedStorage } from "@/storage/contracts/storage.contract"; import type { IStorageAdapter } from "@/storage/contracts/storage-adapter.contract"; /** + * The IStorageFactory contract makes it easy to switch between different {@link IStorageAdapter | storage adapters} dynamically. * @group Contracts */ -export type StorageManagerUseSettings< - TType, +export type IStorageFactory< TAdapters extends string = string, + TType = unknown, > = { - adapter?: TAdapters; - validator?: Validator; + /** + * @example + * ```ts + * import { StorageManager, MemoryStorageAdapter, RedisStorageAdapter } from "@daiso-tech/core"; + * import Redis from "ioredis"; + * + * const storageManager = new StorageManager({ + * adapters: { + * memory: new MemoryStorageAdapter(), + * redis: new RedisStorageAdapter(new Redis()), + * }, + * defaultAdapter: "memory", + * rootNamespace: "@storage/" + * }); + * + * (async () => { + * // Will add key using the default adapter which is the memory addapter + * await storageManager + * .use() + * .add("a", 1); + * // Will add key using the redis addapter + * await storageManager + * .use("redis") + * .add("a", 1); + * })(); + * ``` + */ + use(adapter?: TAdapters): INamespacedStorage; }; /** - * The IStorageManager contract makes it easy to switch between different {@link IStorageAdapter | storage adapters} dynamically. + * The IStorageManager contract makes it easy to configure and switch between different {@link IStorageAdapter | storage adapters} dynamically. * @group Contracts */ -export type IStorageManager = { - use( - settings?: StorageManagerUseSettings, - ): INamespacedStorage; +export type IStorageManager< + TAdapters extends string = string, + TType = unknown, +> = IStorageFactory & { + /** + * The withValidation method is used to set a validator, which validates the storage values during runtime. + * The type is inferred from the provided validator. + * @example + * ```ts + * import { StorageManager, MemoryStorageAdapter, RedisStorageAdapter, zodValidator } from "@daiso-tech/core"; + * import Redis from "ioredis"; + * import { z } from "zod" + * + * const storageManager = new StorageManager({ + * adapters: { + * memory: new MemoryStorageAdapter(), + * redis: new RedisStorageAdapter(new Redis()), + * }, + * defaultAdapter: "memory", + * rootNamespace: "@storage/" + * }); + * + * (async () => { + * await storageManager + * .withValidation(zodValidator(z.string())) + * .use() + * // You will se an typescript error and get runtime erorr + * .add("a", 1); + * })(); + * ``` + */ + withValidation( + validator: Validator, + ): IStorageFactory; + + /** + * The withTypes method is used to set the value types of the storage. + * @example + * ```ts + * import { StorageManager, MemoryStorageAdapter, RedisStorageAdapter, zodValidator } from "@daiso-tech/core"; + * import Redis from "ioredis"; + * import { z } from "zod" + * + * const storageManager = new StorageManager({ + * adapters: { + * memory: new MemoryStorageAdapter(), + * redis: new RedisStorageAdapter(new Redis()), + * }, + * defaultAdapter: "memory", + * rootNamespace: "@storage/" + * }); + * + * (async () => { + * await storageManager + * .withTypes() + * .use() + * // You will se an typescript error + * .add("a", 1) + * })(); + * ``` + */ + withType(): IStorageFactory< + TAdapters, + TOutput + >; }; diff --git a/src/storage/contracts/storage.contract.ts b/src/storage/contracts/storage.contract.ts index 27e56cb..511bd09 100644 --- a/src/storage/contracts/storage.contract.ts +++ b/src/storage/contracts/storage.contract.ts @@ -22,25 +22,27 @@ import { export type StorageValue = Exclude; +/** + * The IStorageListenable contract defines a way for listening {@link IStorage} crud operations. + */ +export type IStorageListenable = IListenable< + AllStorageEvents +>; + /** * The IStorage contract defines a way for storing data as key-value pairs independent of storage. * It commes with more convient methods compared to IStorageAdapter. - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} - * @throws {TypeStorageError} {@link TypeStorageError} * @group Contracts */ -export type IStorage = IListenable> & { +export type IStorage = IStorageListenable & { /** * The exists method returns true when key is found otherwise false will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ exists(key: string): PromiseLike; /** * The existsMany method returns true for the keys that are found otherwise false will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ existsMany( @@ -49,14 +51,12 @@ export type IStorage = IListenable> & { /** * The missing method returns true when key is not found otherwise false will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ missing(key: string): PromiseLike; /** * The missingMany method returns true for the keys that are not found otherwise false will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ missingMany( @@ -65,14 +65,12 @@ export type IStorage = IListenable> & { /** * The get method returns the value when key is found otherwise null will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ get(key: string): PromiseLike; /** * The getMany returns the value for the keys that are found otherwise null will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ getMany( @@ -81,14 +79,12 @@ export type IStorage = IListenable> & { /** * The getOr method returns the value when key is found otherwise defaultValue will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ getOr(key: string, defaultValue: AsyncLazyable): PromiseLike; /** * The getOrMany method returns the value for the keys that are found otherwise defaultValue will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ getOrMany( @@ -97,7 +93,6 @@ export type IStorage = IListenable> & { /** * The getOrFail method returns the value when key is found otherwise an error will be thrown. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} * @throws {KeyNotFoundStorageError} {@link KeyNotFoundStorageError} */ @@ -105,14 +100,12 @@ export type IStorage = IListenable> & { /** * The add method adds a key with given value when key doesn't exists. Returns true when key doesn't exists otherwise false will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ add(key: string, value: StorageValue): PromiseLike; /** * The addMany method adds the keys that doesn't exists. Returns true for the keys that doesn't exists otherwise false will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ addMany( @@ -121,14 +114,12 @@ export type IStorage = IListenable> & { /** * The update method updates the given key with given value. Returns true when key otherwise false will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ update(key: string, value: TType): PromiseLike; /** * The updateMany method updates the given keys. Returns true for the keys that exists otherwise false will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ updateMany( @@ -137,14 +128,12 @@ export type IStorage = IListenable> & { /** * The put method replaces the key with given value if found. If the key is not found it will just be added. True is returned if the key is found otherwise false will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ put(key: string, value: StorageValue): PromiseLike; /** * The putMany method replaces the keys that are found. Adds keys that are not found. Returns true for all the keys that are found otherwise false is returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ putMany( @@ -153,14 +142,12 @@ export type IStorage = IListenable> & { /** * The remove method removes the given key when found. Returns true if the key is found otherwise false is returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ remove(key: string): PromiseLike; /** * The removesMany method removes the keys that are found. Returns true for the keys that are found otherwise false is returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ removeMany( @@ -169,14 +156,12 @@ export type IStorage = IListenable> & { /** * The getAndRemove method removes the given key and returns it when found otherwise null will be returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ getAndRemove(key: string): PromiseLike; /** * The getOrAdd method will retrieve the given key if found otherwise valueToAdd will be added and returned. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ getOrAdd( @@ -188,7 +173,6 @@ export type IStorage = IListenable> & { * The increment method will increment the given key if found otherwise nonthing will occur. * Returns true if key exists otherwise false will be returned. * An error will thrown if the key is not a number. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} * @throws {TypeStorageError} {@link TypeStorageError} */ @@ -201,7 +185,6 @@ export type IStorage = IListenable> & { * The decrement method will decrement the given key if found otherwise nonthing will occur. * Returns true if key exists otherwise false will be returned. * An error will thrown if the key is not a number. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} * @throws {TypeStorageError} {@link TypeStorageError} * An error will thrown if the key is not a number. @@ -213,29 +196,57 @@ export type IStorage = IListenable> & { /** * The clear method removes all the keys in the storage. - * @throws {StorageError} {@link StorageError} * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} */ clear(): PromiseLike; + + /** + * The getNamespace method return the complete namespace. + * @example + * ```ts + * const storage = new Storage(new MemoryStorageAdapter(), { + * rootNamespace: "@root/" + * }); + * + * // Will be "@root/" + * console.log(storage.getNamespace()) + * + * const storageA = storage.withNamespace("a/"); + * + * // Will be "@root/a/" + * console.log(storageA.getNamespace()) + * ``` + */ + getNamespace(): string; }; /** * The INamespacedStorage contract defines a way for storing data as key-value pairs independent of storage. * It commes with one extra method which is useful for multitennat applications compared to IStorage. - * @throws {StorageError} {@link StorageError} - * @throws {UnexpectedStorageError} {@link UnexpectedStorageError} - * @throws {TypeStorageError} {@link TypeStorageError} * @group Contracts */ export type INamespacedStorage = IStorage & { /** * The withNamespace method returns new instance of {@link IStorage} where all the keys will be prefixed with a given namespace. * This useful for multitennat applications. + * @example + * ```ts + * import { Storage, MemoryStorageAdapter } from "@daiso-tech/core"; + * + * (async () => { + * const storage = new Storage(new MemoryStorageAdapter()); + * + * const storageA = storage.withNamespace("a"); + * await storageA.add("a", 1); + * + * const storageB = storage.withNamespace("b"); + * await storageB.add("b", 2); + * + * // Will print { a: 1, b: null } + * console.log(await storageA.getMany(["a", "b"])); + * })(); + * + * ``` */ withNamespace(namespace: string): IStorage; - - /** - * The getNamespace method return the complete namespace. - */ - getNamespace(): string; }; diff --git a/src/storage/implementations/_shared/test-utilities/storage-adapter.test-suite.ts b/src/storage/implementations/_shared/test-utilities/storage-adapter.test-suite.ts index a190d4d..74242b5 100644 --- a/src/storage/implementations/_shared/test-utilities/storage-adapter.test-suite.ts +++ b/src/storage/implementations/_shared/test-utilities/storage-adapter.test-suite.ts @@ -22,12 +22,28 @@ export type StorageAdapterTestSuiteSettings = { test: TestAPI; describe: SuiteAPI; beforeEach: typeof beforeEach; - createAdapter: () => Promisable>; + createAdapter: () => Promisable; }; /** + * The storageAdapterTestSuite function simplifies the process of testing your custom implementation of {@link IStorageAdapter}. + * @example + * ```ts + * import { storageAdapterTestSuite, MemoryStorageAdapter } from "@daiso-tech/core"; + * import { expext, test, describe, beforeEach } from "vitest"; + * + * describe("class: MemoryStorageAdapter", () => { + * storageAdapterTestSuite({ + * createAdapter: () => new MemoryStorageAdapter(), + * test, + * beforeEach, + * expect, + * describe, + * }); + * }); + * ``` * @group Utilities */ -export function storageTestSuite( +export function storageAdapterTestSuite( settings: StorageAdapterTestSuiteSettings, ): void { const { expect, test, createAdapter, describe, beforeEach } = settings; diff --git a/src/storage/implementations/memory-storage-adapter/memory-storage-adapter.test.ts b/src/storage/implementations/memory-storage-adapter/memory-storage-adapter.test.ts index 386c422..d5717d8 100644 --- a/src/storage/implementations/memory-storage-adapter/memory-storage-adapter.test.ts +++ b/src/storage/implementations/memory-storage-adapter/memory-storage-adapter.test.ts @@ -1,9 +1,9 @@ import { beforeEach, describe, expect, test } from "vitest"; -import { storageTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; +import { storageAdapterTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; import { MemoryStorageAdapter } from "@/storage/implementations/memory-storage-adapter/_module"; describe("class: MemoryStorageAdapter", () => { - storageTestSuite({ + storageAdapterTestSuite({ createAdapter: () => new MemoryStorageAdapter(new Map()), test, diff --git a/src/storage/implementations/mongodb-storage-adapter/mongodb-storage-adapter.test.ts b/src/storage/implementations/mongodb-storage-adapter/mongodb-storage-adapter.test.ts index c2f4354..d9d4988 100644 --- a/src/storage/implementations/mongodb-storage-adapter/mongodb-storage-adapter.test.ts +++ b/src/storage/implementations/mongodb-storage-adapter/mongodb-storage-adapter.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest"; -import { storageTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; +import { storageAdapterTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; import { MongodbStorageAdapter } from "@/storage/implementations/mongodb-storage-adapter/_module"; import { MongoDBContainer, @@ -22,7 +22,7 @@ describe("class: MongodbStorageAdapter", () => { await client.close(); await startedContainer.stop(); }, timeout.toMilliseconds()); - storageTestSuite({ + storageAdapterTestSuite({ createAdapter: async () => { const storageAdapter = new MongodbStorageAdapter( client.db("database").collection("storage"), diff --git a/src/storage/implementations/redis-storage-adapter/redis-storage-adapter.test.ts b/src/storage/implementations/redis-storage-adapter/redis-storage-adapter.test.ts index f4a0bb3..0c97a07 100644 --- a/src/storage/implementations/redis-storage-adapter/redis-storage-adapter.test.ts +++ b/src/storage/implementations/redis-storage-adapter/redis-storage-adapter.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest"; -import { storageTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; +import { storageAdapterTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; import { RedisStorageAdapter } from "@/storage/implementations/redis-storage-adapter/_module"; import { RedisContainer, @@ -20,7 +20,7 @@ describe("class: RedisStorageAdapter", () => { await client.quit(); await startedContainer.stop(); }, timeout.toMilliseconds()); - storageTestSuite({ + storageAdapterTestSuite({ createAdapter: () => new RedisStorageAdapter(client), test, beforeEach, diff --git a/src/storage/implementations/sqlite/libsql-storage-adapter/libsql-storage-adapter.test.ts b/src/storage/implementations/sqlite/libsql-storage-adapter/libsql-storage-adapter.test.ts index 696aef3..65e04ef 100644 --- a/src/storage/implementations/sqlite/libsql-storage-adapter/libsql-storage-adapter.test.ts +++ b/src/storage/implementations/sqlite/libsql-storage-adapter/libsql-storage-adapter.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest"; -import { storageTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; +import { storageAdapterTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; import { LibsqlStorageAdapter } from "@/storage/implementations/sqlite/libsql-storage-adapter/_module"; import { type Client, createClient } from "@libsql/client"; @@ -18,7 +18,7 @@ describe("class: LibsqlStorageAdapter", () => { afterEach(() => { client.close(); }); - storageTestSuite({ + storageAdapterTestSuite({ createAdapter: async () => { const storageAdapter = new LibsqlStorageAdapter(client, { tableName: "custom_table", diff --git a/src/storage/implementations/sqlite/sqlite-storage-adapter/sqlite-storage-adapter.test.ts b/src/storage/implementations/sqlite/sqlite-storage-adapter/sqlite-storage-adapter.test.ts index 31ef29c..6c3b46a 100644 --- a/src/storage/implementations/sqlite/sqlite-storage-adapter/sqlite-storage-adapter.test.ts +++ b/src/storage/implementations/sqlite/sqlite-storage-adapter/sqlite-storage-adapter.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest"; -import { storageTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; +import { storageAdapterTestSuite } from "@/storage/implementations/_shared/test-utilities/_module"; import { SqliteStorageAdapter } from "@/storage/implementations/sqlite/sqlite-storage-adapter/_module"; import Sqlite, { type Database } from "better-sqlite3"; @@ -11,7 +11,7 @@ describe("class: SqliteStorageAdapter", () => { afterEach(() => { database.close(); }); - storageTestSuite({ + storageAdapterTestSuite({ createAdapter: async () => { const storageAdapter = new SqliteStorageAdapter(database, { tableName: "custom_table", diff --git a/src/storage/implementations/storage/storage-manager.ts b/src/storage/implementations/storage/storage-manager.ts index 2e4228f..2934665 100644 --- a/src/storage/implementations/storage/storage-manager.ts +++ b/src/storage/implementations/storage/storage-manager.ts @@ -2,16 +2,15 @@ * @module Storage */ +import type { INamespacedEventBus } from "@/event-bus/contracts/_module"; import type { + IStorageFactory, INamespacedStorage, IStorageAdapter, IStorageManager, - StorageManagerUseSettings, } from "@/storage/contracts/_module"; -import { - Storage, - type StorageSettings, -} from "@/storage/implementations/storage/storage"; +import { Storage } from "@/storage/implementations/storage/storage"; +import type { Validator } from "@/utilities/_module"; /** * @group Derivables @@ -20,35 +19,54 @@ export type StorageManagerSettings = { // eslint-disable-next-line @typescript-eslint/no-explicit-any adapters: Record>; defaultAdapter: NoInfer; + rootNamespace: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any - adapterSettings?: Omit, "validator">; + eventBus?: INamespacedEventBus; }; /** * @group Derivables */ -export class StorageManager - implements IStorageManager +export class StorageManager + implements IStorageManager { - private readonly adapters: Record>; + private readonly adapters: Record; private readonly defaultAdapter: TAdapters; + private readonly rootNamespace: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any - private readonly adapterSettings: Omit, "validator">; + private readonly eventBus?: INamespacedEventBus; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private validator: Validator = (value) => value; constructor(settings: StorageManagerSettings) { - const { adapters, defaultAdapter, adapterSettings = {} } = settings; + const { adapters, defaultAdapter, rootNamespace, eventBus } = settings; this.adapters = adapters; this.defaultAdapter = defaultAdapter; - this.adapterSettings = adapterSettings; + this.rootNamespace = rootNamespace; + this.eventBus = eventBus; } - use( - adapterSettings: StorageManagerUseSettings = {}, - ): INamespacedStorage { - const { adapter = this.defaultAdapter, validator } = adapterSettings; + use(adapter: TAdapters = this.defaultAdapter): INamespacedStorage { return new Storage(this.adapters[adapter], { - ...this.adapterSettings, - validator, + rootNamespace: this.rootNamespace, + validator: this.validator, + eventBus: this.eventBus, }); } + + withValidation( + validator: Validator, + ): IStorageFactory { + this.validator = validator; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any + return this as any; + } + + withType(): IStorageFactory< + TAdapters, + TOutput + > { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any + return this as any; + } } diff --git a/src/storage/implementations/storage/with-event-storage-adapter.ts b/src/storage/implementations/storage/with-event-storage-adapter.ts index 2a44ba2..b368042 100644 --- a/src/storage/implementations/storage/with-event-storage-adapter.ts +++ b/src/storage/implementations/storage/with-event-storage-adapter.ts @@ -15,7 +15,10 @@ import { type KeysFoundStorageEvent, type KeysNotFoundStorageEvent, } from "@/storage/contracts/_module"; -import type { IEventBus } from "@/event-bus/contracts/_module"; +import { + UnexpectedEventBusError, + type IEventBus, +} from "@/event-bus/contracts/_module"; import type { WithNamespaceStorageAdapter } from "@/storage/implementations/storage/with-namespace-storage-adapter"; import { isArrayEmpty, isObjectEmpty } from "@/_shared/utilities"; @@ -47,7 +50,9 @@ export class WithEventStorageAdapter for (const key in result) { const value = result[key]; if (value === undefined) { - continue; + throw new UnexpectedEventBusError( + `Destructed field "key" is undefined`, + ); } if (value === null) { keysNotFoundEvent.keys.push(key); @@ -87,7 +92,9 @@ export class WithEventStorageAdapter for (const key in result) { const value = values[key]; if (value === undefined) { - continue; + throw new UnexpectedEventBusError( + `Destructed field "key" is undefined`, + ); } const hasAdded = result[key]; if (hasAdded) { @@ -124,9 +131,11 @@ export class WithEventStorageAdapter for (const key in result) { const value = values[key]; if (value === undefined) { - continue; + throw new UnexpectedEventBusError( + `Destructed field "key" is undefined`, + ); } - const keysExists = result[key]; + const { [key]: keysExists } = result; if (keysExists) { keysUpdatedEvent.values[key] = value; } else { @@ -171,7 +180,9 @@ export class WithEventStorageAdapter for (const key in result) { const value = values[key]; if (value === undefined) { - continue; + throw new UnexpectedEventBusError( + `Destructed field "key" is undefined`, + ); } const keyExists = result[key]; if (keyExists) { @@ -214,9 +225,11 @@ export class WithEventStorageAdapter keys: [], }; for (const key in result) { - const keysExists = result[key]; + const { [key]: keysExists } = result; if (keysExists === undefined) { - continue; + throw new UnexpectedEventBusError( + `Destructed field "key" is undefined`, + ); } if (keysExists) { keysRemovedEvent.keys.push(key);