@pivanov/utils 0.0.2 โ†’ 0.0.3

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,54 +1,54 @@
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>
7
- <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;
11
- <img src="https://github.com/pivanov/pivanov-utils/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI Status" />
12
- &nbsp;&nbsp;
13
- <img src="https://codecov.io/github/pivanov/pivanov-utils/graph/badge.svg?token=EPRKTP7D79" alt="Coverage Status">
4
+ <i>A comprehensive collection of TypeScript utilities for modern web development</i>
5
+ <br />
6
+ <br />
7
+ <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;
11
+ <img src="https://github.com/pivanov/pivanov-utils/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI Status" />
12
+ &nbsp;&nbsp;
13
+ <img src="https://codecov.io/github/pivanov/pivanov-utils/graph/badge.svg?token=EPRKTP7D79" alt="Coverage Status" />
14
14
  </p>
15
15
 
16
16
  ## Features
17
17
 
18
- - ๐Ÿ”„ **Type-Safe** - Full TypeScript support with strict type checking
19
- - ๐Ÿ”„ **Immutable Operations** - Safe object and array manipulations
20
- - ๐ŸŽจ **String Transformations** - Comprehensive string formatting utilities
21
- - ๐Ÿ› ๏ธ **DOM Utilities** - Browser-safe DOM manipulation helpers
22
- - โšก **Performance Focused** - Optimized for both speed and bundle size
23
- - ๐Ÿ“ฆ **Tree-Shakeable** - Import only what you need
24
- - ๐Ÿ“ **Well Documented** - Detailed documentation and examples
25
- - โœ… **Well Tested** - Comprehensive test coverage
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
26
24
 
27
25
  ## Installation
28
26
 
29
27
  ```bash
30
- pnpm install @pivanov/utils
31
- # or
32
- yarn add @pivanov/utils
28
+ pnpm add @pivanov/utils
33
29
  # or
34
30
  npm install @pivanov/utils
31
+ # or
32
+ yarn add @pivanov/utils
35
33
  ```
36
34
 
37
- ## Documentation
35
+ ## Quick Start
38
36
 
39
- ### Module Overview
37
+ ```typescript
38
+ import { camelCase } from '@pivanov/utils/string';
39
+ import { pick } from '@pivanov/utils/object';
40
+ import { isString } from '@pivanov/utils/assertion';
40
41
 
41
- The package is organized into several modules:
42
+ camelCase('foo-bar'); // 'fooBar'
43
+ pick({ name: 'John', age: 30 }, ['name']); // { name: 'John' }
44
+ isString('hello'); // true
45
+ ```
42
46
 
43
- - **assertion** - Type guards and runtime type checking
44
- - **object** - Object manipulation utilities
45
- - **promise** - Async utilities and promise helpers
46
- - **string** - String transformation utilities
47
- - **tools** - Various utilities including DOM, Event Bus, and more
47
+ ## API Reference
48
48
 
49
- ### Assertion Module
49
+ ### Type Guards & Assertions
50
50
 
51
- Type-safe runtime type checking utilities.
51
+ Runtime type checking with TypeScript type narrowing.
52
52
 
53
53
  ```typescript
54
54
  import {
@@ -58,32 +58,32 @@ import {
58
58
  isObject,
59
59
  isFunction,
60
60
  isNull,
61
- isUndefined
61
+ isUndefined,
62
62
  } from '@pivanov/utils/assertion';
63
63
 
64
- // Type Guards with TypeScript type narrowing
65
- isString('hello'); // type narrowed to string
66
- isNumber(123); // type narrowed to number
67
- isBoolean(true); // type narrowed to boolean
68
- isObject({}); // type narrowed to object
69
- isFunction(() => {}); // type narrowed to function
70
- isNull(null); // type narrowed to null
71
- isUndefined(void 0); // type narrowed to undefined
72
-
73
- // Use in conditional checks
64
+ // Type guards automatically narrow TypeScript types
74
65
  function processValue(value: unknown) {
75
66
  if (isString(value)) {
76
- return value.toUpperCase(); // TypeScript knows value is string
67
+ return value.toUpperCase(); // TypeScript knows this is a string
77
68
  }
78
69
  if (isNumber(value)) {
79
- return value.toFixed(2); // TypeScript knows value is number
70
+ return value.toFixed(2); // TypeScript knows this is a number
80
71
  }
81
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
82
82
  ```
83
83
 
84
- ### Object Module
84
+ ### Object Utilities
85
85
 
86
- Type-safe object manipulation utilities.
86
+ Immutable object manipulation with full type safety.
87
87
 
88
88
  ```typescript
89
89
  import { pick, omit, merge, deepMerge } from '@pivanov/utils/object';
@@ -98,8 +98,8 @@ omit(user, ['email']);
98
98
  // { name: 'John', age: 30 }
99
99
 
100
100
  // Shallow merge
101
- merge({ a: 1 }, { b: 2 });
102
- // { a: 1, b: 2 }
101
+ merge({ a: 1, b: 2 }, { b: 3, c: 4 });
102
+ // { a: 1, b: 3, c: 4 }
103
103
 
104
104
  // Deep merge with nested structures
105
105
  deepMerge(
@@ -109,24 +109,9 @@ deepMerge(
109
109
  // { user: { name: 'John', settings: { theme: 'dark', fontSize: 14 } } }
110
110
  ```
111
111
 
112
- ### Promise Module
112
+ ### String Utilities
113
113
 
114
- Async utilities for better promise handling.
115
-
116
- ```typescript
117
- import { sleep } from '@pivanov/utils/promise';
118
-
119
- // Pause execution for specified duration
120
- async function example() {
121
- console.log('Start');
122
- await sleep(1000); // Wait for 1 second
123
- console.log('End');
124
- }
125
- ```
126
-
127
- ### String Module
128
-
129
- String manipulation utilities with TypeScript support.
114
+ String transformation utilities with TypeScript template literal types support.
130
115
 
131
116
  ```typescript
132
117
  import {
@@ -136,234 +121,289 @@ import {
136
121
  slugify,
137
122
  capitalize,
138
123
  uncapitalize,
139
- capitalizeFirstLetter
124
+ capitalizeFirstLetter,
140
125
  } from '@pivanov/utils/string';
141
126
 
142
127
  // Case transformations
143
- camelCase('foo-bar'); // 'fooBar'
144
- pascalCase('foo_bar'); // 'FooBar'
145
- kebabCase('fooBar'); // 'foo-bar'
146
- slugify('Hello World!'); // 'hello-world'
128
+ camelCase('foo-bar'); // 'fooBar'
129
+ camelCase('FOO_BAR'); // 'fooBar'
147
130
 
148
- // Capitalization with TypeScript support
149
- capitalize('hello'); // 'Hello' (with type Capitalize<'hello'>)
150
- uncapitalize('Hello'); // 'hello' (with type Uncapitalize<'Hello'>)
151
- capitalizeFirstLetter('hello world'); // 'Hello world'
152
- ```
153
-
154
- ### Tools Module
131
+ pascalCase('foo-bar'); // 'FooBar'
132
+ pascalCase('foo_bar_baz'); // 'FooBarBaz'
155
133
 
156
- Various utilities for DOM manipulation, event handling, and more.
134
+ kebabCase('fooBar'); // 'foo-bar'
135
+ kebabCase('XMLHttpRequest'); // 'xml-http-request'
157
136
 
