@pivanov/utils 0.0.3 โ†’ 1.0.1

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.
Files changed (46) hide show
  1. package/README.md +125 -335
  2. package/dist/cjs/assertion/index.js +7 -0
  3. package/dist/cjs/cache/index.js +7 -0
  4. package/dist/cjs/index.js +2 -2
  5. package/dist/cjs/object/index.js +7 -0
  6. package/dist/cjs/package.json +3 -0
  7. package/dist/cjs/promise/index.js +7 -0
  8. package/dist/cjs/string/index.js +7 -0
  9. package/dist/cjs/tools/index.js +7 -0
  10. package/dist/cjs/types/index.js +7 -0
  11. package/dist/esm/assertion/index.js +7 -0
  12. package/dist/esm/cache/index.js +7 -0
  13. package/dist/esm/chunk-3ymwkv74.js +8 -0
  14. package/dist/esm/chunk-jtvcg9xg.js +6 -0
  15. package/dist/esm/chunk-m1ymbxy0.js +8 -0
  16. package/dist/esm/chunk-m7vtwv05.js +8 -0
  17. package/dist/esm/chunk-vtcj34cd.js +8 -0
  18. package/dist/esm/chunk-y84wjyfx.js +7 -0
  19. package/dist/esm/index.js +2 -2
  20. package/dist/esm/object/index.js +7 -0
  21. package/dist/esm/package.json +3 -0
  22. package/dist/esm/promise/index.js +7 -0
  23. package/dist/esm/string/index.js +7 -0
  24. package/dist/esm/tools/index.js +7 -0
  25. package/dist/esm/types/index.js +7 -0
  26. package/dist/types/assertion/index.d.ts +145 -0
  27. package/dist/types/cache/index.d.ts +16 -0
  28. package/dist/types/cache/internal.d.ts +12 -0
  29. package/dist/types/cache/response.d.ts +91 -0
  30. package/dist/types/cache/storage.d.ts +75 -0
  31. package/dist/types/cache/support.d.ts +14 -0
  32. package/dist/types/index.d.ts +6 -0
  33. package/dist/types/object/index.d.ts +118 -0
  34. package/dist/types/promise/index.d.ts +81 -0
  35. package/dist/types/string/index.d.ts +127 -0
  36. package/dist/types/tools/deepClone.d.ts +2 -0
  37. package/dist/types/tools/dom.d.ts +82 -0
  38. package/dist/types/tools/eventBus/eventBus.d.ts +37 -0
  39. package/dist/types/tools/eventBus/index.d.ts +3 -0
  40. package/dist/types/tools/eventBus/types.d.ts +47 -0
  41. package/dist/types/tools/eventBus/useEventBus.d.ts +3 -0
  42. package/dist/types/tools/index.d.ts +5 -0
  43. package/dist/types/tools/isEqual.d.ts +21 -0
  44. package/dist/types/types/index.d.ts +61 -0
  45. package/package.json +74 -35
  46. package/dist/index.d.ts +0 -691
package/README.md CHANGED
@@ -1,408 +1,198 @@
1
1
  # @pivanov/utils
2
2
 
3
3
  <p align="center">
4
- <i>A comprehensive collection of TypeScript utilities for modern web development</i>
5
- <br />
6
- <br />
4
+ <i>A focused collection of TypeScript utilities for modern web development.</i>
5
+ <br /><br />
7
6
  <img src="https://img.shields.io/npm/v/@pivanov/utils?logo=npm" alt="NPM Version" />
8
- &nbsp;&nbsp;
9
- <img src="https://img.shields.io/bundlephobia/minzip/@pivanov/utils" alt="Bundle Size" />
10
- &nbsp;&nbsp;
7
+ &nbsp;
8
+ <img src="https://img.shields.io/npm/dw/@pivanov/utils" alt="Weekly Downloads" />
9
+ &nbsp;
11
10
  <img src="https://github.com/pivanov/pivanov-utils/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI Status" />
12
- &nbsp;&nbsp;
11
+ &nbsp;
13
12
  <img src="https://codecov.io/github/pivanov/pivanov-utils/graph/badge.svg?token=EPRKTP7D79" alt="Coverage Status" />
14
13
  </p>
15
14
 
16
15
  ## Features
17
16
 
18
- - ๐Ÿ”’ **Type-Safe** - Full TypeScript support with strict type checking and excellent type inference
19
- - ๐ŸŽฏ **Tree-Shakeable** - Import only what you need for minimal bundle size
20
- - โšก **Performance Focused** - Optimized implementations with no external dependencies
21
- - ๐Ÿงช **Well Tested** - Comprehensive test coverage for reliability
22
- - ๐Ÿ“ฆ **Modular** - Organized into focused modules for easy navigation
23
- - ๐Ÿ“ **Well Documented** - Detailed JSDoc comments and examples
17
+ - **Fully typed** - strict TypeScript across every module, literal-type preserving where it matters
18
+ - **Tree-shakeable** - ESM + CJS + per-module subpath exports, `"sideEffects": false`
19
+ - **Zero dependencies** - React is an optional peer dep only for the `useEventBus` hook
20
+ - **Well tested** - 200+ tests, real edge cases (circular refs, typed arrays, Buffers, symbols)
24
21
 
25
22
  ## Installation
26
23
 
27
24
  ```bash
28
- pnpm add @pivanov/utils
29
- # or
30
- npm install @pivanov/utils
31
- # or
32
- yarn add @pivanov/utils
25
+ bun add @pivanov/utils
33
26
  ```
34
27
 
35
- ## Quick Start
36
-
37
- ```typescript
38
- import { camelCase } from '@pivanov/utils/string';
39
- import { pick } from '@pivanov/utils/object';
40
- import { isString } from '@pivanov/utils/assertion';
41
-
42
- camelCase('foo-bar'); // 'fooBar'
43
- pick({ name: 'John', age: 30 }, ['name']); // { name: 'John' }
44
- isString('hello'); // true
28
+ ```bash
29
+ npm install @pivanov/utils
45
30
  ```
46
31
 
47
- ## API Reference
48
-
49
- ### Type Guards & Assertions
50
-
51
- Runtime type checking with TypeScript type narrowing.
52
-
53
- ```typescript
54
- import {
55
- isString,
56
- isNumber,
57
- isBoolean,
58
- isObject,
59
- isFunction,
60
- isNull,
61
- isUndefined,
62
- } from '@pivanov/utils/assertion';
63
-
64
- // Type guards automatically narrow TypeScript types
65
- function processValue(value: unknown) {
66
- if (isString(value)) {
67
- return value.toUpperCase(); // TypeScript knows this is a string
68
- }
69
- if (isNumber(value)) {
70
- return value.toFixed(2); // TypeScript knows this is a number
71
- }
72
- }
73
-
74
- // Examples
75
- isString('hello'); // true
76
- isNumber(42); // true
77
- isBoolean(true); // true
78
- isObject({}); // true (plain objects only, not arrays)
79
- isFunction(() => {}); // true
80
- isNull(null); // true
81
- isUndefined(undefined); // true
32
+ ```bash
33
+ yarn add @pivanov/utils
82
34
  ```
83
35
 
84
- ### Object Utilities
85
-
86
- Immutable object manipulation with full type safety.
87
-
88
- ```typescript
89
- import { pick, omit, merge, deepMerge } from '@pivanov/utils/object';
90
-
91
- // Pick specific properties
92
- const user = { name: 'John', age: 30, email: 'john@example.com' };
93
- pick(user, ['name', 'email']);
94
- // { name: 'John', email: 'john@example.com' }
95
-
96
- // Omit specific properties
97
- omit(user, ['email']);
98
- // { name: 'John', age: 30 }
99
-
100
- // Shallow merge
101
- merge({ a: 1, b: 2 }, { b: 3, c: 4 });
102
- // { a: 1, b: 3, c: 4 }
103
-
104
- // Deep merge with nested structures
105
- deepMerge(
106
- { user: { name: 'John', settings: { theme: 'dark' } } },
107
- { user: { settings: { fontSize: 14 } } }
108
- );
109
- // { user: { name: 'John', settings: { theme: 'dark', fontSize: 14 } } }
36
+ ```bash
37
+ pnpm add @pivanov/utils
110
38
  ```
111
39
 
112
- ### String Utilities
40
+ ## Quick start
113
41
 
114
- String transformation utilities with TypeScript template literal types support.
115
-
116
- ```typescript
117
- import {
118
- camelCase,
119
- pascalCase,
120
- kebabCase,
121
- slugify,
122
- capitalize,
123
- uncapitalize,
124
- capitalizeFirstLetter,
125
- } from '@pivanov/utils/string';
126
-
127
- // Case transformations
128
- camelCase('foo-bar'); // 'fooBar'
129
- camelCase('FOO_BAR'); // 'fooBar'
130
-
131
- pascalCase('foo-bar'); // 'FooBar'
132
- pascalCase('foo_bar_baz'); // 'FooBarBaz'
133
-
134
- kebabCase('fooBar'); // 'foo-bar'
135
- kebabCase('XMLHttpRequest'); // 'xml-http-request'
136
-
137
- slugify('Hello World!'); // 'hello-world'
138
- slugify('รœber Cafรฉ'); // 'uber-cafe'
139
-
140
- // Capitalization with TypeScript support
141
- capitalize('hello'); // 'Hello' (type: Capitalize<'hello'>)
142
- uncapitalize('Hello'); // 'hello' (type: Uncapitalize<'Hello'>)
143
- capitalizeFirstLetter('hello world'); // 'Hello world'
42
+ ```ts
43
+ import { camelCase, snakeCase, slugify } from '@pivanov/utils/string';
44
+ import { pick, groupBy, deepMerge } from '@pivanov/utils/object';
45
+ import { isString, isNil, isDefined } from '@pivanov/utils/assertion';
46
+ import { sleep, timeout, retry, parallelLimit } from '@pivanov/utils/promise';
47
+ import { deepClone, isEqual, busDispatch, useEventBus } from '@pivanov/utils/tools';
144
48
  ```
