all-you-need 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # all-you-need
2
2
 
3
+ **English** | [Русский](./README.ru.md)
4
+
3
5
  [![npm version](https://img.shields.io/npm/v/all-you-need)](https://www.npmjs.com/package/all-you-need)
4
6
  [![bundle size](https://img.shields.io/bundlephobia/minzip/all-you-need)](https://bundlephobia.com/package/all-you-need)
5
7
  [![license](https://img.shields.io/npm/l/all-you-need)](./LICENSE)
@@ -30,83 +32,108 @@ import type { DeepPartial, Nullable, Merge } from "all-you-need/types";
30
32
 
31
33
  ### Object
32
34
 
33
- | Type | Description |
35
+ | Name | Description |
34
36
  |------|-------------|
35
37
  | `DeepPartial<T>` | Makes all properties optional recursively |
36
38
  | `DeepReadonly<T>` | Makes all properties readonly recursively |
37
39
  | `Merge<T, S>` | Merge two object types, second overrides first |
38
- | `MutableKeys<T>` | Extract mutable keys from a type |
39
- | `Prettify<T>` | Simplify complex intersection types for readability |
40
+ | `MutableKeys<T>` | Extract mutable (non-readonly) keys from a type |
41
+ | `Prettify<T>` | Flatten complex intersection types for better readability in IDE hints |
40
42
  | `ReadonlyKeys<T>` | Extract readonly keys from a type |
41
43
  | `RequiredDeep<T>` | Makes all properties required recursively |
42
44
  | `SetNonNullable<T, K>` | Make specified keys non-nullable |
43
45
  | `SetOptional<T, K>` | Make specified keys optional |
44
46
  | `SetRequired<T, K>` | Make specified keys required |
45
- | `Writable<T>` | Remove readonly from all properties |
46
- | `WritableDeep<T>` | Remove readonly recursively |
47
+ | `Writable<T>` | Remove readonly from all properties (shallow) |
48
+ | `WritableDeep<T>` | Remove readonly from all properties recursively |
47
49
 
48
50
  ### Keys
49
51
 
50
- | Type | Description |
52
+ | Name | Description |
51
53
  |------|-------------|
52
- | `ConditionalKeys<T, Condition>` | Extract keys whose values match condition |
53
- | `ConditionalPick<T, Condition>` | Pick properties whose values match condition |
54
- | `ConditionalExcept<T, Condition>` | Omit properties whose values match condition |
55
- | `Entries<T>` | Type-safe `Object.entries()` return type |
56
- | `OmitByValue<T, V>` | Omit properties by value type |
57
- | `PickByValue<T, V>` | Pick properties by value type |
58
- | `StringKeyOf<T>` | Extract string keys from a type |
59
- | `ValueOf<T>` | Extract value types from an object type |
54
+ | `ConditionalKeys<T, Condition>` | Extract keys whose values extend the given condition type |
55
+ | `ConditionalPick<T, Condition>` | Pick properties whose values extend the given condition type |
56
+ | `ConditionalExcept<T, Condition>` | Omit properties whose values extend the given condition type |
57
+ | `Entries<T>` | Type-safe return type for `Object.entries()` |
58
+ | `OmitByValue<T, V>` | Omit properties whose values extend `V` |
59
+ | `PickByValue<T, V>` | Pick properties whose values extend `V` |
60
+ | `StringKeyOf<T>` | Extract only string keys from a type (excludes `number` and `symbol`) |
61
+ | `ValueOf<T>` | Union of all value types from an object type |
60
62
 
61
63
  ### String
62
64
 
63
- | Type | Description |
65
+ | Name | Description |
64
66
  |------|-------------|
65
- | `CamelCase<S>` | Convert string literal to camelCase |
66
- | `KebabCase<S>` | Convert string literal to kebab-case |
67
- | `Join<T, D>` | Join tuple of strings with a delimiter |
68
- | `Split<S, D>` | Split string literal by a delimiter |
67
+ | `CamelCase<S>` | Convert string literal type to camelCase |
68
+ | `KebabCase<S>` | Convert string literal type to kebab-case |
69
+ | `Join<T, D>` | Join a tuple of string literals with a delimiter |
70
+ | `Split<S, D>` | Split a string literal type by a delimiter into a tuple |
69
71
 
70
72
  ### Union
71
73
 
72
- | Type | Description |
74
+ | Name | Description |
73
75
  |------|-------------|
74
- | `LiteralUnion<L, Base>` | Union with autocomplete for literals |
75
- | `TupleToUnion<T>` | Convert tuple type to union |
76
- | `UnionToIntersection<U>` | Convert union type to intersection |
76
+ | `LiteralUnion<L, Base>` | Create a union that preserves IDE autocomplete for literal types |
77
+ | `TupleToUnion<T>` | Convert a tuple/array type to a union of its elements |
78
+ | `UnionToIntersection<U>` | Convert a union type to an intersection type |
77
79
 
78
80
  ### Async
79
81
 
80
- | Type | Description |
82
+ | Name | Description |
81
83
  |------|-------------|
82
- | `AsyncReturnType<T>` | Unwrap return type of an async function |
83
- | `Promisable<T>` | `T | PromiseLike<T>` |
84
+ | `AsyncReturnType<T>` | Extract the unwrapped return type of an async function |
85
+ | `Promisable<T>` | A value that can be either `T` or `PromiseLike<T>` |
84
86
 
85
87
  ### Common
86
88
 
87
- | Type | Description |
89
+ | Name | Description |
88
90
  |------|-------------|
89
- | `Branded<T, Brand>` | Nominal/branded type |
90
- | `EmptyObject` | `Record<string, never>` |
91
- | `IsEqual<A, B>` | Check if two types are equal |
92
- | `Nullable<T>` | `T | null` |
93
- | `Optional<T>` | `T | undefined` |
91
+ | `Branded<T, Brand>` | Create a nominal/branded type to distinguish structurally identical types |
92
+ | `EmptyObject` | An object type with no properties (`Record<string, never>`) |
93
+ | `IsEqual<A, B>` | Check at the type level if two types are exactly equal |
94
+ | `Nullable<T>` | `T \| null` |
95
+ | `Optional<T>` | `T \| undefined` |
94
96
 
95
97
  ### JSON
96
98
 
97
- | Type | Description |
99
+ | Name | Description |
98
100
  |------|-------------|
99
- | `JsonValue` | Any valid JSON value |
100
- | `JsonObject` | JSON object type |
101
- | `JsonArray` | JSON array type |
102
- | `JsonPrimitive` | JSON primitive type |
101
+ | `JsonValue` | Any valid JSON value (primitive, object, or array) |
102
+ | `JsonObject` | A JSON-compatible object (`Record<string, JsonValue>`) |
103
+ | `JsonArray` | A JSON-compatible array (`JsonValue[]`) |
104
+ | `JsonPrimitive` | JSON primitive: `string \| number \| boolean \| null` |
103
105
 
104
106
  ## Entities
105
107
 
108
+ | Name | Description |
109
+ |------|-------------|
110
+ | `Result<T, E>` | Monadic error handling with explicit `Ok`/`Err` — no exceptions |
111
+ | `Queue<T>` | FIFO queue data structure |
112
+ | `Stack<T>` | LIFO stack data structure |
113
+ | `TypedStorage` | Universal typed wrapper for `localStorage`/`sessionStorage` with prefix and TTL |
114
+ | `Logger` | Leveled logger with prefix, timestamp, and configurable log levels |
115
+
116
+ Also exports types: `StorageOptions`, `TypedStorageConfig`, `LogLevel`, `LoggerOptions`.
117
+
106
118
  ### `Result<T, E>`
107
119
 
108
120
  Monadic error handling — explicit `Ok`/`Err` without exceptions.
109
121
 
122
+ | Method | Signature | Description |
123
+ |--------|-----------|-------------|
124
+ | `Result.ok` | `<T>(value: T) => Result<T, never>` | Create a successful result |
125
+ | `Result.err` | `<E>(error: E) => Result<never, E>` | Create an error result |
126
+ | `isOk` | `() => this is Result<T, never>` | Type guard — check if result is Ok |
127
+ | `isErr` | `() => this is Result<never, E>` | Type guard — check if result is Err |
128
+ | `value` | `get value(): T` | Get Ok value (throws if Err) |
129
+ | `error` | `get error(): E` | Get Err value (throws if Ok) |
130
+ | `unwrap` | `() => T` | Get value or throw if Err |
131
+ | `unwrapOr` | `(defaultValue: T) => T` | Get value or return default |
132
+ | `unwrapErr` | `() => E` | Get error or throw if Ok |
133
+ | `map` | `<U>(fn: (value: T) => U) => Result<U, E>` | Transform Ok value |
134
+ | `mapErr` | `<F>(fn: (error: E) => F) => Result<T, F>` | Transform Err value |
135
+ | `flatMap` | `<U>(fn: (value: T) => Result<U, E>) => Result<U, E>` | Chain Result-returning operations |
136
+
110
137
  ```typescript
111
138
  import { Result } from "all-you-need/entities";
112
139
 
@@ -114,6 +141,7 @@ const ok = Result.ok(42);
114
141
  const err = Result.err("not found");
115
142
 
116
143
  ok.isOk(); // true
144
+ ok.value; // 42
117
145
  ok.unwrap(); // 42
118
146
  err.unwrapOr(0); // 0
119
147
 
@@ -122,7 +150,17 @@ ok.map(x => x * 2).unwrap(); // 84
122
150
 
123
151
  ### `Queue<T>`
124
152
 
125
- FIFO queue with `enqueue`, `dequeue`, `peek`, `size`, `isEmpty`, `clear`, `toArray`.
153
+ FIFO queue data structure.
154
+
155
+ | Method | Signature | Description |
156
+ |--------|-----------|-------------|
157
+ | `enqueue` | `(item: T) => void` | Add item to the back of the queue |
158
+ | `dequeue` | `() => T \| undefined` | Remove and return the front item |
159
+ | `peek` | `() => T \| undefined` | View the front item without removing |
160
+ | `size` | `get size(): number` | Number of items in the queue |
161
+ | `isEmpty` | `get isEmpty(): boolean` | Whether the queue is empty |
162
+ | `clear` | `() => void` | Remove all items |
163
+ | `toArray` | `() => T[]` | Convert queue to array |
126
164
 
127
165
  ```typescript
128
166
  import { Queue } from "all-you-need/entities";
@@ -135,7 +173,17 @@ q.dequeue(); // 1
135
173
 
136
174
  ### `Stack<T>`
137
175
 
138
- LIFO stack with `push`, `pop`, `peek`, `size`, `isEmpty`, `clear`, `toArray`.
176
+ LIFO stack data structure.
177
+
178
+ | Method | Signature | Description |
179
+ |--------|-----------|-------------|
180
+ | `push` | `(item: T) => void` | Push item onto the top |
181
+ | `pop` | `() => T \| undefined` | Remove and return the top item |
182
+ | `peek` | `() => T \| undefined` | View the top item without removing |
183
+ | `size` | `get size(): number` | Number of items in the stack |
184
+ | `isEmpty` | `get isEmpty(): boolean` | Whether the stack is empty |
185
+ | `clear` | `() => void` | Remove all items |
186
+ | `toArray` | `() => T[]` | Convert stack to array |
139
187
 
140
188
  ```typescript
141
189
  import { Stack } from "all-you-need/entities";
@@ -148,160 +196,190 @@ s.pop(); // 2
148
196
 
149
197
  ### `TypedStorage`
150
198
 
151
- Universal typed wrapper for `localStorage` / `sessionStorage` / custom storage with prefix, TTL, and serialization.
199
+ Universal typed wrapper for `localStorage` / `sessionStorage` / custom storage with prefix, TTL, and JSON serialization.
200
+
201
+ | Method | Signature | Description |
202
+ |--------|-----------|-------------|
203
+ | `constructor` | `(config: TypedStorageConfig) => TypedStorage` | Create storage with `adapter` and optional `prefix` |
204
+ | `get` | `<T>(key: string) => T \| null` | Retrieve a typed value by key (returns null if missing or expired) |
205
+ | `set` | `<T>(key: string, value: T, options?: StorageOptions) => void` | Store a value with optional TTL (in ms) |
206
+ | `remove` | `(key: string) => void` | Remove a key |
207
+ | `has` | `(key: string) => boolean` | Check if key exists and is not expired |
208
+ | `clear` | `() => void` | Remove all keys (only prefixed keys if prefix is set) |
209
+ | `keys` | `() => string[]` | Get all keys (returned without prefix) |
152
210
 
153
211
  ```typescript
154
212
  import { TypedStorage } from "all-you-need/entities";
155
213
 
156
- const storage = new TypedStorage({ prefix: "app_" });
214
+ const storage = new TypedStorage({
215
+ adapter: localStorage,
216
+ prefix: "app_",
217
+ });
218
+
157
219
  storage.set("user", { name: "Dan" });
158
220
  storage.get("user"); // { name: "Dan" }
221
+ storage.set("token", "abc", { ttl: 60000 }); // expires in 60s
159
222
  ```
160
223
 
161
224
  ### `Logger`
162
225
 
163
- Leveled logger (`debug`, `info`, `warn`, `error`) with customizable output and log levels.
226
+ Leveled logger with optional prefix and timestamp.
227
+
228
+ | Method | Signature | Description |
229
+ |--------|-----------|-------------|
230
+ | `constructor` | `(options?: LoggerOptions) => Logger` | Create logger with optional `level`, `prefix`, `timestamp` |
231
+ | `debug` | `(...args: unknown[]) => void` | Log at debug level |
232
+ | `info` | `(...args: unknown[]) => void` | Log at info level |
233
+ | `warn` | `(...args: unknown[]) => void` | Log at warn level |
234
+ | `error` | `(...args: unknown[]) => void` | Log at error level |
235
+
236
+ `LoggerOptions`: `{ level?: LogLevel, prefix?: string, timestamp?: boolean }`
164
237
 
165
238
  ```typescript
166
239
  import { Logger } from "all-you-need/entities";
167
240
 
168
- const logger = new Logger({ level: "info" });
241
+ const logger = new Logger({ level: "info", prefix: "[App]", timestamp: true });
169
242
  logger.info("Server started");
170
- logger.debug("This will be hidden");
243
+ logger.debug("This will be hidden"); // level is below "info"
171
244
  ```
172
245
 
173
246
  ## Utils
174
247
 
175
248
  ### String
176
249
 
177
- | Function | Signature |
178
- |----------|-----------|
179
- | `capitalize` | `(str: string) => string` |
180
- | `truncate` | `(str: string, maxLength: number, suffix?: string) => string` |
181
- | `slugify` | `(str: string) => string` |
182
- | `toCamelCase` | `(str: string) => string` |
183
- | `toKebabCase` | `(str: string) => string` |
184
- | `toPascalCase` | `(str: string) => string` |
185
- | `toSnakeCase` | `(str: string) => string` |
186
- | `escapeHtml` | `(str: string) => string` |
187
- | `unescapeHtml` | `(str: string) => string` |
188
- | `escapeRegExp` | `(str: string) => string` |
189
- | `template` | `(str: string, data: Record<string, string \| number>) => string` |
190
- | `maskString` | `(str: string, visibleStart?: number, visibleEnd?: number, maskChar?: string) => string` |
191
- | `pluralize` | `(count: number, one: string, few: string, many: string) => string` |
250
+ | Name | Description | Signature |
251
+ |------|-------------|-----------|
252
+ | `capitalize` | Capitalize first letter of a string | `(str: string) => string` |
253
+ | `truncate` | Truncate string to max length with suffix (default `"..."`) | `(str: string, maxLength: number, suffix?: string) => string` |
254
+ | `slugify` | Convert string to URL-friendly slug | `(str: string) => string` |
255
+ | `toCamelCase` | Convert string to camelCase | `(str: string) => string` |
256
+ | `toKebabCase` | Convert string to kebab-case | `(str: string) => string` |
257
+ | `toPascalCase` | Convert string to PascalCase | `(str: string) => string` |
258
+ | `toSnakeCase` | Convert string to snake_case | `(str: string) => string` |
259
+ | `escapeHtml` | Escape HTML special characters to prevent XSS | `(str: string) => string` |
260
+ | `unescapeHtml` | Unescape HTML entities back to characters | `(str: string) => string` |
261
+ | `escapeRegExp` | Escape special regex characters in a string | `(str: string) => string` |
262
+ | `template` | Replace `{{key}}` placeholders with values from data object | `(str: string, data: Record<string, string \| number>) => string` |
263
+ | `maskString` | Mask middle characters (e.g. for credit cards, emails) | `(str: string, visibleStart?: number, visibleEnd?: number, maskChar?: string) => string` |
264
+ | `pluralize` | Russian pluralization — select correct word form by count | `(count: number, one: string, few: string, many: string) => string` |
192
265
 
193
266
  ### Array
194
267
 
195
- | Function | Signature |
196
- |----------|-----------|
197
- | `chunk` | `<T>(arr: T[], size: number) => T[][]` |
198
- | `compact` | `<T>(arr: (T \| null \| undefined \| false \| 0 \| "")[]) => T[]` |
199
- | `difference` | `<T>(a: T[], b: T[]) => T[]` |
200
- | `first` | `<T>(arr: T[]) => T \| undefined` |
201
- | `last` | `<T>(arr: T[]) => T \| undefined` |
202
- | `flatten` | `<T>(arr: unknown[]) => T[]` |
203
- | `groupBy` | `<T, K>(arr: T[], keyFn: (item: T) => K) => Record<K, T[]>` |
204
- | `intersection` | `<T>(a: T[], b: T[]) => T[]` |
205
- | `range` | `(start: number, end: number, step?: number) => number[]` |
206
- | `shuffle` | `<T>(arr: T[]) => T[]` |
207
- | `sortBy` | `<T>(arr: T[], keyFn: (item: T) => string \| number) => T[]` |
208
- | `unique` | `<T>(arr: T[]) => T[]` |
209
- | `uniqueBy` | `<T>(arr: T[], keyFn: (item: T) => unknown) => T[]` |
210
- | `zip` | `<T, U>(a: T[], b: U[]) => [T, U][]` |
268
+ | Name | Description | Signature |
269
+ |------|-------------|-----------|
270
+ | `chunk` | Split array into chunks of given size | `<T>(arr: T[], size: number) => T[][]` |
271
+ | `compact` | Remove falsy values (null, undefined, false, 0, "") from array | `<T>(arr: (T \| null \| undefined \| false \| 0 \| "")[]) => T[]` |
272
+ | `difference` | Elements in first array that are not in second | `<T>(a: T[], b: T[]) => T[]` |
273
+ | `first` | Get the first element of an array | `<T>(arr: T[]) => T \| undefined` |
274
+ | `last` | Get the last element of an array | `<T>(arr: T[]) => T \| undefined` |
275
+ | `flatten` | Recursively flatten a nested array | `<T>(arr: unknown[]) => T[]` |
276
+ | `groupBy` | Group array items by a key function | `<T, K>(arr: T[], keyFn: (item: T) => K) => Record<K, T[]>` |
277
+ | `intersection` | Elements present in both arrays | `<T>(a: T[], b: T[]) => T[]` |
278
+ | `range` | Generate an array of numbers from start to end | `(start: number, end: number, step?: number) => number[]` |
279
+ | `shuffle` | Randomly shuffle array elements (Fisher-Yates) | `<T>(arr: T[]) => T[]` |
280
+ | `sortBy` | Sort array by a key function (returns new array) | `<T>(arr: T[], keyFn: (item: T) => string \| number) => T[]` |
281
+ | `unique` | Remove duplicate values from an array | `<T>(arr: T[]) => T[]` |
282
+ | `uniqueBy` | Remove duplicates by a key function | `<T>(arr: T[], keyFn: (item: T) => unknown) => T[]` |
283
+ | `zip` | Combine two arrays into pairs | `<T, U>(a: T[], b: U[]) => [T, U][]` |
211
284
 
212
285
  ### Object
213
286
 
214
- | Function | Signature |
215
- |----------|-----------|
216
- | `pick` | `<T, K>(obj: T, keys: K[]) => Pick<T, K>` |
217
- | `omit` | `<T, K>(obj: T, keys: K[]) => Omit<T, K>` |
218
- | `deepClone` | `<T>(value: T) => T` |
219
- | `deepEqual` | `(a: unknown, b: unknown) => boolean` |
220
- | `deepMerge` | `<T, S>(target: T, source: S) => T & S` |
221
- | `diff` | `<T>(original: T, changed: T) => Partial<T>` |
222
- | `flattenObject` | `(obj: Record<string, unknown>, prefix?: string, separator?: string) => Record<string, unknown>` |
223
- | `isEmpty` | `(value: unknown) => boolean` |
224
- | `mapKeys` | `<T>(obj: T, fn: (key: string) => string) => Record<string, T[keyof T]>` |
225
- | `mapValues` | `<T, U>(obj: T, fn: (value: T[keyof T], key: string) => U) => Record<string, U>` |
287
+ | Name | Description | Signature |
288
+ |------|-------------|-----------|
289
+ | `pick` | Create object with only specified keys | `<T, K>(obj: T, keys: K[]) => Pick<T, K>` |
290
+ | `omit` | Create object without specified keys | `<T, K>(obj: T, keys: K[]) => Omit<T, K>` |
291
+ | `deepClone` | Deep clone any value using structuredClone | `<T>(value: T) => T` |
292
+ | `deepEqual` | Recursively compare two values for equality | `(a: unknown, b: unknown) => boolean` |
293
+ | `deepMerge` | Recursively merge two objects | `<T, S>(target: T, source: S) => T & S` |
294
+ | `diff` | Get changed properties between two objects | `<T>(original: T, changed: T) => Partial<T>` |
295
+ | `flattenObject` | Flatten nested object to dot-notation keys | `(obj: Record<string, unknown>, prefix?: string, separator?: string) => Record<string, unknown>` |
296
+ | `isEmpty` | Check if value is empty (null, undefined, empty string/array/object) | `(value: unknown) => boolean` |
297
+ | `mapKeys` | Transform object keys using a mapping function | `<T>(obj: T, fn: (key: string) => string) => Record<string, T[keyof T]>` |
298
+ | `mapValues` | Transform object values using a mapping function | `<T, U>(obj: T, fn: (value: T[keyof T], key: string) => U) => Record<string, U>` |
226
299
 
227
300
  ### Number
228
301
 
229
- | Function | Signature |
230
- |----------|-----------|
231
- | `clamp` | `(value: number, min: number, max: number) => number` |
232
- | `round` | `(value: number, decimals?: number) => number` |
233
- | `randomInt` | `(min: number, max: number) => number` |
234
- | `sum` | `(arr: number[]) => number` |
235
- | `average` | `(arr: number[]) => number` |
236
- | `median` | `(arr: number[]) => number` |
237
- | `percentage` | `(value: number, total: number) => number` |
238
- | `formatNumber` | `(value: number, locale?: string) => string` |
239
- | `formatCurrency` | `(value: number, currency?: string, locale?: string) => string` |
240
- | `formatBytes` | `(bytes: number, decimals?: number) => string` |
302
+ | Name | Description | Signature |
303
+ |------|-------------|-----------|
304
+ | `clamp` | Restrict a number to a min/max range | `(value: number, min: number, max: number) => number` |
305
+ | `round` | Round number to specified decimal places (default 0) | `(value: number, decimals?: number) => number` |
306
+ | `randomInt` | Generate random integer in inclusive range | `(min: number, max: number) => number` |
307
+ | `sum` | Sum all numbers in an array | `(arr: number[]) => number` |
308
+ | `average` | Calculate arithmetic mean of an array | `(arr: number[]) => number` |
309
+ | `median` | Calculate median value of an array | `(arr: number[]) => number` |
310
+ | `percentage` | Calculate what percent value is of total | `(value: number, total: number) => number` |
311
+ | `formatNumber` | Format number with locale-aware separators (default `"ru-RU"`) | `(value: number, locale?: string) => string` |
312
+ | `formatCurrency` | Format number as currency (default `"RUB"`, `"ru-RU"`) | `(value: number, currency?: string, locale?: string) => string` |
313
+ | `formatBytes` | Format bytes into human-readable string (KB, MB, GB...) | `(bytes: number, decimals?: number) => string` |
241
314
 
242
315
  ### Date
243
316
 
244
- | Function | Signature |
245
- |----------|-----------|
246
- | `formatDate` | `(date: Date, options?: Intl.DateTimeFormatOptions, locale?: string) => string` |
247
- | `relativeTime` | `(date: Date, locale?: string) => string` |
248
- | `isToday` | `(date: Date) => boolean` |
249
- | `isYesterday` | `(date: Date) => boolean` |
250
- | `addDays` | `(date: Date, days: number) => Date` |
251
- | `addMonths` | `(date: Date, months: number) => Date` |
252
- | `daysBetween` | `(a: Date, b: Date) => number` |
317
+ | Name | Description | Signature |
318
+ |------|-------------|-----------|
319
+ | `formatDate` | Format date using `Intl.DateTimeFormat` (default `"ru-RU"`) | `(date: Date, options?: Intl.DateTimeFormatOptions, locale?: string) => string` |
320
+ | `relativeTime` | Get human-readable relative time (default `"ru-RU"`) | `(date: Date, locale?: string) => string` |
321
+ | `isToday` | Check if a date is today | `(date: Date) => boolean` |
322
+ | `isYesterday` | Check if a date is yesterday | `(date: Date) => boolean` |
323
+ | `addDays` | Add or subtract days from a date | `(date: Date, days: number) => Date` |
324
+ | `addMonths` | Add or subtract months from a date | `(date: Date, months: number) => Date` |
325
+ | `daysBetween` | Calculate absolute number of days between two dates | `(a: Date, b: Date) => number` |
253
326
 
254
327
  ### Async
255
328
 
256
- | Function | Signature |
257
- |----------|-----------|
258
- | `sleep` | `(ms: number) => Promise<void>` |
259
- | `debounce` | `<T>(fn: T, ms: number) => (...args) => void` |
260
- | `throttle` | `<T>(fn: T, ms: number) => (...args) => void` |
261
- | `retry` | `<T>(fn: () => Promise<T>, options?: RetryOptions) => Promise<T>` |
262
- | `timeout` | `<T>(promise: Promise<T>, ms: number) => Promise<T>` |
263
- | `pLimit` | `(concurrency: number) => <T>(fn: () => Promise<T>) => Promise<T>` |
264
- | `pSettle` | `<T>(promises: Promise<T>[]) => Promise<SettledResult<T>[]>` |
265
- | `tryCatch` | `<T>(fn: () => Promise<T>) => Promise<[null, T] \| [Error, null]>` |
329
+ | Name | Description | Signature |
330
+ |------|-------------|-----------|
331
+ | `sleep` | Pause execution for specified milliseconds | `(ms: number) => Promise<void>` |
332
+ | `debounce` | Delay function call until after a period of inactivity | `<T>(fn: T, ms: number) => (...args: Parameters<T>) => void` |
333
+ | `throttle` | Limit function to execute at most once per interval | `<T>(fn: T, ms: number) => (...args: Parameters<T>) => void` |
334
+ | `retry` | Retry an async function with configurable attempts, delay, and backoff factor | `<T>(fn: () => Promise<T>, options?: RetryOptions) => Promise<T>` |
335
+ | `timeout` | Reject a promise if it doesn't resolve within specified time | `<T>(promise: Promise<T>, ms: number) => Promise<T>` |
336
+ | `pLimit` | Limit number of concurrent async operations | `(concurrency: number) => <T>(fn: () => Promise<T>) => Promise<T>` |
337
+ | `pSettle` | Settle all promises and return results with status | `<T>(promises: Promise<T>[]) => Promise<SettledResult<T>[]>` |
338
+ | `tryCatch` | Go-style async error handling — returns `[error, result]` tuple | `<T>(fn: () => Promise<T>) => Promise<[null, T] \| [Error, null]>` |
339
+ | `tryCatchSync` | Go-style sync error handling — returns `[error, result]` tuple | `<T>(fn: () => T) => [null, T] \| [Error, null]` |
340
+
341
+ Also exports types: `RetryOptions`, `SettledOk<T>`, `SettledErr`, `SettledResult<T>`.
266
342
 
267
343
  ### Validation
268
344
 
269
- | Function | Signature |
270
- |----------|-----------|
271
- | `isEmail` | `(str: string) => boolean` |
272
- | `isURL` | `(str: string) => boolean` |
273
- | `isPhone` | `(str: string) => boolean` |
274
- | `isStrongPassword` | `(str: string, options?: PasswordOptions) => boolean` |
275
- | `isINN` | `(str: string) => boolean` |
345
+ | Name | Description | Signature |
346
+ |------|-------------|-----------|
347
+ | `isEmail` | Validate email address format | `(str: string) => boolean` |
348
+ | `isURL` | Validate URL format | `(str: string) => boolean` |
349
+ | `isPhone` | Validate phone number format | `(str: string) => boolean` |
350
+ | `isStrongPassword` | Check password strength (length, cases, digits, symbols) | `(str: string, options?: PasswordOptions) => boolean` |
351
+ | `isINN` | Validate Russian INN (taxpayer identification number) | `(str: string) => boolean` |
352
+
353
+ Also exports type: `PasswordOptions`.
276
354
 
277
355
  ### Type Guards
278
356
 
279
- | Function | Signature |
280
- |----------|-----------|
281
- | `isString` | `(value: unknown) => value is string` |
282
- | `isNumber` | `(value: unknown) => value is number` |
283
- | `isBoolean` | `(value: unknown) => value is boolean` |
284
- | `isArray` | `(value: unknown) => value is unknown[]` |
285
- | `isObject` | `(value: unknown) => value is Record<string, unknown>` |
286
- | `isFunction` | `(value: unknown) => value is (...args: unknown[]) => unknown` |
287
- | `isDefined` | `<T>(value: T \| undefined) => value is T` |
288
- | `isNonNullable` | `<T>(value: T) => value is NonNullable<T>` |
289
- | `hasProperty` | `<K>(obj: unknown, key: K) => obj is Record<K, unknown>` |
357
+ | Name | Description | Signature |
358
+ |------|-------------|-----------|
359
+ | `isString` | Check if value is a string | `(value: unknown) => value is string` |
360
+ | `isNumber` | Check if value is a finite number (excludes NaN) | `(value: unknown) => value is number` |
361
+ | `isBoolean` | Check if value is a boolean | `(value: unknown) => value is boolean` |
362
+ | `isArray` | Check if value is an array | `(value: unknown) => value is unknown[]` |
363
+ | `isObject` | Check if value is a plain object (not null, not array) | `(value: unknown) => value is Record<string, unknown>` |
364
+ | `isFunction` | Check if value is a function | `(value: unknown) => value is (...args: unknown[]) => unknown` |
365
+ | `isDefined` | Check if value is not undefined | `<T>(value: T \| undefined) => value is T` |
366
+ | `isNonNullable` | Check if value is not null and not undefined | `<T>(value: T) => value is NonNullable<T>` |
367
+ | `hasProperty` | Check if object has a specific property | `<K>(obj: unknown, key: K) => obj is Record<K, unknown>` |
290
368
 
291
369
  ### Environment
292
370
 
293
- | Function | Signature |
294
- |----------|-----------|
295
- | `isBrowser` | `() => boolean` |
296
- | `isNode` | `() => boolean` |
297
- | `isSSR` | `() => boolean` |
371
+ | Name | Description | Signature |
372
+ |------|-------------|-----------|
373
+ | `isBrowser` | Check if running in a browser environment | `() => boolean` |
374
+ | `isNode` | Check if running in Node.js | `() => boolean` |
375
+ | `isSSR` | Check if running in SSR (Node.js without DOM) | `() => boolean` |
298
376
 
299
377
  ### ID
300
378
 
301
- | Function | Signature |
302
- |----------|-----------|
303
- | `uuid` | `() => string` |
304
- | `nanoid` | `(size?: number) => string` |
379
+ | Name | Description | Signature |
380
+ |------|-------------|-----------|
381
+ | `uuid` | Generate a random UUID v4 | `() => string` |
382
+ | `nanoid` | Generate a compact URL-safe unique ID (default 21 chars) | `(size?: number) => string` |
305
383
 
306
384
  ## Scripts
307
385