158
- #### DOM Utilities
137
+ slugify('Hello World!'); // 'hello-world'
138
+ slugify('รœber Cafรฉ'); // 'uber-cafe'
159
139
 
160
- ```typescript
161
- import { checkVisibility, setStyleProperties } from '@pivanov/utils/tools';
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'
144
+ ```
162
145
 
163
- // Check element visibility
164
- const isVisible = checkVisibility(element);
146
+ **When to use each:**
165
147
 
166
- // Set CSS custom properties
167
- setStyleProperties(element, {
168
- '--background-color': '#fff',
169
- '--text-color': '#000'
170
- });
171
- ```
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)
172
152
 
173
- #### Event Bus
153
+ ### Promise Utilities
174
154
 
175
- Type-safe event bus with React hooks support.
155
+ Simple async utilities for better promise handling.
176
156
 
177
157
  ```typescript
178
- import { busDispatch, busSubscribe, useEventBus } from '@pivanov/utils/tools/eventBus';
179
-
180
- // Define your events interface
181
- interface Events {
182
- 'user-logged-in': { id: number; name: string };
183
- 'data-updated': { timestamp: number };
184
- }
158
+ import { sleep } from '@pivanov/utils/promise';
185
159
 
186
- // React hook usage
187
- function Component() {
188
- useEventBus('user-logged-in', (data) => {
189
- console.log('User logged in:', data.name);
190
- });
160
+ async function example() {
161
+ console.log('Start');
162
+ await sleep(1000); // Wait 1 second
163
+ console.log('Done');
191
164
  }
192
-
193
- // Direct subscription
194
- const unsubscribe = busSubscribe('data-updated', (data) => {
195
- console.log('Data updated at:', data.timestamp);
196
- });
197
-
198
- // Dispatch events
199
- busDispatch('user-logged-in', { id: 1, name: 'John' });
200
165
  ```
201
166
 
202
- #### Deep Clone & Equality
167
+ ### Deep Clone & Equality
168
+
169
+ High-performance deep cloning and comparison with circular reference handling.
203
170
 
204
171
  ```typescript
205
172
  import { deepClone, isEqual } from '@pivanov/utils/tools';
206
173
 
207
- // Deep clone with circular reference handling
174
+ // Deep clone complex structures
208
175
  const original = {
209
176
  nested: { array: [1, 2, { value: 3 }] },
210
177
  date: new Date(),
211
- circular: {} as any
178
+ map: new Map([['key', 'value']]),
179
+ set: new Set([1, 2, 3]),
180
+ circular: {} as any,
212
181
  };
213
- original.circular = original;
182
+ original.circular = original; // Circular reference
183
+
214
184
  const cloned = deepClone(original);
185
+ // Fully independent copy with circular references preserved
215
186
 
216
187
  // Deep equality comparison
217
188
  isEqual({ a: [1, 2, 3] }, { a: [1, 2, 3] }); // true
218
189
  isEqual(new Date('2024-01-01'), new Date('2024-01-01')); // true
219
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)
220
192
  ```
221
193
 
222
- #### Cache API
194
+ **Supported types:**
223
195
 
224
- Browser-safe cache utilities with TypeScript support.
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
202
+
203
+ ### Event Bus
204
+
205
+ Type-safe, lightweight event bus with React hooks support.
225
206
 
226
207
  ```typescript
227
- import { CacheAPI } from '@pivanov/utils/tools/cache-api';
208
+ import { busDispatch, busSubscribe, useEventBus } from '@pivanov/utils/tools';
228
209
 
229
- // Initialize cache
230
- const cache = new CacheAPI('my-cache');
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
+ };
231
216
 
232
- // Store data with expiration
233
- await cache.set('user-data', { name: 'John' }, {
234
- expireIn: '1h' // Supports: 's' (seconds), 'm' (minutes), 'h' (hours), 'd' (days)
235
- });
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
+ });
236
222
 
237
- // Retrieve data
238
- const userData = await cache.get('user-data');
223
+ return <div>Profile</div>;
224
+ }
239
225
 
240
- // Check if data exists and is not expired
241
- const exists = await cache.has('user-data');
226
+ // Vanilla JavaScript usage
227
+ const unsubscribe = busSubscribe<AppEvents>('data-updated', (data) => {
228
+ console.log('Updated at:', data.timestamp);
229
+ });
242
230
 
243
- // Remove data
244
- await cache.delete('user-data');
231
+ // Later: cleanup
232
+ unsubscribe();
245
233
 
246
- // Clear all cache
247
- await cache.clear();
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
+ });
248
240
  ```
249
241
 
250
- #### React to Web Components (r2wc)
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
251
249
 
252
- A utility to convert React components into standalone Web Components that can be used in any application.
250
+ ### Browser Cache API
253
251
 
254
- ##### Creating a Widget
252
+ Type-safe wrapper around the browser Cache API with expiration support.
255
253
 
256
- Create a single file for your widget (e.g., `sparkline-widget.ts`):
257
254
  ```typescript
258
- import { registerWidget } from '@pivanov/utils';
255
+ 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
+ });
259
274
 
260
- // Your React component
261
- const SparklineChart = ({ values, color }) => (
262
- <div className="sparkline">
263
- {/* Your chart implementation */}
264
- </div>
275
+ // Retrieve data
276
+ const userData = await storageGetItem<{ id: number; name: string }>(
277
+ CACHE_NAME,
278
+ 'user-data'
265
279
  );
266
280
 
267
- // Register the widget with optional configuration
268
- registerWidget({
269
- name: 'sparkline',
270
- component: SparklineChart,
271
- styles: [
272
- 'https://cdn.example.com/styles/sparkline.css', // Optional: CSS files to load
273
- ],
274
- svgSpritePath: 'https://cdn.example.com/icons/sprite.svg', // Optional: SVG sprite path
275
- });
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);
295
+
296
+ // Calculate cache size (in bytes)
297
+ const totalSize = await storageCalculateSize(CACHE_NAME);
298
+ const itemSize = await storageCalculateSize(CACHE_NAME, 'user-data');
299
+
300
+ // Clear all cache
301
+ await storageClear(CACHE_NAME);
276
302
  ```
277
303
 
278
- ##### Using in React Applications
304
+ **Features:**
305
+
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
311
+
312
+ ### DOM Utilities
313
+
314
+ Browser-safe DOM manipulation helpers.
279
315
 
280
316
  ```typescript
281
- import { importWidgets } from '@pivanov/utils';
282
- import { createRoot } from 'react-dom/client';
283
- import { App } from './app';
284
-
285
- // Load widgets ... we need to do this once
286
- void importWidgets([
287
- 'https://cdn.example.com/widgets/sparkline.js'
288
- ]);
289
-
290
- const container = document.getElementById('root');
291
- if (container) {
292
- createRoot(container).render(<App />);
317
+ 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);
293
328
  }
294
329
 
295
- ...
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
+ }
296
335
 