145
49
 
146
- **When to use each:**
50
+ ## What's inside
147
51
 
148
- - `camelCase`: JavaScript/TypeScript variables and properties
149
- - `pascalCase`: Class names, React components, TypeScript types
150
- - `kebabCase`: CSS classes, HTML attributes, file names
151
- - `slugify`: URL slugs (more aggressive than kebabCase)
52
+ | Module | Surface |
53
+ |---|---|
54
+ | `assertion` | `isString`, `isNumber`, `isBoolean`, `isFunction`, `isObject`, `isRecord`, `isNull`, `isUndefined`, `isNil`, `isDefined`, `isArray`, `isDate`, `isRegExp`, `isError`, `isPromise`, `isMap`, `isSet`, `isPrimitive`, `isEmpty` |
55
+ | `object` | `pick`, `omit`, `pickBy`, `omitBy`, `merge`, `deepMerge`, `mapValues`, `mapKeys`, `groupBy`, `invert`, `hasOwn`, `keysOf`, `entriesOf`, `fromEntries` |
56
+ | `promise` | `sleep`, `timeout`, `retry`, `defer`, `parallelLimit` - all AbortSignal-aware where relevant |
57
+ | `string` | `camelCase`, `pascalCase`, `kebabCase`, `snakeCase`, `titleCase`, `slugify`, `capitalize`, `uncapitalize`, `capitalizeFirstLetter`, `truncate`, `escapeHtml`, `escapeRegExp`, `words`, `lines` |
58
+ | `tools/deepClone` | Rich deep clone (prototypes, getters/setters, symbols, Buffers, TypedArrays, circular refs) |
59
+ | `tools/isEqual` | Deep equality with cycle detection; compares RegExp, Error, TypedArrays, ArrayBuffer |
60
+ | `tools/dom` | `isBrowser`, `checkVisibility`, `isInViewport`, `setStyleProperties`, `calculateRenderedTextWidth` |
61
+ | `cache` | Cache Storage helpers: JSON metadata with optional TTL, plus raw `Request`/`Response` caching. Shipped as a standalone `@pivanov/utils/cache` entry point |
62
+ | `tools/eventBus` | `busDispatch`, `busSubscribe`, `busOnce`, `useEventBus` - typed topics, optional error handler |
63
+ | `types` | `TDict`, `TObjType`, `DeepPartial`, `DeepReadonly`, `Mutable`, `Prettify` |
152
64
 