297
- // Then use the widgets as React components
298
- <WidgetComponent
299
- name="sparkline"
300
- widgetProps={{ color: 'green', values: [1, 2, 3, 4, 5] }}
301
- />
302
- ```
336
+ // Set CSS custom properties
337
+ setStyleProperties(element, {
338
+ '--primary-color': '#3b82f6',
339
+ '--spacing': '1rem',
340
+ '--border-radius': '8px',
341
+ });
303
342
 
304
- ##### Using in Plain HTML
305
-
306
- ```html
307
- <!DOCTYPE html>
308
- <html>
309
- <head>
310
- <script type="module">
311
- import { importWidgets, renderWidget } from 'https://esm.sh/@pivanov/utils';
312
-
313
- void importWidgets([
314
- 'https://cdn.example.com/widgets/sparkline.js'
315
- ]);
316
-
317
- renderWidget({
318
- name: 'sparkline',
319
- mountTo: document.getElementById('sparkline'),
320
- widgetProps: {
321
- color: 'green',
322
- values: [1, 2, 3, 4, 5]
323
- }
324
- });
325
- </script>
326
- </head>
327
- <body>
328
- <div id="sparkline"></div>
329
- </body>
330
- </html>
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
+ );
331
352
  ```
332
353
 
333
- Key Features:
334
- - Single-file widget definition
335
- - Automatic CSS and SVG sprite loading
336
- - ESM support for browser and bundler usage
337
- - TypeScript support with proper type inference
338
- - Works in both React and plain HTML environments
339
-
340
- ### Tree Shaking
354
+ ## Tree Shaking
341
355
 
342
- Import specific utilities to minimize bundle size:
356
+ Import only what you need to minimize bundle size:
343
357
 
344
358
  ```typescript
345
- // Import only what you need
359
+ // โœ… Good: Import specific utilities
346
360
  import { camelCase } from '@pivanov/utils/string';
347
361
  import { deepClone } from '@pivanov/utils/tools';
348
- import { isString } from '@pivanov/utils/assertion';
362
+
363
+ // โŒ Avoid: Importing everything
364
+ import * as utils from '@pivanov/utils';
349
365
  ```
350
366
 
351
- ### TypeScript Integration
367
+ ## TypeScript Support
352
368
 
353
- All utilities are written in TypeScript and provide excellent type inference:
369
+ All utilities provide excellent type inference:
354
370
 
355
371
  ```typescript
356
372
  import { pick } from '@pivanov/utils/object';
373
+ import { capitalize } from '@pivanov/utils/string';
374
+
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" }
379
+
380
+ const str = 'hello' as const;
381
+ const capitalized = capitalize(str);
382
+ // Type: "Hello"
383
+ ```
357
384
 
358
- // TypeScript will infer correct types
359
- const user = { name: 'John', age: 30 } as const;
360
- const picked = pick(user, ['name']); // Type: { name: "John" }
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.)
361
399
  ```
362
400
 
363
- ## Contributing
401
+ ## Browser Compatibility
364
402
 
365
- Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
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
366
406
 
367
407
  ## License
368
408
 
369
- MIT ยฉ Pavel Ivanov
409
+ MIT ยฉ [Pavel Ivanov](https://github.com/pivanov)
package/dist/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @pivanov/utils v0.0.2
2
+ * @pivanov/utils v0.0.3
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"),t=require("react/jsx-runtime"),r=require("react-dom/client");const n=e=>{if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t},o=(e,...t)=>{if(!t.length)return e;const r=t.shift();if(void 0===r)return e;if(n(e)&&n(r))for(const t of Object.keys(r)){const s=r[t];if(n(s)){e[t]||Object.assign(e,{[t]:{}});const r=e[t];n(r)&&o(r,s)}else Object.assign(e,{[t]:s})}return o(e,...t)},s=(e,t)=>"bigint"==typeof t?t.toString():t,i=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}`,a=(e,t)=>{if(!e)return;const r=i(e);window.dispatchEvent(new CustomEvent(r,{detail:t,bubbles:!0,cancelable:!1}))},c=(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=i(e);return window.addEventListener(n,r),()=>window.removeEventListener(n,r)},l=(e,n,o={},s)=>{const i=class extends HTMLElement{constructor(){super();let e=Array.from(document.styleSheets).find((e=>e instanceof CSSStyleSheet&&!e.disabled));if(!e){const t=document.createElement("style");document.head.appendChild(t),e=t.sheet}Array.from(e.cssRules).find((e=>e.selectorText===`${this.tagName.toLowerCase()}`))||e.insertRule(`${this.tagName.toLowerCase()} { display: inline-flex; }`,e.cssRules.length);const t=o.shadow||(s?.length?"open":"closed");if(!o.shadow&&s?.length&&console.info("@@@ Styles are provided but shadowDOM is not enabled"),this.container=t?this.attachShadow({mode:t}):this,s&&t){const e=document.createElement("style");e.textContent=s.join("\n"),this.container.appendChild(e)}}connectedCallback(){this.root||(this.root=r.createRoot(this.container),this.subscribeToEvents())}disconnectedCallback(){this.root&&this.root.unmount()}subscribeToEvents(){c("@@-widget-create",(e=>{const{widgetProps:t,cacheKey:r}=e;this.cacheKey||r!==`${this.tagName.toLowerCase().replace("widget-","")}-${this.uuid}`||(this.cacheKey=r,this.mountComponent(t))})),c("@@-widget-update",(e=>{const{widgetProps:t,cacheKey:r}=e;this.cacheKey===r&&r===`${this.tagName.toLowerCase().replace("widget-","")}-${this.uuid}`&&a("@@-widget-update-props",{widgetProps:t,uuid:this.uuid})}))}mountComponent(e){this.root&&e&&this.root.render(t.jsx(n,{...e,container:this.container,root:this}))}};return customElements.get(e)||customElements.define(e,i),i},f=e.memo((r=>{const{name:n,uuid:o}=r,s=e.useRef(),i=`widget-${n}`;return e.useEffect((()=>{s.current&&(s.current.uuid=o)}),[o]),"string"==typeof o&&o.length?t.jsx(i,{ref:s}):null}),(()=>!0)),u=r=>{const{name:n,uuid:o,widgetProps:s}=r,i=e.useRef(!1),l=o?.trim()||crypto.randomUUID().replace(/-/g,"").slice(0,6),u=n.trim().toLowerCase(),p=`${u}-${l}`,d=!!u.length&&!!l.length;return e.useEffect((()=>{d&&i.current&&a("@@-widget-update",{widgetName:u,widgetProps:s,cacheKey:p})}),[s,l,u,d,p]),c("@@-widgets-loaded",(()=>{i.current=!0,a("@@-widget-create",{widgetName:u,widgetProps:s,cacheKey:p})})),d?t.jsx(f,{name:u,uuid:l}):(console.warn("Invalid widget!",u),null)},p=t=>{const{root:r,container:n,svgSpritePath:o,children:s}=t,[i,a]=e.useState(!1),[l,f]=e.useState();return e.useEffect((()=>{o?(async e=>{try{const t=await fetch(`${e}`);if(!t.ok)throw new Error(`Failed to fetch SVG: ${t.statusText}`);const r=await t.text(),n=(new DOMParser).parseFromString(r,"image/svg+xml");if(n.querySelector("parsererror"))throw new Error("SVG parsing failed");const o=n.documentElement;return o.setAttribute("xmlns","http://www.w3.org/2000/svg"),o.removeAttribute("id"),o}catch(e){return console.error("Error generating SVG sprite:",e),null}})(o).then((async e=>{n&&e&&n.appendChild(e),a(!0)})):a(!0)}),[]),c("@@-widget-update-props",(e=>{const{widgetProps:t,uuid:n}=e;r.uuid===n&&f(t)})),i?e.cloneElement(s,{...l,key:performance.now()}):null},d=(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 o=0;o<r;o++)o in e&&(n[o]=d(e[o],t));return n}const n=new Array(r);if(t.set(e,n),Object.keys(e).length===r){let o=0;const s=r-r%8;for(;o<s;)n[o]=d(e[o],t),n[o+1]=d(e[o+1],t),n[o+2]=d(e[o+2],t),n[o+3]=d(e[o+3],t),n[o+4]=d(e[o+4],t),n[o+5]=d(e[o+5],t),n[o+6]=d(e[o+6],t),n[o+7]=d(e[o+7],t),o+=8;for(;o<r;)n[o]=d(e[o],t),o++}else for(let o=0;o<r;o++)o in e&&(n[o]=d(e[o],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),o=n.length;for(let e=0;e<o;e++)r.add(d(n[e],t));return r}if(e instanceof Map){const r=new Map;t.set(e,r);const n=Array.from(e),o=n.length;for(let e=0;e<o;e++){const[o,s]=n[e];r.set(o,d(s,t))}return r}if(Object.getPrototypeOf(e)===Object.prototype){const r={};t.set(e,r);const n=Object.getOwnPropertySymbols(e),o=n.length;if(o>0)for(let t=0;t<o;t++){const o=n[t],s=Object.getOwnPropertyDescriptor(e,o);s?.enumerable&&Object.defineProperty(r,o,s)}const s=Object.getOwnPropertyDescriptors(e),i=Object.keys(e),a=i.length;let c=!1;for(let e=0;e<a;e++){const t=s[i[e]];if(t.get||t.set){c=!0;break}}if(c)for(let e=0;e<a;e++){const n=i[e],o=s[n];if(o.enumerable)if(o.get||o.set)Object.defineProperty(r,n,o);else{const e=d(o.value,t);Object.defineProperty(r,n,{...o,value:e})}}else{let n=0;const o=a-a%8;for(;n<o;){const o=i[n],s=i[n+1],a=i[n+2],c=i[n+3],l=i[n+4],f=i[n+5],u=i[n+6],p=i[n+7],g=e;r[o]=d(g[o],t),r[s]=d(g[s],t),r[a]=d(g[a],t),r[c]=d(g[c],t),r[l]=d(g[l],t),r[f]=d(g[f],t),r[u]=d(g[u],t),r[p]=d(g[p],t),n+=8}for(;n<a;){const o=i[n++];r[o]=d(e[o],t)}}return r}const n=Object.getPrototypeOf(e),o=Object.create(n);t.set(e,o);const s=Object.getOwnPropertyDescriptors(e),i=Object.keys(s),a=i.length;for(let e=0;e<a;e++){const r=i[e],n=s[r];if(n.enumerable)if(n.get||n.set)Object.defineProperty(o,r,n);else{const e=d(n.value,t);Object.defineProperty(o,r,{...n,value:e})}}const c=Object.getOwnPropertySymbols(e),l=c.length;for(let t=0;t<l;t++){const r=c[t],n=Object.getOwnPropertyDescriptor(e,r);n?.enumerable&&Object.defineProperty(o,r,n)}return o},g=(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(!g(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)||!g(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(!g(e[n],t[n]))return!1;return!0}return e===t};exports.WidgetComponent=u,exports.busDispatch=a,exports.busSubscribe=c,exports.calculateRenderedTextWidth=(e,t,r=!1,n='Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"')=>{const o=r?e.toUpperCase():e,s=document.createElement("canvas").getContext("2d");return s.font=`${t}px ${n}`,s.measureText(o).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=>d(e,new WeakMap),exports.deepMerge=o,exports.importWidgets=async e=>{const t=e.filter((e=>e?.length&&!document.querySelector(`script[src="${e}"]`)));(await Promise.allSettled(t.map((e=>(async e=>new Promise(((t,r)=>{const n=document.createElement("script");n.onload=()=>{t(!0)},n.onerror=()=>{const t=new Error(`Failed to load script: ${e}`);r(t)},n.type="module",n.src=e,document.head.appendChild(n)})))(e))))).forEach(((e,r)=>{"rejected"===e.status&&console.error(`Failed to load widget script ${t[r]}:`,e.reason)})),a("@@-widgets-loaded",!0)},exports.isBoolean=e=>"boolean"==typeof e,exports.isBrowser=()=>"undefined"!=typeof window,exports.isEqual=g,exports.isFunction=e=>"function"==typeof e,exports.isNull=e=>null===e,exports.isNumber=e=>"number"==typeof e,exports.isObject=n,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.r2wc=l,exports.registerWidget=e=>{const{name:r,styles:n,component:o,svgSpritePath:s}=e;return l(`widget-${r}`,(e=>{const{root:n,container:i,...a}=e;return t.jsx(p,{container:i,root:n,svgSpritePath:s,children:t.jsx(o,{...a,name:r})})}),{shadow:"open"},n)},exports.renderWidget=e=>{const{name:n,uuid:o,mountTo:s,widgetProps:i}=e,a=o?.trim()||crypto.randomUUID().replace(/-/g,"").slice(0,6),c=n.trim().toLowerCase();!!c.length&&!!a.length&&s&&s instanceof HTMLElement&&r.createRoot(s).render(t.jsx(u,{name:c,uuid:a,widgetProps:i}))},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=await r.match(t);if(e){const t=e.clone();return(await t.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),o=await n.keys();for(const e of o){const o=e.url.split("/"),s=o[o.length-1]||"";(r&&s.startsWith(t)||!r&&s.endsWith(t))&&await n.delete(e)}},exports.storageExists=async(e,t)=>{const r=await caches.open(e);return void 0!==await r.match(t)},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=await r.match(t);if(!n)return null;const o=await n.text();return JSON.parse(o)},exports.storageRemoveItem=async(e,t)=>{const r=await caches.open(e);return await r.delete(t)},exports.storageSetItem=async(e,t,r)=>{const n=await caches.open(e),o=JSON.stringify(r,s),i=new Response(o,{headers:{"Content-Type":"application/json"}});await n.put(t,i)},exports.stringifyBigIntValues=s,exports.uncapitalize=e=>e.charAt(0).toLowerCase()+e.slice(1),exports.useEventBus=(t,r,n=[])=>{e.useEffect((()=>{if(t&&"function"==typeof r)return c(t,r)}),[t,r,...n])};
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])};
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @pivanov/utils v0.0.2
2
+ * @pivanov/utils v0.0.3
3
3
  * (c) 2024-present Pavel Ivanov
4
4
  * Released under the MIT License.
5
5
  * https://github.com/pivanov/utils
6
6
  */