153
- ### Promise Utilities
65
+ See the full **[API documentation](https://pivanov.github.io/pivanov-utils/)**.
154
66
 
155
- Simple async utilities for better promise handling.
67
+ ## Tree shaking
156
68
 
157
- ```typescript
158
- import { sleep } from '@pivanov/utils/promise';
69
+ Subpath imports give the smallest bundles:
159
70
 
160
- async function example() {
161
- console.log('Start');
162
- await sleep(1000); // Wait 1 second
163
- console.log('Done');
164
- }
71
+ ```ts
72
+ import { camelCase } from '@pivanov/utils/string';
73
+ import { deepClone } from '@pivanov/utils/tools';
165
74
  ```
166
75
 
167
- ### Deep Clone & Equality
168
-
169
- High-performance deep cloning and comparison with circular reference handling.
170
-
171
- ```typescript
172
- import { deepClone, isEqual } from '@pivanov/utils/tools';
173
-
174
- // Deep clone complex structures
175
- const original = {
176
- nested: { array: [1, 2, { value: 3 }] },
177
- date: new Date(),
178
- map: new Map([['key', 'value']]),
179
- set: new Set([1, 2, 3]),
180
- circular: {} as any,
181
- };
182
- original.circular = original; // Circular reference
76
+ Top-level imports tree-shake fine in modern bundlers:
183
77
 
184
- const cloned = deepClone(original);
185
- // Fully independent copy with circular references preserved
186
-
187
- // Deep equality comparison
188
- isEqual({ a: [1, 2, 3] }, { a: [1, 2, 3] }); // true
189
- isEqual(new Date('2024-01-01'), new Date('2024-01-01')); // true
190
- isEqual([1, 2, [3, 4]], [1, 2, [3, 4]]); // true
191
- isEqual(new Set([1, 2]), new Set([2, 1])); // false (Sets maintain order)
78
+ ```ts
79
+ import { camelCase, deepClone } from '@pivanov/utils';
192
80
  ```
193
81
 
194
- **Supported types:**
195
-
196
- - Primitives (string, number, boolean, null, undefined, symbol, bigint)
197
- - Arrays (including sparse arrays)
198
- - Plain objects (with getters/setters)
199
- - Date, RegExp, Map, Set
200
- - TypedArrays, ArrayBuffer, Buffer
201
- - Circular references
82
+ ## A few highlights
202
83
 
203
- ### Event Bus
84
+ ### Async that cancels
204
85
 
205
- Type-safe, lightweight event bus with React hooks support.
86
+ ```ts
87
+ import { sleep, timeout, retry } from '@pivanov/utils/promise';
206
88
 
207
- ```typescript
208
- import { busDispatch, busSubscribe, useEventBus } from '@pivanov/utils/tools';
89
+ const ctrl = new AbortController();
90
+ await sleep(1000, ctrl.signal); // cancellable
91
+ await timeout(fetch('/slow'), 3000); // race with timer
92
+ await retry(() => fetch('/api'), { attempts: 5, backoff: (n) => 100 * 2 ** n });
93
+ ```
209
94
 
210
- // Define your application events (optional but recommended)
211
- type AppEvents = {
212
- 'user-logged-in': { id: number; name: string };
213
- 'data-updated': { timestamp: number };
214
- 'notification': { message: string; type: 'info' | 'error' };
215
- };
95
+ ### Typed event bus
216
96
 
217
- // React hook usage (auto-unsubscribes on unmount)
218
- function UserProfile() {
219
- useEventBus<AppEvents>('user-logged-in', (data) => {
220
- console.log(`Welcome ${data.name}!`);
221
- });
97
+ ```ts
98
+ import { busDispatch, useEventBus, type IEventBus } from '@pivanov/utils/tools';
222
99
 
223
- return <div>Profile</div>;
100
+ interface UserLoggedIn extends IEventBus<{ id: number; name: string }> {
101
+ topic: 'user:logged-in';
224
102
  }
225
103
 
226
- // Vanilla JavaScript usage
227
- const unsubscribe = busSubscribe<AppEvents>('data-updated', (data) => {
228
- console.log('Updated at:', data.timestamp);
229
- });
230
-
231
- // Later: cleanup
232
- unsubscribe();
233
-
234
- // Dispatch events anywhere in your app
235
- busDispatch<AppEvents>('user-logged-in', { id: 1, name: 'John' });
236
- busDispatch<AppEvents>('notification', {
237
- message: 'Settings saved',
238
- type: 'info',
239
- });
104
+ useEventBus<UserLoggedIn>('user:logged-in', (user) => console.log(user.name));
105
+ busDispatch<UserLoggedIn>('user:logged-in', { id: 1, name: 'Ada' });
240
106
  ```
241
107
 
242
- **Features:**
243
-
244
- - Fully type-safe with TypeScript
245
- - Works across React and vanilla JavaScript
246
- - Automatic cleanup with React hook
247
- - Uses hashed topic names to avoid collisions
248
- - Zero dependencies
108
+ See the [Typed Events guide](https://pivanov.github.io/pivanov-utils/guides/typed-events) for an event-map pattern at scale.
249
109
 
250
- ### Browser Cache API
110
+ ### Cache with TTL
251
111
 
252
- Type-safe wrapper around the browser Cache API with expiration support.
253
-
254
- ```typescript
112
+ ```ts
255
113
  import {
256
- storageSetItem,
257
- storageGetItem,
258
- storageRemoveItem,
259
- storageExists,
260
- storageClear,
261
- storageClearByPrefixOrSuffix,
262
- storageGetAllKeys,
263
- storageCalculateSize,
264
- } from '@pivanov/utils/tools';
265
-
266
- const CACHE_NAME = 'my-app-cache';
267
-
268
- // Store data
269
- await storageSetItem(CACHE_NAME, 'user-data', {
270
- id: 1,
271
- name: 'John',
272
- bigNumber: BigInt(9007199254740991), // BigInt support!
273
- });
274
-
275
- // Retrieve data
276
- const userData = await storageGetItem<{ id: number; name: string }>(
277
- CACHE_NAME,
278
- 'user-data'
279
- );
280
-
281
- // Check existence
282
- const exists = await storageExists(CACHE_NAME, 'user-data');
283
-
284
- // Remove specific item
285
- await storageRemoveItem(CACHE_NAME, 'user-data');
286
-
287
- // Get all keys
288
- const keys = await storageGetAllKeys(CACHE_NAME);
289
-
290
- // Clear items by prefix
291
- await storageClearByPrefixOrSuffix(CACHE_NAME, 'temp-', true);
292
-
293
- // Clear items by suffix
294
- await storageClearByPrefixOrSuffix(CACHE_NAME, '-cache', false);
114
+ storageSetItemWithTTL,
115
+ storageGetItemWithTTL,
116
+ } from '@pivanov/utils/cache';
295
117
 
296
- // Calculate cache size (in bytes)
297
- const totalSize = await storageCalculateSize(CACHE_NAME);
298
- const itemSize = await storageCalculateSize(CACHE_NAME, 'user-data');
118
+ await storageSetItemWithTTL('app', 'token', 'abc123', 10 * 60 * 1000);
299
119
 
300
- // Clear all cache
301
- await storageClear(CACHE_NAME);
120
+ const token = await storageGetItemWithTTL<string>('app', 'token');
121
+ // null if missing or expired; expired entries are deleted on read
302
122
  ```
303
123
 
304
- **Features:**
124
+ ### Raw response caching in a service worker
305
125
 
306
- - Automatic JSON serialization/deserialization
307
- - BigInt support (automatically converted to strings)
308
- - Type-safe with generics
309
- - Works with absolute URLs as keys
310
- - Size calculation utilities
126
+ `@pivanov/utils/cache` is built as its own bundle with no React, no event bus and
127
+ no DOM helpers, so a service worker can import it without dragging the rest of
128
+ the package in. Alongside the JSON helpers it exposes a raw layer that stores a
129
+ `Response` byte for byte, preserving body, status and headers.
311
130
 
312
- ### DOM Utilities
313
-
314
- Browser-safe DOM manipulation helpers.
315
-
316
- ```typescript
131
+ ```ts
317
132
  import {
318
- isBrowser,
319
- checkVisibility,
320
- setStyleProperties,
321
- calculateRenderedTextWidth,
322
- } from '@pivanov/utils/tools';
323
-
324
- // Check if running in browser (SSR-safe)
325
- if (isBrowser()) {
326
- // Browser-only code
327
- window.addEventListener('scroll', handleScroll);
328
- }
329
-
330
- // Check element visibility in viewport
331
- const element = document.querySelector('.my-element') as HTMLElement;
332
- if (checkVisibility(element)) {
333
- element.classList.add('visible');
334
- }
335
-
336
- // Set CSS custom properties
337
- setStyleProperties(element, {
338
- '--primary-color': '#3b82f6',
339
- '--spacing': '1rem',
340
- '--border-radius': '8px',
133
+ cacheDelete,
134
+ cacheMatchResponse,
135
+ cacheNames,
136
+ cachePutResponse,
137
+ } from '@pivanov/utils/cache';
138
+
139
+ const CACHE = 'assets-v3';
140
+
141
+ self.addEventListener('fetch', (event: FetchEvent) => {
142
+ event.respondWith((async () => {
143
+ const hit = await cacheMatchResponse(CACHE, event.request);
144
+ if (hit) {
145
+ return hit;
146
+ }
147
+
148
+ const response = await fetch(event.request);
149
+ await cachePutResponse(CACHE, event.request, response); // caches a clone
150
+ return response; // still readable
151
+ })());
341
152
  });
342
153
 
343
- // Calculate text width for dynamic layouts
344
- const width = calculateRenderedTextWidth('Hello World', 16);
345
- const widthUppercase = calculateRenderedTextWidth('Hello World', 16, true);
346
- const widthCustomFont = calculateRenderedTextWidth(
347
- 'Hello World',
348
- 16,
349
- false,
350
- 'Arial'
351
- );
154
+ // Revision cleanup
155
+ const outdated = (await cacheNames()).filter((name) => name !== CACHE);
156
+ await Promise.all(outdated.map(cacheDelete));
352
157
  ```
353
158
 
354
- ## Tree Shaking
159
+ ### Deep clone that actually preserves shape
355
160
 
356
- Import only what you need to minimize bundle size:
357
-
358
- ```typescript
359
- // โœ… Good: Import specific utilities
360
- import { camelCase } from '@pivanov/utils/string';
161
+ ```ts
361
162
  import { deepClone } from '@pivanov/utils/tools';
362
163
 
363
- // โŒ Avoid: Importing everything
364
- import * as utils from '@pivanov/utils';
365
- ```
366
-
367
- ## TypeScript Support
164
+ class User {
165
+ constructor(public name: string) {}
166
+ greet() { return `hi ${this.name}`; }
167
+ }
368
168
 
369
- All utilities provide excellent type inference:
169
+ const clone = deepClone(new User('Ada'));
170
+ clone instanceof User; // true
171
+ clone.greet(); // 'hi Ada'
172
+ ```
370
173
 
371
- ```typescript
372
- import { pick } from '@pivanov/utils/object';
373
- import { capitalize } from '@pivanov/utils/string';
174
+ ## Compatibility
374
175
 
375
- // TypeScript infers exact types
376
- const user = { name: 'John', age: 30, email: 'john@example.com' } as const;
377
- const picked = pick(user, ['name', 'email']);
378
- // Type: { name: "John"; email: "john@example.com" }
176
+ - Modern browsers (ES2022)
177
+ - Bun, Node 18+ (ESM or CJS)
178
+ - Cache API requires browser support (Chrome 40+, Firefox 41+, Safari 11.1+)
179
+ - React hook requires React 18+
379
180
 
380
- const str = 'hello' as const;
381
- const capitalized = capitalize(str);
382
- // Type: "Hello"
383
- ```
181
+ ## Development
384
182
 
385
- ## Module Overview
386
-
387
- ```text
388
- @pivanov/utils
389
- โ”œโ”€โ”€ /assertion - Type guards (isString, isNumber, etc.)
390
- โ”œโ”€โ”€ /object - Object utilities (pick, omit, merge, deepMerge)
391
- โ”œโ”€โ”€ /promise - Async utilities (sleep)
392
- โ”œโ”€โ”€ /string - String utilities (camelCase, kebabCase, etc.)
393
- โ””โ”€โ”€ /tools - Various tools
394
- โ”œโ”€โ”€ deepClone
395
- โ”œโ”€โ”€ isEqual
396
- โ”œโ”€โ”€ DOM utilities (isBrowser, checkVisibility, etc.)
397
- โ”œโ”€โ”€ eventBus (busDispatch, busSubscribe, useEventBus)
398
- โ””โ”€โ”€ Cache API (storageSetItem, storageGetItem, etc.)
183
+ ```bash
184
+ bun install
185
+ bun test # run tests
186
+ bun run test:coverage # with coverage (lcov)
187
+ bun run typecheck
188
+ bun run lint
189
+ bun run build # ESM + CJS + .d.ts
190
+ bun run docs:dev # run VitePress docs site locally
399
191
  ```
400
192
 
401
- ## Browser Compatibility
193
+ ## Sponsors
402
194
 
403
- - Modern browsers with ES2015+ support
404
- - Cache API requires browser support (Chrome 40+, Firefox 41+, Safari 11.1+)
405
- - SSR-safe with browser environment detection
195
+ Supported by [LogicStar AI](https://logicstar.ai/)
406
196
 
407
197
  ## License
408
198
 
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * @pivanov/utils v1.0.1
3
+ * (c) 2024-present Pavel Ivanov
4
+ * Released under the MIT License.
5
+ * https://github.com/pivanov/utils
6
+ */
7
+ var{defineProperty:G,getOwnPropertyNames:I,getOwnPropertyDescriptor:J}=Object,K=Object.prototype.hasOwnProperty;function L(q){return this[q]}var N=(q)=>{var B=(H??=new WeakMap).get(q),D;if(B)return B;if(B=G({},"__esModule",{value:!0}),q&&typeof q==="object"||typeof q==="function"){for(var F of I(q))if(!K.call(B,F))G(B,F,{get:L.bind(q,F),enumerable:!(D=J(q,F))||D.enumerable})}return H.set(q,B),B},H;var O=(q)=>q;function Q(q,B){this[q]=O.bind(null,B)}var R=(q,B)=>{for(var D in B)G(q,D,{get:B[D],enumerable:!0,configurable:!0,set:Q.bind(B,D)})};var M={};R(M,{isUndefined:()=>Z,isString:()=>V,isSet:()=>d,isRegExp:()=>A,isRecord:()=>Y,isPromise:()=>P,isPrimitive:()=>f,isObject:()=>X,isNumber:()=>U,isNull:()=>_,isNil:()=>$,isMap:()=>b,isFunction:()=>W,isError:()=>C,isEmpty:()=>k,isDefined:()=>j,isDate:()=>z,isBoolean:()=>T,isArray:()=>x});module.exports=N(M);var T=(q)=>{return typeof q==="boolean"},U=(q)=>{return typeof q==="number"},V=(q)=>{return typeof q==="string"},W=(q)=>{return typeof q==="function"},X=(q)=>{if(q===null||typeof q!=="object")return!1;let B=Object.getPrototypeOf(q);return B===Object.prototype||B===null},Y=(q)=>{return typeof q==="object"&&q!==null},Z=(q)=>{return q===void 0},_=(q)=>{return q===null},$=(q)=>{return q===null||q===void 0},j=(q)=>{return q!==void 0},x=(q)=>{return Array.isArray(q)},z=(q)=>{return q instanceof Date},A=(q)=>{return q instanceof RegExp},C=(q)=>{return q instanceof Error},P=(q)=>{return q!==null&&(typeof q==="object"||typeof q==="function")&&typeof q.then==="function"},b=(q)=>{return q instanceof Map},d=(q)=>{return q instanceof Set},f=(q)=>{if(q===null||q===void 0)return!0;let B=typeof q;return B==="string"||B==="number"||B==="boolean"||B==="bigint"||B==="symbol"},k=(q)=>{if(q===null||q===void 0)return!0;if(typeof q==="string"||Array.isArray(q))return q.length===0;if(q instanceof Map||q instanceof Set)return q.size===0;if(typeof q==="object")return Object.keys(q).length===0;return!1};
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * @pivanov/utils v1.0.1
3
+ * (c) 2024-present Pavel Ivanov
4
+ * Released under the MIT License.
5
+ * https://github.com/pivanov/utils
6
+ */
7
+ var{defineProperty:Q,getOwnPropertyNames:G,getOwnPropertyDescriptor:M}=Object,S=Object.prototype.hasOwnProperty;function U(b){return this[b]}var $=(b)=>{var d=(Y??=new WeakMap).get(b),w;if(d)return d;if(d=Q({},"__esModule",{value:!0}),b&&typeof b==="object"||typeof b==="function"){for(var R of G(b))if(!S.call(d,R))Q(d,R,{get:U.bind(b,R),enumerable:!(w=M(b,R))||w.enumerable})}return Y.set(b,d),d},Y;var L=(b)=>b;function B(b,d){this[b]=L.bind(null,d)}var I=(b,d)=>{for(var w in d)Q(b,w,{get:d[w],enumerable:!0,configurable:!0,set:B.bind(d,w)})};var N={};I(N,{stringifyBigIntValues:()=>Z,storageSetItemWithTTL:()=>p,storageSetItem:()=>_,storageRemoveItem:()=>E,storageGetItemWithTTL:()=>q,storageGetItem:()=>D,storageGetAllKeys:()=>u,storageExists:()=>l,storageClearBySuffix:()=>k,storageClearByPrefixOrSuffix:()=>X,storageClearByPrefix:()=>C,storageClear:()=>o,storageCalculateSize:()=>r,isCacheStorageSupported:()=>W,cacheResponseKeys:()=>z,cachePutResponse:()=>V,cacheNames:()=>v,cacheMatchResponse:()=>f,cacheHasResponse:()=>x,cacheExists:()=>K,cacheDeleteResponse:()=>g,cacheDelete:()=>i,cacheClear:()=>P});module.exports=$(N);var W=()=>{return typeof caches<"u"};var O=()=>{if(!W())throw Error("[@pivanov/utils/cache] Cache Storage is not available in this environment.");return caches},A=(b)=>{return O().open(b)},F=(b)=>{if(b.startsWith("http://")||b.startsWith("https://"))return b;return`https://cache.internal/${b}`};var V=async(b,d,w)=>{if(w.bodyUsed)throw TypeError("[@pivanov/utils/cache] cachePutResponse received a Response whose body has already been consumed.");await(await A(b)).put(d,w.clone())},f=async(b,d,w)=>{return(await A(b)).match(d,w)},g=async(b,d,w)=>{return(await A(b)).delete(d,w)},x=async(b,d,w)=>{return await(await A(b)).match(d,w)!==void 0},z=async(b)=>{return(await(await A(b)).keys()).map((R)=>R.url)},P=async(b)=>{let d=await A(b),w=await d.keys();await Promise.all(w.map((R)=>d.delete(R)))},v=async()=>{return O().keys()},K=async(b)=>{return O().has(b)},i=async(b)=>{return O().delete(b)};var Z=(b,d)=>{return typeof d==="bigint"?d.toString():d},_=async(b,d,w)=>{let R=await A(b),T=JSON.stringify(w,Z),J=new Response(T,{headers:{"Content-Type":"application/json"}}),j=F(d);await R.put(new Request(j),J)},D=async(b,d)=>{let w=await A(b),R=F(d),T=await w.match(new Request(R));if(!T)return null;let J=await T.text();return JSON.parse(J)},n=(b)=>{return typeof b==="object"&&b!==null&&b.__ttl===!0&&typeof b.exp==="number"},p=async(b,d,w,R)=>{let T={__ttl:!0,v:w,exp:Date.now()+R};await _(b,d,T)},q=async(b,d)=>{let w=await D(b,d);if(!n(w))return null;if(Date.now()>=w.exp)return await E(b,d),null;return w.v},E=async(b,d)=>{let w=await A(b),R=F(d);return w.delete(new Request(R))},o=async(b)=>{let d=await A(b),w=await d.keys();for(let R of w)await d.delete(R)},X=async(b,d,w=!0)=>{let R=await A(b),T=await R.keys();for(let J of T){let j=J.url.split("/"),H=j[j.length-1]||"";if(w&&H.startsWith(d)||!w&&H.endsWith(d))await R.delete(J)}},C=async(b,d)=>{await X(b,d,!0)},k=async(b,d)=>{await X(b,d,!1)},l=async(b,d)=>{let w=await A(b),R=F(d);return await w.match(new Request(R))!==void 0},u=async(b)=>{return(await(await A(b)).keys()).map((R)=>{let T=R.url.split("/");return T[T.length-1]||""})},r=async(b,d)=>{let w=await A(b);if(d){let J=F(d),j=await w.match(new Request(J));if(j)return(await j.clone().arrayBuffer()).byteLength;return 0}let R=await w.keys(),T=0;for(let J of R){let j=await w.match(J);if(j){let H=await j.clone().arrayBuffer();T+=H.byteLength}}return T};
package/dist/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @pivanov/utils v0.0.3
2
+ * @pivanov/utils v1.0.1
3
3
  * (c) 2024-present Pavel Ivanov
4
4
  * Released under the MIT License.
5
5
  * https://github.com/pivanov/utils
6
6
  */
7
- "use strict";var e=require("react");const t=e=>{if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t},r=(e,...n)=>{if(!n.length)return e;const s=n.shift();if(void 0===s)return e;if(t(e)&&t(s))for(const n of Object.keys(s)){const o=s[n];if(t(o)){e[n]||Object.assign(e,{[n]:{}});const s=e[n];t(s)&&r(s,o)}else Object.assign(e,{[n]:o})}return r(e,...n)},n=(e,t)=>"bigint"==typeof t?t.toString():t,s=e=>e.startsWith("http://")||e.startsWith("https://")?e:`https://cache.internal/${e}`,o=e=>`${((e,t="๐Ÿš€")=>{let r=0;const n=e+t;for(let e=0;e<n.length;e++)r=(r<<5)-r+n.charCodeAt(e),r|=0;return(r>>>0).toString(16)})(e)}::${e}`,i=(e,t)=>{if(!e||"function"!=typeof t)return()=>{};const r=(e=>t=>{if(t instanceof CustomEvent)try{e(t.detail)}catch(e){console.error("Event listener error:",e)}})(t),n=o(e);return window.addEventListener(n,r),()=>window.removeEventListener(n,r)},a=(e,t)=>{if(null===e||"object"!=typeof e)return e;const r=t.get(e);if(r)return r;if(Array.isArray(e)){const r=e.length;if(r<32){const n=new Array(r);t.set(e,n);for(let s=0;s<r;s++)s in e&&(n[s]=a(e[s],t));return n}const n=new Array(r);if(t.set(e,n),Object.keys(e).length===r){let s=0;const o=r-r%8;for(;s<o;)n[s]=a(e[s],t),n[s+1]=a(e[s+1],t),n[s+2]=a(e[s+2],t),n[s+3]=a(e[s+3],t),n[s+4]=a(e[s+4],t),n[s+5]=a(e[s+5],t),n[s+6]=a(e[s+6],t),n[s+7]=a(e[s+7],t),s+=8;for(;s<r;)n[s]=a(e[s],t),s++}else for(let s=0;s<r;s++)s in e&&(n[s]=a(e[s],t));return n}if(e instanceof Date||e instanceof RegExp){return new(0,e.constructor)(e)}if("undefined"!=typeof Buffer)try{if(Buffer.isBuffer(e))return Buffer.from(e)}catch(e){if(e instanceof Error)throw e}if(ArrayBuffer.isView(e)){const t=e.constructor;if("buffer"in e){const r=e;return new t(r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength))}return new t(new ArrayBuffer(e.byteLength))}if(e instanceof ArrayBuffer)return e.slice(0);if(e&&"object"==typeof e&&"buffer"in e&&"byteLength"in e){const t=e;if("undefined"==typeof Buffer){const e=new Uint8Array(t.buffer,t.byteOffset||0,t.byteLength),r=new Uint8Array(e.length);return r.set(e),r}return{buffer:t.buffer instanceof ArrayBuffer?t.buffer.slice(0):new ArrayBuffer(t.byteLength),byteLength:t.byteLength,byteOffset:t.byteOffset||0,length:t.length||t.byteLength,BYTES_PER_ELEMENT:t.BYTES_PER_ELEMENT||1}}if(e instanceof Set){const r=new Set;t.set(e,r);const n=Array.from(e),s=n.length;for(let e=0;e<s;e++)r.add(a(n[e],t));return r}if(e instanceof Map){const r=new Map;t.set(e,r);const n=Array.from(e),s=n.length;for(let e=0;e<s;e++){const[s,o]=n[e];r.set(s,a(o,t))}return r}if(Object.getPrototypeOf(e)===Object.prototype){const r={};t.set(e,r);const n=Object.getOwnPropertySymbols(e),s=n.length;if(s>0)for(let t=0;t<s;t++){const s=n[t],o=Object.getOwnPropertyDescriptor(e,s);o?.enumerable&&Object.defineProperty(r,s,o)}const o=Object.getOwnPropertyDescriptors(e),i=Object.keys(e),c=i.length;let f=!1;for(let e=0;e<c;e++){const t=o[i[e]];if(t.get||t.set){f=!0;break}}if(f)for(let e=0;e<c;e++){const n=i[e],s=o[n];if(s.enumerable)if(s.get||s.set)Object.defineProperty(r,n,s);else{const e=a(s.value,t);Object.defineProperty(r,n,{...s,value:e})}}else{let n=0;const s=c-c%8;for(;n<s;){const s=i[n],o=i[n+1],c=i[n+2],f=i[n+3],l=i[n+4],p=i[n+5],u=i[n+6],y=i[n+7],g=e;r[s]=a(g[s],t),r[o]=a(g[o],t),r[c]=a(g[c],t),r[f]=a(g[f],t),r[l]=a(g[l],t),r[p]=a(g[p],t),r[u]=a(g[u],t),r[y]=a(g[y],t),n+=8}for(;n<c;){const s=i[n++];r[s]=a(e[s],t)}}return r}const n=Object.getPrototypeOf(e),s=Object.create(n);t.set(e,s);const o=Object.getOwnPropertyDescriptors(e),i=Object.keys(o),c=i.length;for(let e=0;e<c;e++){const r=i[e],n=o[r];if(n.enumerable)if(n.get||n.set)Object.defineProperty(s,r,n);else{const e=a(n.value,t);Object.defineProperty(s,r,{...n,value:e})}}const f=Object.getOwnPropertySymbols(e),l=f.length;for(let t=0;t<l;t++){const r=f[t],n=Object.getOwnPropertyDescriptor(e,r);n?.enumerable&&Object.defineProperty(s,r,n)}return s},c=(e,t)=>{if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(!c(e[r],t[r]))return!1;return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(const r of e)if(!t.has(r))return!1;return!0}if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(const[r,n]of e)if(!t.has(r)||!c(n,t.get(r)))return!1;return!0}if(null!==e&&"object"==typeof e&&!Array.isArray(e)&&null!==t&&"object"==typeof t&&!Array.isArray(t)){const r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(const n of r)if(!c(e[n],t[n]))return!1;return!0}return e===t};exports.busDispatch=(e,t)=>{if(!e)return;const r=o(e);window.dispatchEvent(new CustomEvent(r,{detail:t,bubbles:!0,cancelable:!1}))},exports.busSubscribe=i,exports.calculateRenderedTextWidth=(e,t,r=!1,n='Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"')=>{const s=r?e.toUpperCase():e,o=document.createElement("canvas").getContext("2d");return o.font=`${t}px ${n}`,o.measureText(s).width},exports.camelCase=e=>e.trim().toLowerCase().replace(/^[-_\s]+/,"").replace(/[-_\s]+(.)?/g,((e,t)=>t?t.toUpperCase():"")),exports.capitalize=e=>e.charAt(0).toUpperCase()+e.slice(1),exports.capitalizeFirstLetter=e=>e.charAt(0).toUpperCase()+e.slice(1),exports.checkVisibility=e=>{const t=e.getBoundingClientRect(),r=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(t.bottom<0||t.top-r>=0)},exports.deepClone=e=>a(e,new WeakMap),exports.deepMerge=r,exports.isBoolean=e=>"boolean"==typeof e,exports.isBrowser=()=>"undefined"!=typeof window,exports.isEqual=c,exports.isFunction=e=>"function"==typeof e,exports.isNull=e=>null===e,exports.isNumber=e=>"number"==typeof e,exports.isObject=t,exports.isString=e=>"string"==typeof e,exports.isUndefined=e=>void 0===e,exports.kebabCase=e=>null==e?e:e.normalize("NFKD").replace(/\p{Diacritic}/gu,"").replace(/[^\w\s-]/g," ").replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/[-_\s]+/g,"-").toLowerCase().replace(/^-+|-+$/g,"").trim(),exports.merge=(e,...t)=>Object.assign(e,...t),exports.omit=(e,t)=>t.reduce(((e,t)=>(delete e[t],e)),{...e}),exports.pascalCase=e=>e.split(/(?=[0-9])|(?<=[0-9])|[^a-zA-Z0-9]+/g).filter(Boolean).map((e=>e.toLowerCase())).map((e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`)).join(""),exports.pick=(e,t)=>{const r={};for(const n of t)n in e&&(r[n]=e[n]);return r},exports.setStyleProperties=(e,t)=>{if(e)for(const[r,n]of Object.entries(t))e.style.setProperty(r,n)},exports.sleep=e=>new Promise((t=>{setTimeout(t,e)})),exports.slugify=e=>e.normalize("NFKD").toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[-\s_]+/g,"-").replace(/^-+|-+$/g,""),exports.storageCalculateSize=async(e,t)=>{const r=await caches.open(e);if(t){const e=s(t),n=await r.match(new Request(e));if(n){const e=n.clone();return(await e.arrayBuffer()).byteLength}return 0}const n=await r.keys();let o=0;for(const e of n){const t=await r.match(e);if(t){const e=t.clone();o+=(await e.arrayBuffer()).byteLength}}return o},exports.storageClear=async e=>{const t=await caches.open(e),r=await t.keys();for(const e of r)await t.delete(e)},exports.storageClearByPrefixOrSuffix=async(e,t,r=!0)=>{const n=await caches.open(e),s=await n.keys();for(const e of s){const s=e.url.split("/"),o=s[s.length-1]||"";(r&&o.startsWith(t)||!r&&o.endsWith(t))&&await n.delete(e)}},exports.storageExists=async(e,t)=>{const r=await caches.open(e),n=s(t);return void 0!==await r.match(new Request(n))},exports.storageGetAllKeys=async e=>{const t=await caches.open(e);return(await t.keys()).map((e=>{const t=e.url.split("/");return t[t.length-1]||""}))},exports.storageGetItem=async(e,t)=>{const r=await caches.open(e),n=s(t),o=await r.match(new Request(n));if(!o)return null;const i=await o.text();return JSON.parse(i)},exports.storageRemoveItem=async(e,t)=>{const r=await caches.open(e),n=s(t);return await r.delete(new Request(n))},exports.storageSetItem=async(e,t,r)=>{const o=await caches.open(e),i=JSON.stringify(r,n),a=new Response(i,{headers:{"Content-Type":"application/json"}}),c=s(t);await o.put(new Request(c),a)},exports.stringifyBigIntValues=n,exports.uncapitalize=e=>e.charAt(0).toLowerCase()+e.slice(1),exports.useEventBus=(t,r,n=[])=>{e.useEffect((()=>{if(t&&"function"==typeof r)return i(t,r)}),[t,r,...n])};
7
+ var{defineProperty:g,getOwnPropertyNames:PQ,getOwnPropertyDescriptor:EQ}=Object,VQ=Object.prototype.hasOwnProperty;function qQ(J){return this[J]}var P=(J)=>{var Q=(s??=new WeakMap).get(J),X;if(Q)return Q;if(Q=g({},"__esModule",{value:!0}),J&&typeof J==="object"||typeof J==="function"){for(var Y of PQ(J))if(!VQ.call(Q,Y))g(Q,Y,{get:qQ.bind(J,Y),enumerable:!(X=EQ(J,Y))||X.enumerable})}return s.set(J,Q),Q},s;var LQ=(J)=>J;function DQ(J,Q){this[J]=LQ.bind(null,Q)}var V=(J,Q)=>{for(var X in Q)g(J,X,{get:Q[X],enumerable:!0,configurable:!0,set:DQ.bind(Q,X)})};var wQ={};V(wQ,{isUndefined:()=>XJ,isString:()=>e,isSet:()=>HJ,isRegExp:()=>FJ,isRecord:()=>QJ,isPromise:()=>WJ,isPrimitive:()=>OJ,isObject:()=>D,isNumber:()=>a,isNull:()=>YJ,isNil:()=>ZJ,isMap:()=>BJ,isFunction:()=>JJ,isError:()=>UJ,isEmpty:()=>IJ,isDefined:()=>GJ,isDate:()=>_J,isBoolean:()=>t,isArray:()=>$J});module.exports=P(wQ);var t=(J)=>{return typeof J==="boolean"},a=(J)=>{return typeof J==="number"},e=(J)=>{return typeof J==="string"},JJ=(J)=>{return typeof J==="function"},D=(J)=>{if(J===null||typeof J!=="object")return!1;let Q=Object.getPrototypeOf(J);return Q===Object.prototype||Q===null},QJ=(J)=>{return typeof J==="object"&&J!==null},XJ=(J)=>{return J===void 0},YJ=(J)=>{return J===null},ZJ=(J)=>{return J===null||J===void 0},GJ=(J)=>{return J!==void 0},$J=(J)=>{return Array.isArray(J)},_J=(J)=>{return J instanceof Date},FJ=(J)=>{return J instanceof RegExp},UJ=(J)=>{return J instanceof Error},WJ=(J)=>{return J!==null&&(typeof J==="object"||typeof J==="function")&&typeof J.then==="function"},BJ=(J)=>{return J instanceof Map},HJ=(J)=>{return J instanceof Set},OJ=(J)=>{if(J===null||J===void 0)return!0;let Q=typeof J;return Q==="string"||Q==="number"||Q==="boolean"||Q==="bigint"||Q==="symbol"},IJ=(J)=>{if(J===null||J===void 0)return!0;if(typeof J==="string"||Array.isArray(J))return J.length===0;if(J instanceof Map||J instanceof Set)return J.size===0;if(typeof J==="object")return Object.keys(J).length===0;return!1};var xQ={};V(xQ,{pickBy:()=>d,pick:()=>TJ,omitBy:()=>MJ,omit:()=>RJ,merge:()=>LJ,mapValues:()=>zJ,mapKeys:()=>AJ,keysOf:()=>EJ,invert:()=>KJ,hasOwn:()=>PJ,groupBy:()=>SJ,fromEntries:()=>qJ,entriesOf:()=>VJ,deepMerge:()=>v});module.exports=P(xQ);var RJ=(J,Q)=>{let X={...J};for(let Y of Q)delete X[Y];return X},TJ=(J,Q)=>{let X={};for(let Y of Q)if(Y in J)X[Y]=J[Y];return X},d=(J,Q)=>{let X={};for(let Y of Object.keys(J))if(Q(J[Y],Y))X[Y]=J[Y];return X},MJ=(J,Q)=>{return d(J,(X,Y)=>!Q(X,Y))},zJ=(J,Q)=>{let X={};for(let Y of Object.keys(J))X[Y]=Q(J[Y],Y);return X},AJ=(J,Q)=>{let X={};for(let Y of Object.keys(J))X[Q(J[Y],Y)]=J[Y];return X},SJ=(J,Q)=>{let X={};return J.forEach((Y,Z)=>{let _=Q(Y,Z);if(X[_])X[_].push(Y);else X[_]=[Y]}),X},KJ=(J)=>{let Q={};for(let X of Object.keys(J))Q[J[X]]=X;return Q},PJ=(J,Q)=>{return Object.hasOwn(J,Q)},EJ=(J)=>{return Object.keys(J)},VJ=(J)=>{return Object.entries(J)},qJ=(J)=>{return Object.fromEntries(J)},LJ=(J,...Q)=>{return Object.assign({},J,...Q)},v=(J,...Q)=>{let X={...J};for(let Y of Q){if(Y===void 0)continue;for(let Z of Object.keys(Y)){let _=Y[Z],F=X[Z];if(D(_))X[Z]=v(D(F)?F:{},_);else X[Z]=_}}return X};var NQ={};V(NQ,{timeout:()=>DJ,sleep:()=>k,retry:()=>wJ,parallelLimit:()=>NJ,defer:()=>xJ});module.exports=P(NQ);var k=(J,Q)=>{return new Promise((X,Y)=>{if(Q?.aborted){Y(Q.reason);return}let Z=setTimeout(()=>{Q?.removeEventListener("abort",_),X()},J),_=()=>{clearTimeout(Z),Y(Q?.reason)};Q?.addEventListener("abort",_,{once:!0})})},DJ=(J,Q,X)=>{return new Promise((Y,Z)=>{let _=setTimeout(()=>{Z(X??Error(`Operation timed out after ${Q}ms`))},Q);J.then((F)=>{clearTimeout(_),Y(F)},(F)=>{clearTimeout(_),Z(F)})})},wJ=async(J,Q={})=>{let{attempts:X=3,backoff:Y=0,signal:Z,shouldRetry:_}=Q,F;for(let H=1;H<=X;H++){if(Z?.aborted)throw Z.reason;try{return await J(H)}catch(A){if(F=A,H>=X)break;if(_&&!_(A,H))break;let E=typeof Y==="function"?Y(H):Y;if(E>0)await k(E,Z)}}throw F},xJ=()=>{let J,Q;return{promise:new Promise((Y,Z)=>{J=Y,Q=Z}),resolve:J,reject:Q}},NJ=async(J,Q,X)=>{if(Q<1)throw Error("parallelLimit: concurrency must be >= 1");let Y=Array(J.length),Z=0,_=async()=>{while(!0){let H=Z++;if(H>=J.length)return;Y[H]=await X(J[H],H)}},F=Array.from({length:Math.min(Q,J.length)},_);return await Promise.all(F),Y};var CQ={};V(CQ,{words:()=>bJ,uncapitalize:()=>yJ,truncate:()=>jJ,titleCase:()=>kJ,snakeCase:()=>vJ,slugify:()=>mJ,pascalCase:()=>CJ,lines:()=>lJ,kebabCase:()=>dJ,escapeRegExp:()=>uJ,escapeHtml:()=>pJ,capitalizeFirstLetter:()=>gJ,capitalize:()=>hJ,camelCase:()=>fJ});module.exports=P(CQ);var fJ=(J)=>{return J.trim().toLowerCase().replace(/^[-_\s]+/,"").replace(/[-_\s]+(.)?/g,(Q,X)=>X?X.toUpperCase():"")},CJ=(J)=>{return J.split(/(?=[0-9])|(?<=[0-9])|[^a-zA-Z0-9]+/g).filter(Boolean).map((Q)=>Q.toLowerCase()).map((Q)=>`${Q.charAt(0).toUpperCase()}${Q.slice(1)}`).join("")},gJ=(J)=>J.charAt(0).toUpperCase()+J.slice(1),dJ=(J)=>{if(J==null)return J;return J.normalize("NFKD").replace(/\p{Diacritic}/gu,"").replace(/[^\w\s-]/g," ").replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").replace(/[-_\s]+/g,"-").toLowerCase().replace(/^-+|-+$/g,"").trim()},vJ=(J)=>{if(J==null)return J;return J.normalize("NFKD").replace(/\p{Diacritic}/gu,"").replace(/[^\w\s-]/g," ").replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").replace(/[-_\s]+/g,"_").toLowerCase().replace(/^_+|_+$/g,"").trim()},kJ=(J)=>{return J.normalize("NFKD").replace(/[^\w\s-]/g," ").split(/[-_\s]+/).filter(Boolean).map((Q)=>Q.charAt(0).toUpperCase()+Q.slice(1).toLowerCase()).join(" ")},mJ=(J)=>{return J.normalize("NFKD").toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[-\s_]+/g,"-").replace(/^-+|-+$/g,"")},hJ=(J)=>{return J.charAt(0).toUpperCase()+J.slice(1)},yJ=(J)=>{return J.charAt(0).toLowerCase()+J.slice(1)},jJ=(J,Q,X="โ€ฆ")=>{if(J.length<=Q)return J;if(Q<=X.length)return X.slice(0,Q);return J.slice(0,Q-X.length)+X},fQ={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},pJ=(J)=>{return J.replace(/[&<>"']/g,(Q)=>fQ[Q])},uJ=(J)=>{return J.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")},bJ=(J)=>{return J.split(/[-_\s]+/).filter(Boolean)},lJ=(J)=>{return J.split(/\r\n|\r|\n/)};var mQ={};V(mQ,{useEventBus:()=>AQ,stringifyBigIntValues:()=>m,storageSetItemWithTTL:()=>JQ,storageSetItem:()=>h,storageRemoveItem:()=>j,storageGetItemWithTTL:()=>QQ,storageGetItem:()=>y,storageGetAllKeys:()=>$Q,storageExists:()=>GQ,storageClearBySuffix:()=>ZQ,storageClearByPrefixOrSuffix:()=>C,storageClearByPrefix:()=>YQ,storageClear:()=>XQ,storageCalculateSize:()=>_Q,setStyleProperties:()=>WQ,isInViewport:()=>OQ,isEqual:()=>KQ,isCacheStorageSupported:()=>f,isBrowser:()=>UQ,deepClone:()=>FQ,checkVisibility:()=>BQ,calculateRenderedTextWidth:()=>IQ,cacheResponseKeys:()=>rJ,cachePutResponse:()=>cJ,cacheNames:()=>tJ,cacheMatchResponse:()=>nJ,cacheHasResponse:()=>oJ,cacheExists:()=>aJ,cacheDeleteResponse:()=>iJ,cacheDelete:()=>eJ,cacheClear:()=>sJ,busSubscribe:()=>x,busOnce:()=>MQ,busDispatch:()=>TQ,__resetTextMeasurementCache:()=>HQ});module.exports=P(mQ);var f=()=>{return typeof caches<"u"};var w=()=>{if(!f())throw Error("[@pivanov/utils/cache] Cache Storage is not available in this environment.");return caches},R=(J)=>{return w().open(J)},q=(J)=>{if(J.startsWith("http://")||J.startsWith("https://"))return J;return`https://cache.internal/${J}`};var cJ=async(J,Q,X)=>{if(X.bodyUsed)throw TypeError("[@pivanov/utils/cache] cachePutResponse received a Response whose body has already been consumed.");await(await R(J)).put(Q,X.clone())},nJ=async(J,Q,X)=>{return(await R(J)).match(Q,X)},iJ=async(J,Q,X)=>{return(await R(J)).delete(Q,X)},oJ=async(J,Q,X)=>{return await(await R(J)).match(Q,X)!==void 0},rJ=async(J)=>{return(await(await R(J)).keys()).map((Y)=>Y.url)},sJ=async(J)=>{let Q=await R(J),X=await Q.keys();await Promise.all(X.map((Y)=>Q.delete(Y)))},tJ=async()=>{return w().keys()},aJ=async(J)=>{return w().has(J)},eJ=async(J)=>{return w().delete(J)};var m=(J,Q)=>{return typeof Q==="bigint"?Q.toString():Q},h=async(J,Q,X)=>{let Y=await R(J),Z=JSON.stringify(X,m),_=new Response(Z,{headers:{"Content-Type":"application/json"}}),F=q(Q);await Y.put(new Request(F),_)},y=async(J,Q)=>{let X=await R(J),Y=q(Q),Z=await X.match(new Request(Y));if(!Z)return null;let _=await Z.text();return JSON.parse(_)},gQ=(J)=>{return typeof J==="object"&&J!==null&&J.__ttl===!0&&typeof J.exp==="number"},JQ=async(J,Q,X,Y)=>{let Z={__ttl:!0,v:X,exp:Date.now()+Y};await h(J,Q,Z)},QQ=async(J,Q)=>{let X=await y(J,Q);if(!gQ(X))return null;if(Date.now()>=X.exp)return await j(J,Q),null;return X.v},j=async(J,Q)=>{let X=await R(J),Y=q(Q);return X.delete(new Request(Y))},XQ=async(J)=>{let Q=await R(J),X=await Q.keys();for(let Y of X)await Q.delete(Y)},C=async(J,Q,X=!0)=>{let Y=await R(J),Z=await Y.keys();for(let _ of Z){let F=_.url.split("/"),H=F[F.length-1]||"";if(X&&H.startsWith(Q)||!X&&H.endsWith(Q))await Y.delete(_)}},YQ=async(J,Q)=>{await C(J,Q,!0)},ZQ=async(J,Q)=>{await C(J,Q,!1)},GQ=async(J,Q)=>{let X=await R(J),Y=q(Q);return await X.match(new Request(Y))!==void 0},$Q=async(J)=>{return(await(await R(J)).keys()).map((Y)=>{let Z=Y.url.split("/");return Z[Z.length-1]||""})},_Q=async(J,Q)=>{let X=await R(J);if(Q){let _=q(Q),F=await X.match(new Request(_));if(F)return(await F.clone().arrayBuffer()).byteLength;return 0}let Y=await X.keys(),Z=0;for(let _ of Y){let F=await X.match(_);if(F){let H=await F.clone().arrayBuffer();Z+=H.byteLength}}return Z};var FQ=(J)=>{return B(J,new WeakMap)},B=(J,Q)=>{if(J===null||typeof J!=="object")return J;let X=Q.get(J);if(X)return X;if(Array.isArray(J)){let G=J.length;if(G<32){let $=Array(G);Q.set(J,$);for(let W=0;W<G;W++)if(W in J)$[W]=B(J[W],Q);return $}let U=Array(G);if(Q.set(J,U),Object.keys(J).length===G){let $=0,W=G%8,I=G-W;while($<I)U[$]=B(J[$],Q),U[$+1]=B(J[$+1],Q),U[$+2]=B(J[$+2],Q),U[$+3]=B(J[$+3],Q),U[$+4]=B(J[$+4],Q),U[$+5]=B(J[$+5],Q),U[$+6]=B(J[$+6],Q),U[$+7]=B(J[$+7],Q),$+=8;while($<G)U[$]=B(J[$],Q),$++}else for(let $=0;$<G;$++)if($ in J)U[$]=B(J[$],Q);return U}if(J instanceof Date||J instanceof RegExp)return new J.constructor(J);if(typeof Buffer<"u")try{if(Buffer.isBuffer(J))return Buffer.from(J)}catch(G){if(G instanceof Error)throw G}if(ArrayBuffer.isView(J)){let G=J.constructor;if("buffer"in J){let $=J,W=$.buffer.slice($.byteOffset,$.byteOffset+$.byteLength);return new G(W)}return new G(new ArrayBuffer(J.byteLength))}if(J instanceof ArrayBuffer)return J.slice(0);if(J&&typeof J==="object"&&"buffer"in J&&"byteLength"in J){let G=J;if(typeof Buffer>"u"){let $=new Uint8Array(G.buffer,G.byteOffset||0,G.byteLength),W=new Uint8Array($.length);return W.set($),W}return{buffer:G.buffer instanceof ArrayBuffer?G.buffer.slice(0):new ArrayBuffer(G.byteLength),byteLength:G.byteLength,byteOffset:G.byteOffset||0,length:G.length||G.byteLength,BYTES_PER_ELEMENT:G.BYTES_PER_ELEMENT||1}}if(J instanceof Set){let G=new Set;Q.set(J,G);let U=Array.from(J),$=U.length;for(let W=0;W<$;W++)G.add(B(U[W],Q));return G}if(J instanceof Map){let G=new Map;Q.set(J,G);let U=Array.from(J),$=U.length;for(let W=0;W<$;W++){let[I,S]=U[W];G.set(I,B(S,Q))}return G}if(Object.getPrototypeOf(J)===Object.prototype){let G={};Q.set(J,G);let U=Object.getOwnPropertySymbols(J),$=U.length;if($>0)for(let O=0;O<$;O++){let T=U[O],M=Object.getOwnPropertyDescriptor(J,T);if(M?.enumerable)Object.defineProperty(G,T,M)}let W=Object.getOwnPropertyDescriptors(J),I=Object.keys(J),S=I.length,u=!1;for(let O=0;O<S;O++){let T=W[I[O]];if(T.get||T.set){u=!0;break}}if(u)for(let O=0;O<S;O++){let T=I[O],M=W[T];if(M.enumerable)if(M.get||M.set)Object.defineProperty(G,T,M);else{let K=B(M.value,Q);Object.defineProperty(G,T,{...M,value:K})}}else{let O=0,T=S%8,M=S-T;while(O<M){let K=I[O],b=I[O+1],l=I[O+2],c=I[O+3],n=I[O+4],i=I[O+5],o=I[O+6],r=I[O+7],z=J;G[K]=B(z[K],Q),G[b]=B(z[b],Q),G[l]=B(z[l],Q),G[c]=B(z[c],Q),G[n]=B(z[n],Q),G[i]=B(z[i],Q),G[o]=B(z[o],Q),G[r]=B(z[r],Q),O+=8}while(O<S){let K=I[O++];G[K]=B(J[K],Q)}}return G}let Y=Object.getPrototypeOf(J),Z=Object.create(Y);Q.set(J,Z);let _=Object.getOwnPropertyDescriptors(J),F=Object.keys(_),H=F.length;for(let G=0;G<H;G++){let U=F[G],$=_[U];if(!$.enumerable)continue;if($.get||$.set)Object.defineProperty(Z,U,$);else{let W=B($.value,Q);Object.defineProperty(Z,U,{...$,value:W})}}let A=Object.getOwnPropertySymbols(J),E=A.length;for(let G=0;G<E;G++){let U=A[G],$=Object.getOwnPropertyDescriptor(J,U);if(!$?.enumerable)continue;Object.defineProperty(Z,U,$)}return Z};var UQ=()=>{return typeof window<"u"&&typeof document<"u"},WQ=(J,Q)=>{if(!J)return;for(let[X,Y]of Object.entries(Q))J.style.setProperty(X,Y)},BQ=(J,Q={})=>{let{checkViewport:X=!0,checkDisplay:Y=!0,checkVisibility:Z=!0,checkOpacity:_=!0}=Q;if(!J.isConnected)return!1;if(Y||Z||_){let F=window.getComputedStyle(J);if(Y&&F.display==="none")return!1;if(Z&&F.visibility!==""&&F.visibility!=="visible")return!1;if(_&&F.opacity!==""){let H=Number.parseFloat(F.opacity);if(!Number.isNaN(H)&&H===0)return!1}}if(X){let F=J.getBoundingClientRect();if(F.width!==0||F.height!==0){let H=window.innerHeight||document.documentElement.clientHeight,A=window.innerWidth||document.documentElement.clientWidth,E=F.bottom<=0||F.top>=H,G=F.right<=0||F.left>=A;if(E||G)return!1}}return!0};var p,L,dQ=()=>{if(L!==void 0)return L;if(typeof document>"u")return L=null,null;return p=document.createElement("canvas"),L=p.getContext("2d"),L},HQ=()=>{p=void 0,L=void 0},OQ=(J,Q={})=>{let{vertical:X=!0,horizontal:Y=!0}=Q,Z=J.getBoundingClientRect();if(Z.width===0&&Z.height===0)return!0;let _=window.innerHeight||document.documentElement.clientHeight,F=window.innerWidth||document.documentElement.clientWidth;if(X&&(Z.bottom<=0||Z.top>=_))return!1;if(Y&&(Z.right<=0||Z.left>=F))return!1;return!0},IQ=(J,Q,X=!1,Y='Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"')=>{let Z=dQ();if(!Z)return 0;let _=X?J.toUpperCase():J;return Z.font=`${Q}px ${Y}`,Z.measureText(_).width};var vQ=(J,Q="\uD83D\uDE80")=>{let X=0,Y=J+Q;for(let Z=0;Z<Y.length;Z++){let _=Y.charCodeAt(Z);X=(X<<5)-X+_,X|=0}return(X>>>0).toString(16)},RQ=(J)=>{return`${vQ(J)}::${J}`},kQ=(J,Q)=>{return(X)=>{if(!(X instanceof CustomEvent))return;try{J(X.detail)}catch(Y){if(Q)Q(Y);else console.error("Event listener error:",Y)}}},TQ=(J,Q)=>{if(!J)return;let X=RQ(J);window.dispatchEvent(new CustomEvent(X,{detail:Q,bubbles:!0,cancelable:!1}))},x=(J,Q,X={})=>{if(!J||typeof Q!=="function")return()=>{};let Y=kQ(Q,X.onError),Z=RQ(J);return window.addEventListener(Z,Y),()=>window.removeEventListener(Z,Y)},MQ=(J,Q,X={})=>{if(!J||typeof Q!=="function")return()=>{};let Y=x(J,(Z)=>{Y(),Q(Z)},X);return Y};var zQ=require("react");var AQ=(J,Q,X=[])=>{zQ.useEffect(()=>{if(!J||typeof Q!=="function")return;return x(J,Q)},[J,Q,...X])};var KQ=(J,Q)=>{return N(J,Q,new WeakMap)},N=(J,Q,X)=>{if(Object.is(J,Q))return!0;if(J instanceof Date&&Q instanceof Date)return J.getTime()===Q.getTime();if(J instanceof RegExp&&Q instanceof RegExp)return J.source===Q.source&&J.flags===Q.flags;if(J instanceof Error&&Q instanceof Error)return J.name===Q.name&&J.message===Q.message;if(J instanceof ArrayBuffer&&Q instanceof ArrayBuffer){if(J.byteLength!==Q.byteLength)return!1;return SQ(new Uint8Array(J),new Uint8Array(Q))}if(ArrayBuffer.isView(J)&&ArrayBuffer.isView(Q)){if(J.constructor!==Q.constructor)return!1;if(J.byteLength!==Q.byteLength)return!1;let Y=new Uint8Array(J.buffer,J.byteOffset,J.byteLength),Z=new Uint8Array(Q.buffer,Q.byteOffset,Q.byteLength);return SQ(Y,Z)}if(typeof J==="object"&&J!==null&&typeof Q==="object"&&Q!==null){let Y=X.get(J);if(Y?.has(Q))return!0;if(Y)Y.add(Q);else X.set(J,new WeakSet([Q]))}if(Array.isArray(J)&&Array.isArray(Q)){if(J.length!==Q.length)return!1;for(let Y=0;Y<J.length;Y++)if(!N(J[Y],Q[Y],X))return!1;return!0}if(J instanceof Set&&Q instanceof Set){if(J.size!==Q.size)return!1;let Y=Array.from(Q);for(let Z of J){let _=Y.findIndex((F)=>N(Z,F,X));if(_===-1)return!1;Y.splice(_,1)}return!0}if(J instanceof Map&&Q instanceof Map){if(J.size!==Q.size)return!1;for(let[Y,Z]of J)if(!Q.has(Y)||!N(Z,Q.get(Y),X))return!1;return!0}if(J!==null&&typeof J==="object"&&Q!==null&&typeof Q==="object"){let Y=J,Z=Q,_=Object.keys(Y);if(_.length!==Object.keys(Z).length)return!1;for(let F of _)if(!N(Y[F],Z[F],X))return!1;return!0}return!1},SQ=(J,Q)=>{for(let X=0;X<J.length;X++)if(J[X]!==Q[X])return!1;return!0};var hQ={};module.exports=P(hQ);var yQ={};V(yQ,{words:()=>bJ,useEventBus:()=>AQ,uncapitalize:()=>yJ,truncate:()=>jJ,titleCase:()=>kJ,timeout:()=>DJ,stringifyBigIntValues:()=>m,storageSetItemWithTTL:()=>JQ,storageSetItem:()=>h,storageRemoveItem:()=>j,storageGetItemWithTTL:()=>QQ,storageGetItem:()=>y,storageGetAllKeys:()=>$Q,storageExists:()=>GQ,storageClearBySuffix:()=>ZQ,storageClearByPrefixOrSuffix:()=>C,storageClearByPrefix:()=>YQ,storageClear:()=>XQ,storageCalculateSize:()=>_Q,snakeCase:()=>vJ,slugify:()=>mJ,sleep:()=>k,setStyleProperties:()=>WQ,retry:()=>wJ,pickBy:()=>d,pick:()=>TJ,pascalCase:()=>CJ,parallelLimit:()=>NJ,omitBy:()=>MJ,omit:()=>RJ,merge:()=>LJ,mapValues:()=>zJ,mapKeys:()=>AJ,lines:()=>lJ,keysOf:()=>EJ,kebabCase:()=>dJ,isUndefined:()=>XJ,isString:()=>e,isSet:()=>HJ,isRegExp:()=>FJ,isRecord:()=>QJ,isPromise:()=>WJ,isPrimitive:()=>OJ,isObject:()=>D,isNumber:()=>a,isNull:()=>YJ,isNil:()=>ZJ,isMap:()=>BJ,isInViewport:()=>OQ,isFunction:()=>JJ,isError:()=>UJ,isEqual:()=>KQ,isEmpty:()=>IJ,isDefined:()=>GJ,isDate:()=>_J,isCacheStorageSupported:()=>f,isBrowser:()=>UQ,isBoolean:()=>t,isArray:()=>$J,invert:()=>KJ,hasOwn:()=>PJ,groupBy:()=>SJ,fromEntries:()=>qJ,escapeRegExp:()=>uJ,escapeHtml:()=>pJ,entriesOf:()=>VJ,defer:()=>xJ,deepMerge:()=>v,deepClone:()=>FQ,checkVisibility:()=>BQ,capitalizeFirstLetter:()=>gJ,capitalize:()=>hJ,camelCase:()=>fJ,calculateRenderedTextWidth:()=>IQ,cacheResponseKeys:()=>rJ,cachePutResponse:()=>cJ,cacheNames:()=>tJ,cacheMatchResponse:()=>nJ,cacheHasResponse:()=>oJ,cacheExists:()=>aJ,cacheDeleteResponse:()=>iJ,cacheDelete:()=>eJ,cacheClear:()=>sJ,busSubscribe:()=>x,busOnce:()=>MQ,busDispatch:()=>TQ,__resetTextMeasurementCache:()=>HQ});module.exports=P(yQ);