7
- import{useEffect as e,memo as t,useRef as r,useState as n,cloneElement as o}from"react";import{jsx as s}from"react/jsx-runtime";import{createRoot as i}from"react-dom/client";const c=e=>"boolean"==typeof e,a=e=>"number"==typeof e,f=e=>"string"==typeof e,l=e=>"function"==typeof e,u=e=>{if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t},p=e=>void 0===e,d=e=>null===e,h=(e,t)=>t.reduce(((e,t)=>(delete e[t],e)),{...e}),g=(e,t)=>{const r={};for(const n of t)n in e&&(r[n]=e[n]);return r},y=(e,...t)=>Object.assign(e,...t),w=(e,...t)=>{if(!t.length)return e;const r=t.shift();if(void 0===r)return e;if(u(e)&&u(r))for(const t of Object.keys(r)){const n=r[t];if(u(n)){e[t]||Object.assign(e,{[t]:{}});const r=e[t];u(r)&&w(r,n)}else Object.assign(e,{[t]:n})}return w(e,...t)},m=e=>new Promise((t=>{setTimeout(t,e)})),b=e=>e.trim().toLowerCase().replace(/^[-_\s]+/,"").replace(/[-_\s]+(.)?/g,((e,t)=>t?t.toUpperCase():"")),O=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(""),j=e=>e.charAt(0).toUpperCase()+e.slice(1),E=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(),A=e=>e.normalize("NFKD").toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[-\s_]+/g,"-").replace(/^-+|-+$/g,""),P=e=>e.charAt(0).toUpperCase()+e.slice(1),C=e=>e.charAt(0).toLowerCase()+e.slice(1),S=(e,t)=>"bigint"==typeof t?t.toString():t,L=async(e,t,r)=>{const n=await caches.open(e),o=JSON.stringify(r,S),s=new Response(o,{headers:{"Content-Type":"application/json"}});await n.put(t,s)},$=async(e,t)=>{const r=await caches.open(e),n=await r.match(t);if(!n)return null;const o=await n.text();return JSON.parse(o)},v=async(e,t)=>{const r=await caches.open(e);return await r.delete(t)},k=async e=>{const t=await caches.open(e),r=await t.keys();for(const e of r)await t.delete(e)},B=async(e,t,r=!0)=>{const n=await caches.open(e),o=await n.keys();for(const e of o){const o=e.url.split("/"),s=o[o.length-1]||"";(r&&s.startsWith(t)||!r&&s.endsWith(t))&&await n.delete(e)}},x=async(e,t)=>{const r=await caches.open(e);return void 0!==await r.match(t)},T=async e=>{const t=await caches.open(e);return(await t.keys()).map((e=>{const t=e.url.split("/");return t[t.length-1]||""}))},D=async(e,t)=>{const r=await caches.open(e);if(t){const e=await r.match(t);if(e){const t=e.clone();return(await t.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},M=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}`,U=(e,t)=>{if(!e)return;const r=M(e);window.dispatchEvent(new CustomEvent(r,{detail:t,bubbles:!0,cancelable:!1}))},N=(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=M(e);return window.addEventListener(n,r),()=>window.removeEventListener(n,r)},z=(t,r,n=[])=>{e((()=>{if(t&&"function"==typeof r)return N(t,r)}),[t,r,...n])},K=(e,t,r={},n)=>{const o=class extends HTMLElement{constructor(){super();let e=Array.from(document.styleSheets).find((e=>e instanceof CSSStyleSheet&&!e.disabled));if(!e){const t=document.createElement("style");document.head.appendChild(t),e=t.sheet}Array.from(e.cssRules).find((e=>e.selectorText===`${this.tagName.toLowerCase()}`))||e.insertRule(`${this.tagName.toLowerCase()} { display: inline-flex; }`,e.cssRules.length);const t=r.shadow||(n?.length?"open":"closed");if(!r.shadow&&n?.length&&console.info("@@@ Styles are provided but shadowDOM is not enabled"),this.container=t?this.attachShadow({mode:t}):this,n&&t){const e=document.createElement("style");e.textContent=n.join("\n"),this.container.appendChild(e)}}connectedCallback(){this.root||(this.root=i(this.container),this.subscribeToEvents())}disconnectedCallback(){this.root&&this.root.unmount()}subscribeToEvents(){N("@@-widget-create",(e=>{const{widgetProps:t,cacheKey:r}=e;this.cacheKey||r!==`${this.tagName.toLowerCase().replace("widget-","")}-${this.uuid}`||(this.cacheKey=r,this.mountComponent(t))})),N("@@-widget-update",(e=>{const{widgetProps:t,cacheKey:r}=e;this.cacheKey===r&&r===`${this.tagName.toLowerCase().replace("widget-","")}-${this.uuid}`&&U("@@-widget-update-props",{widgetProps:t,uuid:this.uuid})}))}mountComponent(e){this.root&&e&&this.root.render(s(t,{...e,container:this.container,root:this}))}};return customElements.get(e)||customElements.define(e,o),o},R=t((t=>{const{name:n,uuid:o}=t,i=r(),c=`widget-${n}`;return e((()=>{i.current&&(i.current.uuid=o)}),[o]),"string"==typeof o&&o.length?s(c,{ref:i}):null}),(()=>!0)),_=t=>{const{name:n,uuid:o,widgetProps:i}=t,c=r(!1),a=o?.trim()||crypto.randomUUID().replace(/-/g,"").slice(0,6),f=n.trim().toLowerCase(),l=`${f}-${a}`,u=!!f.length&&!!a.length;return e((()=>{u&&c.current&&U("@@-widget-update",{widgetName:f,widgetProps:i,cacheKey:l})}),[i,a,f,u,l]),N("@@-widgets-loaded",(()=>{c.current=!0,U("@@-widget-create",{widgetName:f,widgetProps:i,cacheKey:l})})),u?s(R,{name:f,uuid:a}):(console.warn("Invalid widget!",f),null)},F=e=>{const{name:t,uuid:r,mountTo:n,widgetProps:o}=e,c=r?.trim()||crypto.randomUUID().replace(/-/g,"").slice(0,6),a=t.trim().toLowerCase();!!a.length&&!!c.length&&n&&n instanceof HTMLElement&&i(n).render(s(_,{name:a,uuid:c,widgetProps:o}))},I=t=>{const{root:r,container:s,svgSpritePath:i,children:c}=t,[a,f]=n(!1),[l,u]=n();return e((()=>{i?(async e=>{try{const t=await fetch(`${e}`);if(!t.ok)throw new Error(`Failed to fetch SVG: ${t.statusText}`);const r=await t.text(),n=(new DOMParser).parseFromString(r,"image/svg+xml");if(n.querySelector("parsererror"))throw new Error("SVG parsing failed");const o=n.documentElement;return o.setAttribute("xmlns","http://www.w3.org/2000/svg"),o.removeAttribute("id"),o}catch(e){return console.error("Error generating SVG sprite:",e),null}})(i).then((async e=>{s&&e&&s.appendChild(e),f(!0)})):f(!0)}),[]),N("@@-widget-update-props",(e=>{const{widgetProps:t,uuid:n}=e;r.uuid===n&&u(t)})),a?o(c,{...l,key:performance.now()}):null},H=e=>{const{name:t,styles:r,component:n,svgSpritePath:o}=e;return K(`widget-${t}`,(e=>{const{root:r,container:i,...c}=e;return s(I,{container:i,root:r,svgSpritePath:o,children:s(n,{...c,name:t})})}),{shadow:"open"},r)},V=async e=>{const t=e.filter((e=>e?.length&&!document.querySelector(`script[src="${e}"]`)));(await Promise.allSettled(t.map((e=>(async e=>new Promise(((t,r)=>{const n=document.createElement("script");n.onload=()=>{t(!0)},n.onerror=()=>{const t=new Error(`Failed to load script: ${e}`);r(t)},n.type="module",n.src=e,document.head.appendChild(n)})))(e))))).forEach(((e,r)=>{"rejected"===e.status&&console.error(`Failed to load widget script ${t[r]}:`,e.reason)})),U("@@-widgets-loaded",!0)},Z=e=>G(e,new WeakMap),G=(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 o=0;o<r;o++)o in e&&(n[o]=G(e[o],t));return n}const n=new Array(r);if(t.set(e,n),Object.keys(e).length===r){let o=0;const s=r-r%8;for(;o<s;)n[o]=G(e[o],t),n[o+1]=G(e[o+1],t),n[o+2]=G(e[o+2],t),n[o+3]=G(e[o+3],t),n[o+4]=G(e[o+4],t),n[o+5]=G(e[o+5],t),n[o+6]=G(e[o+6],t),n[o+7]=G(e[o+7],t),o+=8;for(;o<r;)n[o]=G(e[o],t),o++}else for(let o=0;o<r;o++)o in e&&(n[o]=G(e[o],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),o=n.length;for(let e=0;e<o;e++)r.add(G(n[e],t));return r}if(e instanceof Map){const r=new Map;t.set(e,r);const n=Array.from(e),o=n.length;for(let e=0;e<o;e++){const[o,s]=n[e];r.set(o,G(s,t))}return r}if(Object.getPrototypeOf(e)===Object.prototype){const r={};t.set(e,r);const n=Object.getOwnPropertySymbols(e),o=n.length;if(o>0)for(let t=0;t<o;t++){const o=n[t],s=Object.getOwnPropertyDescriptor(e,o);s?.enumerable&&Object.defineProperty(r,o,s)}const s=Object.getOwnPropertyDescriptors(e),i=Object.keys(e),c=i.length;let a=!1;for(let e=0;e<c;e++){const t=s[i[e]];if(t.get||t.set){a=!0;break}}if(a)for(let e=0;e<c;e++){const n=i[e],o=s[n];if(o.enumerable)if(o.get||o.set)Object.defineProperty(r,n,o);else{const e=G(o.value,t);Object.defineProperty(r,n,{...o,value:e})}}else{let n=0;const o=c-c%8;for(;n<o;){const o=i[n],s=i[n+1],c=i[n+2],a=i[n+3],f=i[n+4],l=i[n+5],u=i[n+6],p=i[n+7],d=e;r[o]=G(d[o],t),r[s]=G(d[s],t),r[c]=G(d[c],t),r[a]=G(d[a],t),r[f]=G(d[f],t),r[l]=G(d[l],t),r[u]=G(d[u],t),r[p]=G(d[p],t),n+=8}for(;n<c;){const o=i[n++];r[o]=G(e[o],t)}}return r}const n=Object.getPrototypeOf(e),o=Object.create(n);t.set(e,o);const s=Object.getOwnPropertyDescriptors(e),i=Object.keys(s),c=i.length;for(let e=0;e<c;e++){const r=i[e],n=s[r];if(n.enumerable)if(n.get||n.set)Object.defineProperty(o,r,n);else{const e=G(n.value,t);Object.defineProperty(o,r,{...n,value:e})}}const a=Object.getOwnPropertySymbols(e),f=a.length;for(let t=0;t<f;t++){const r=a[t],n=Object.getOwnPropertyDescriptor(e,r);n?.enumerable&&Object.defineProperty(o,r,n)}return o},W=()=>"undefined"!=typeof window,q=(e,t)=>{if(e)for(const[r,n]of Object.entries(t))e.style.setProperty(r,n)},J=e=>{const t=e.getBoundingClientRect(),r=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(t.bottom<0||t.top-r>=0)},Y=(e,t,r=!1,n='Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"')=>{const o=r?e.toUpperCase():e,s=document.createElement("canvas").getContext("2d");return s.font=`${t}px ${n}`,s.measureText(o).width},Q=(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(!Q(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)||!Q(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(!Q(e[n],t[n]))return!1;return!0}return e===t};export{_ as WidgetComponent,U as busDispatch,N as busSubscribe,Y as calculateRenderedTextWidth,b as camelCase,P as capitalize,j as capitalizeFirstLetter,J as checkVisibility,Z as deepClone,w as deepMerge,V as importWidgets,c as isBoolean,W as isBrowser,Q as isEqual,l as isFunction,d as isNull,a as isNumber,u as isObject,f as isString,p as isUndefined,E as kebabCase,y as merge,h as omit,O as pascalCase,g as pick,K as r2wc,H as registerWidget,F as renderWidget,q as setStyleProperties,m as sleep,A as slugify,D as storageCalculateSize,k as storageClear,B as storageClearByPrefixOrSuffix,x as storageExists,T as storageGetAllKeys,$ as storageGetItem,v as storageRemoveItem,L as storageSetItem,S as stringifyBigIntValues,C as uncapitalize,z as useEventBus};
7
+ import{useEffect as e}from"react";const t=e=>"boolean"==typeof e,r=e=>"number"==typeof e,n=e=>"string"==typeof e,o=e=>"function"==typeof e,s=e=>{if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t},c=e=>void 0===e,a=e=>null===e,i=(e,t)=>t.reduce(((e,t)=>(delete e[t],e)),{...e}),f=(e,t)=>{const r={};for(const n of t)n in e&&(r[n]=e[n]);return r},l=(e,...t)=>Object.assign(e,...t),u=(e,...t)=>{if(!t.length)return e;const r=t.shift();if(void 0===r)return e;if(s(e)&&s(r))for(const t of Object.keys(r)){const n=r[t];if(s(n)){e[t]||Object.assign(e,{[t]:{}});const r=e[t];s(r)&&u(r,n)}else Object.assign(e,{[t]:n})}return u(e,...t)},p=e=>new Promise((t=>{setTimeout(t,e)})),y=e=>e.trim().toLowerCase().replace(/^[-_\s]+/,"").replace(/[-_\s]+(.)?/g,((e,t)=>t?t.toUpperCase():"")),g=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(""),b=e=>e.charAt(0).toUpperCase()+e.slice(1),w=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(),h=e=>e.normalize("NFKD").toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[-\s_]+/g,"-").replace(/^-+|-+$/g,""),O=e=>e.charAt(0).toUpperCase()+e.slice(1),m=e=>e.charAt(0).toLowerCase()+e.slice(1),d=(e,t)=>"bigint"==typeof t?t.toString():t,j=e=>e.startsWith("http://")||e.startsWith("https://")?e:`https://cache.internal/${e}`,A=async(e,t,r)=>{const n=await caches.open(e),o=JSON.stringify(r,d),s=new Response(o,{headers:{"Content-Type":"application/json"}}),c=j(t);await n.put(new Request(c),s)},E=async(e,t)=>{const r=await caches.open(e),n=j(t),o=await r.match(new Request(n));if(!o)return null;const s=await o.text();return JSON.parse(s)},L=async(e,t)=>{const r=await caches.open(e),n=j(t);return await r.delete(new Request(n))},P=async e=>{const t=await caches.open(e),r=await t.keys();for(const e of r)await t.delete(e)},B=async(e,t,r=!0)=>{const n=await caches.open(e),o=await n.keys();for(const e of o){const o=e.url.split("/"),s=o[o.length-1]||"";(r&&s.startsWith(t)||!r&&s.endsWith(t))&&await n.delete(e)}},C=async(e,t)=>{const r=await caches.open(e),n=j(t);return void 0!==await r.match(new Request(n))},v=async e=>{const t=await caches.open(e);return(await t.keys()).map((e=>{const t=e.url.split("/");return t[t.length-1]||""}))},S=async(e,t)=>{const r=await caches.open(e);if(t){const e=j(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},k=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}`,$=(e,t)=>{if(!e)return;const r=k(e);window.dispatchEvent(new CustomEvent(r,{detail:t,bubbles:!0,cancelable:!1}))},D=(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=k(e);return window.addEventListener(n,r),()=>window.removeEventListener(n,r)},R=(t,r,n=[])=>{e((()=>{if(t&&"function"==typeof r)return D(t,r)}),[t,r,...n])},z=e=>M(e,new WeakMap),M=(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 o=0;o<r;o++)o in e&&(n[o]=M(e[o],t));return n}const n=new Array(r);if(t.set(e,n),Object.keys(e).length===r){let o=0;const s=r-r%8;for(;o<s;)n[o]=M(e[o],t),n[o+1]=M(e[o+1],t),n[o+2]=M(e[o+2],t),n[o+3]=M(e[o+3],t),n[o+4]=M(e[o+4],t),n[o+5]=M(e[o+5],t),n[o+6]=M(e[o+6],t),n[o+7]=M(e[o+7],t),o+=8;for(;o<r;)n[o]=M(e[o],t),o++}else for(let o=0;o<r;o++)o in e&&(n[o]=M(e[o],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),o=n.length;for(let e=0;e<o;e++)r.add(M(n[e],t));return r}if(e instanceof Map){const r=new Map;t.set(e,r);const n=Array.from(e),o=n.length;for(let e=0;e<o;e++){const[o,s]=n[e];r.set(o,M(s,t))}return r}if(Object.getPrototypeOf(e)===Object.prototype){const r={};t.set(e,r);const n=Object.getOwnPropertySymbols(e),o=n.length;if(o>0)for(let t=0;t<o;t++){const o=n[t],s=Object.getOwnPropertyDescriptor(e,o);s?.enumerable&&Object.defineProperty(r,o,s)}const s=Object.getOwnPropertyDescriptors(e),c=Object.keys(e),a=c.length;let i=!1;for(let e=0;e<a;e++){const t=s[c[e]];if(t.get||t.set){i=!0;break}}if(i)for(let e=0;e<a;e++){const n=c[e],o=s[n];if(o.enumerable)if(o.get||o.set)Object.defineProperty(r,n,o);else{const e=M(o.value,t);Object.defineProperty(r,n,{...o,value:e})}}else{let n=0;const o=a-a%8;for(;n<o;){const o=c[n],s=c[n+1],a=c[n+2],i=c[n+3],f=c[n+4],l=c[n+5],u=c[n+6],p=c[n+7],y=e;r[o]=M(y[o],t),r[s]=M(y[s],t),r[a]=M(y[a],t),r[i]=M(y[i],t),r[f]=M(y[f],t),r[l]=M(y[l],t),r[u]=M(y[u],t),r[p]=M(y[p],t),n+=8}for(;n<a;){const o=c[n++];r[o]=M(e[o],t)}}return r}const n=Object.getPrototypeOf(e),o=Object.create(n);t.set(e,o);const s=Object.getOwnPropertyDescriptors(e),c=Object.keys(s),a=c.length;for(let e=0;e<a;e++){const r=c[e],n=s[r];if(n.enumerable)if(n.get||n.set)Object.defineProperty(o,r,n);else{const e=M(n.value,t);Object.defineProperty(o,r,{...n,value:e})}}const i=Object.getOwnPropertySymbols(e),f=i.length;for(let t=0;t<f;t++){const r=i[t],n=Object.getOwnPropertyDescriptor(e,r);n?.enumerable&&Object.defineProperty(o,r,n)}return o},T=()=>"undefined"!=typeof window,U=(e,t)=>{if(e)for(const[r,n]of Object.entries(t))e.style.setProperty(r,n)},_=e=>{const t=e.getBoundingClientRect(),r=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(t.bottom<0||t.top-r>=0)},x=(e,t,r=!1,n='Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"')=>{const o=r?e.toUpperCase():e,s=document.createElement("canvas").getContext("2d");return s.font=`${t}px ${n}`,s.measureText(o).width},N=(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(!N(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)||!N(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(!N(e[n],t[n]))return!1;return!0}return e===t};export{$ as busDispatch,D as busSubscribe,x as calculateRenderedTextWidth,y as camelCase,O as capitalize,b as capitalizeFirstLetter,_ as checkVisibility,z as deepClone,u as deepMerge,t as isBoolean,T as isBrowser,N as isEqual,o as isFunction,a as isNull,r as isNumber,s as isObject,n as isString,c as isUndefined,w as kebabCase,l as merge,i as omit,g as pascalCase,f as pick,U as setStyleProperties,p as sleep,h as slugify,S as storageCalculateSize,P as storageClear,B as storageClearByPrefixOrSuffix,C as storageExists,v as storageGetAllKeys,E as storageGetItem,L as storageRemoveItem,A as storageSetItem,d as stringifyBigIntValues,m as uncapitalize,R as useEventBus};
package/dist/index.d.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  /*!
2
- * @pivanov/utils v0.0.2
2
+ * @pivanov/utils v0.0.3
3
3
  * (c) 2024-present Pavel Ivanov
4
4
  * Released under the MIT License.
5
5
  * https://github.com/pivanov/utils
6
6
  */
7
7
 
8
- import { DependencyList, DetailedHTMLProps, HTMLAttributes, ComponentType } from 'react';
9
- import * as react_jsx_runtime from 'react/jsx-runtime';
8
+ import { DependencyList } from 'react';
10
9
 
11
10
  /**
12
11
  * Type guard to check if a value is a boolean
@@ -598,131 +597,6 @@ declare const busSubscribe: <T extends IEventBus>(topic: IEventBus["topic"], lis
598
597
 
599
598
  declare const useEventBus: <T extends IEventBus>(topic: T["topic"], listener: TEventBusListener<T["message"]>, deps?: DependencyList) => void;
600
599
 
601
- declare global {
602
- interface HTMLElement {
603
- uuid?: string;
604
- }
605
- interface ISharedValues {
606
- baseStoreUI: unknown;
607
- }
608
- interface Window {
609
- pivanov?: unknown;
610
- }
611
- interface Navigator {
612
- [key: string | symbol]: unknown;
613
- }
614
- interface Document {
615
- [key: string | symbol]: unknown;
616
- }
617
- namespace JSX {
618
- interface IntrinsicElements {
619
- [elementName: `${string}-${string}`]: DetailedHTMLProps<HTMLAttributes<HTMLElement> & {
620
- [key: string]: unknown;
621
- }, HTMLElement>;
622
- }
623
- }
624
- }
625
- interface IR2WCOptions {
626
- shadow?: 'open' | 'closed';
627
- }
628
- interface IR2WCBaseProps {
629
- container?: HTMLElement;
630
- }
631
-
632
- /**
633
- * Converts a React component into a Web Component (Custom Element)
634
- *
635
- * @template Props - The props type for the React component, must extend IR2WCBaseProps
636
- * @param ReactComponent - The React component to convert
637
- * @param options - Configuration options for the Web Component
638
- * @param styles - Optional array of CSS styles to be injected into the shadow DOM
639
- * @returns A Custom Element constructor that can be registered with customElements.define
640
- *
641
- * @example
642
- * ```tsx
643
- * const MyWebComponent = r2wc(MyReactComponent, {
644
- * shadow: 'open',
645
- * });
646
- *
647
- * customElements.define('my-component', MyWebComponent);
648
- * ```
649
- */
650
- declare const r2wc: <Props extends IR2WCBaseProps>(elementName: string, ReactComponent: ComponentType<Props>, options?: IR2WCOptions, styles?: string[]) => CustomElementConstructor;
651
-
652
- /**
653
- * Props interface for Widget components
654
- * @interface IWidgetComponentProps
655
- * @property {string} name - The name of the widget
656
- * @property {string} [uuid] - Optional group identifier. Widgets sharing the same uuid will update together
657
- * @property {string[]} [jsFiles] - Array of JavaScript file URLs to load. Files are loaded sequentially in the specified order, useful for dependencies
658
- * @property {unknown} [widgetProps] - Optional props to pass to the widget
659
- */
660
- interface IWidgetComponentProps {
661
- name: string;
662
- uuid?: string;
663
- jsFiles?: string[];
664
- widgetProps?: unknown;
665
- }
666
- /**
667
- * React component that handles widget lifecycle and rendering
668
- * @component
669
- * @example
670
- * ```tsx
671
- * <WidgetComponent
672
- * name="my-widget"
673
- * uuid="group1" // Widgets with the same uuid will update together
674
- * widgetProps={{ color: 'blue' }}
675
- * />
676
- * ```
677
- * Multiple widgets with the same uuid will form a group - when one widget's props
678
- * are updated, all widgets in the group will receive the update.
679
- */
680
- declare const WidgetComponent: (props: IWidgetComponentProps) => react_jsx_runtime.JSX.Element | null;
681
- /**
682
- * Props interface for renderWidget function
683
- * @interface IRenderWidgetProps
684
- * @property {string} name - The name of the widget to load
685
- * @property {string} [uuid] - Optional unique identifier for the widget
686
- * @property {HTMLElement} mountTo - DOM element where the widget should be mounted
687
- * @property {Object.<string, unknown>} widgetProps - Props to pass to the widget
688
- */
689
- interface IRenderWidgetProps {
690
- name: string;
691
- uuid?: string;
692
- mountTo: HTMLElement;
693
- widgetProps?: {
694
- [key: string]: unknown;
695
- };
696
- }
697
- /**
698
- * Programmatically loads and mounts a widget into a specified DOM element
699
- * @function
700
- * @param {IRenderWidgetProps} widgetProps - Configuration options for loading the widget
701
- * @example
702
- * ```ts
703
- * renderWidget({
704
- * widgetName: 'my-widget',
705
- * uuid: 'group1',
706
- * mountTo: document.getElementById('widget-container'),
707
- * widgetProps: {
708
- * color: 'blue',
709
- * size: 'large'
710
- * }
711
- * });
712
- * ```
713
- */
714
- declare const renderWidget: (props: IRenderWidgetProps) => void;
715
-
716
- interface IRegisterWidgetProps<T> {
717
- name: string;
718
- styles?: string[];
719
- component: ComponentType<T>;
720
- svgSpritePath?: string;
721
- }
722
- declare const registerWidget: <T extends object>(props: IRegisterWidgetProps<T>) => CustomElementConstructor;
723
-
724
- declare const importWidgets: (jsFiles: string[]) => Promise<void>;
725
-
726
600
  type TCloneable = object | number | string | boolean | symbol | bigint | null | undefined;
727
601
  declare const deepClone: <T extends TCloneable>(obj: T) => T;
728
602
 
@@ -814,4 +688,4 @@ declare const calculateRenderedTextWidth: (text: string, fontSize: number, isUpp
814
688
  */
815
689
  declare const isEqual: <T, K>(obj: T | T[], objToCompare: K | K[]) => boolean;
816
690
 
817
- export { type IEventBus, type TBooleanish, type TCloneable, type TDict, type TObjType, WidgetComponent, busDispatch, busSubscribe, calculateRenderedTextWidth, camelCase, capitalize, capitalizeFirstLetter, checkVisibility, deepClone, deepMerge, importWidgets, isBoolean, isBrowser, isEqual, isFunction, isNull, isNumber, isObject, isString, isUndefined, kebabCase, merge, omit, pascalCase, pick, r2wc, registerWidget, renderWidget, setStyleProperties, sleep, slugify, storageCalculateSize, storageClear, storageClearByPrefixOrSuffix, storageExists, storageGetAllKeys, storageGetItem, storageRemoveItem, storageSetItem, stringifyBigIntValues, uncapitalize, useEventBus };
691
+ export { type IEventBus, type TBooleanish, type TCloneable, type TDict, type TObjType, busDispatch, busSubscribe, calculateRenderedTextWidth, camelCase, capitalize, capitalizeFirstLetter, checkVisibility, deepClone, deepMerge, isBoolean, isBrowser, isEqual, isFunction, isNull, isNumber, isObject, isString, isUndefined, kebabCase, merge, omit, pascalCase, pick, setStyleProperties, sleep, slugify, storageCalculateSize, storageClear, storageClearByPrefixOrSuffix, storageExists, storageGetAllKeys, storageGetItem, storageRemoveItem, storageSetItem, stringifyBigIntValues, uncapitalize, useEventBus };
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@pivanov/utils",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "A collection of personal utilities to live a happier life",
5
5
  "type": "module",
6
- "packageManager": "pnpm@9.0.0",
7
6
  "repository": {
8
7
  "type": "git",
9
8
  "url": "git://github.com/pivanov/pivanov-utils.git"
@@ -12,16 +11,6 @@
12
11
  "bugs": {
13
12
  "url": "https://github.com/pivanov/pivanov-utils/issues"
14
13
  },
15
- "scripts": {
16
- "build": "rm -rf dist && pnpm rollup -c",
17
- "test": "pnpm vitest",
18
- "test:coverage": "pnpm vitest --coverage",
19
- "test:ui": "pnpm vitest --ui",
20
- "lint": "biome lint .",
21
- "format": "biome format . --write",
22
- "check": "biome check . --write",
23
- "prepublishOnly": "pnpm build"
24
- },
25
14
  "author": {
26
15
  "name": "Pavel Ivanov",
27
16
  "email": "iweb.ivanov@gmail.com",
@@ -31,15 +20,13 @@
31
20
  "utils",
32
21
  "typescript",
33
22
  "react",
34
- "web-components",
35
23
  "dom",
36
24
  "cache",
25
+ "browser cache api",
37
26
  "event-bus",
38
27
  "string",
39
28
  "object",
40
- "promise",
41
- "r2wc",
42
- "react-to-web-component"
29
+ "promise"
43
30
  ],
44
31
  "license": "MIT",
45
32
  "main": "dist/cjs/index.js",
@@ -52,7 +39,9 @@
52
39
  "require": "./dist/cjs/index.js"
53
40
  }
54
41
  },
55
- "files": ["dist/"],
42
+ "files": [
43
+ "dist/"
44
+ ],
56
45
  "peerDependencies": {
57
46
  "react": ">=18",
58
47
  "react-dom": ">=18"
@@ -85,5 +74,14 @@
85
74
  },
86
75
  "publishConfig": {
87
76
  "access": "public"
77
+ },
78
+ "scripts": {
79
+ "build": "rm -rf dist && pnpm rollup -c",
80
+ "test": "pnpm vitest",
81
+ "test:coverage": "pnpm vitest --coverage",
82
+ "test:ui": "pnpm vitest --ui",
83
+ "lint": "biome lint .",
84
+ "format": "biome format . --write",
85
+ "check": "biome check . --write"
88
86
  }
89
- }
87
+ }