@leancodepl/utils 8.5.0 → 8.5.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.
package/README.md ADDED
@@ -0,0 +1,465 @@
1
+ # @leancodepl/utils
2
+
3
+ A TypeScript utility library for common development tasks including assertions, object transformations, file operations,
4
+ and hooks.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install @leancodepl/utils
10
+ # or
11
+ yarn add @leancodepl/utils
12
+ ```
13
+
14
+ ## API
15
+
16
+ ### `assertDefined(value, message)`
17
+
18
+ Asserts that a value is not undefined. Throws an error if the value is undefined.
19
+
20
+ **Parameters:**
21
+
22
+ - `value: T | undefined` - The value to check for undefined
23
+ - `message?: string` - Optional error message to use if assertion fails
24
+
25
+ **Usage:**
26
+
27
+ ```typescript
28
+ import { assertDefined } from "@leancodepl/utils"
29
+
30
+ interface User {
31
+ name: string
32
+ email: string
33
+ }
34
+
35
+ function processUserAssert(user?: User) {
36
+ assertDefined(user) // Throws if undefined, no return value
37
+ return user.name // TypeScript knows user is defined
38
+ }
39
+ ```
40
+
41
+ ### `assertNotNull(value, message)`
42
+
43
+ Asserts that a value is not null. Throws an error if the value is null.
44
+
45
+ **Parameters:**
46
+
47
+ - `value: T | null` - The value to check for null
48
+ - `message?: string` - Optional error message to use if assertion fails
49
+
50
+ **Usage:**
51
+
52
+ ```typescript
53
+ import { assertNotNull } from "@leancodepl/utils"
54
+
55
+ interface User {
56
+ name: string
57
+ email: string
58
+ }
59
+
60
+ function processData(data: string | null) {
61
+ assertNotNull(data, "Data cannot be null")
62
+ return data.toUpperCase() // TypeScript knows data is string
63
+ }
64
+ ```
65
+
66
+ ### `assertNotEmpty(value, message)`
67
+
68
+ Asserts that a value is not null or undefined. Throws an error if the value is null or undefined.
69
+
70
+ **Parameters:**
71
+
72
+ - `value: T | null | undefined` - The value to check for null or undefined
73
+ - `message?: string` - Optional error message to use if assertion fails
74
+
75
+ **Usage:**
76
+
77
+ ```typescript
78
+ import { assertNotEmpty } from "@leancodepl/utils"
79
+
80
+ interface User {
81
+ name: string
82
+ email: string
83
+ }
84
+
85
+ function processValue(value: string | null | undefined) {
86
+ assertNotEmpty(value, "Value is required")
87
+ return value.length // TypeScript knows value is string
88
+ }
89
+ ```
90
+
91
+ ### `ensureDefined(value, message)`
92
+
93
+ Ensures that a value is defined, returning it if defined or throwing an error if undefined.
94
+
95
+ **Parameters:**
96
+
97
+ - `value: T | undefined` - The value to ensure is defined
98
+ - `message?: string` - Optional error message to use if the value is undefined
99
+
100
+ **Returns:** The value if it is defined
101
+
102
+ **Usage:**
103
+
104
+ ```typescript
105
+ import { ensureDefined } from "@leancodepl/utils"
106
+
107
+ interface User {
108
+ name: string
109
+ email: string
110
+ }
111
+
112
+ function processUser(user?: User) {
113
+ const definedUser = ensureDefined(user) // Returns User or throws
114
+ return definedUser.name
115
+ }
116
+ ```
117
+
118
+ ### `ensureNotNull(value, message)`
119
+
120
+ Ensures that a value is not null, returning it if not null or throwing an error if null.
121
+
122
+ **Parameters:**
123
+
124
+ - `value: T | null` - The value to ensure is not null
125
+ - `message?: string` - Optional error message to use if the value is null
126
+
127
+ **Returns:** The value if it is not null
128
+
129
+ **Usage:**
130
+
131
+ ```typescript
132
+ import { ensureNotNull } from "@leancodepl/utils"
133
+
134
+ interface User {
135
+ name: string
136
+ email: string
137
+ }
138
+
139
+ function processData(data: string | null) {
140
+ const validData = ensureNotNull(data, "Data cannot be null")
141
+ return validData.toUpperCase()
142
+ }
143
+ ```
144
+
145
+ ### `ensureNotEmpty(value, message)`
146
+
147
+ Ensures that a value is not null or undefined, returning it if valid or throwing an error if empty.
148
+
149
+ **Parameters:**
150
+
151
+ - `value: T | null | undefined` - The value to ensure is not null or undefined
152
+ - `message?: string` - Optional error message to use if the value is null or undefined
153
+
154
+ **Returns:** The value if it is not null or undefined
155
+
156
+ **Usage:**
157
+
158
+ ```typescript
159
+ import { ensureNotEmpty } from "@leancodepl/utils"
160
+
161
+ interface User {
162
+ name: string
163
+ email: string
164
+ }
165
+
166
+ function processValue(value: string | null | undefined) {
167
+ const validValue = ensureNotEmpty(value, "Value is required")
168
+ return validValue.length
169
+ }
170
+ ```
171
+
172
+ ### `addPrefix(object, prefix)`
173
+
174
+ Adds a prefix to all keys in an object, creating a new object with prefixed keys.
175
+
176
+ **Parameters:**
177
+
178
+ - `object: T extends object` - The object whose keys will be prefixed
179
+ - `prefix: TPrefix extends string` - The prefix string to add to each key
180
+
181
+ **Returns:** A new object with all keys prefixed
182
+
183
+ **Usage:**
184
+
185
+ ```typescript
186
+ import { addPrefix } from "@leancodepl/utils"
187
+
188
+ const apiData = { userId: 1, userName: "John" }
189
+ const prefixed = addPrefix(apiData, "api_")
190
+ // Result: { api_userId: 1, api_userName: 'John' }
191
+ ```
192
+
193
+ ### `capitalizeDeep(value)`
194
+
195
+ Recursively transforms all object keys to use capitalized (PascalCase) format.
196
+
197
+ **Parameters:**
198
+
199
+ - `value: T` - The value to transform (can be object, array, or primitive)
200
+
201
+ **Returns:** A new object with all keys converted to PascalCase
202
+
203
+ **Usage:**
204
+
205
+ ```typescript
206
+ import { capitalizeDeep } from "@leancodepl/utils"
207
+
208
+ const clientData = { userId: 1, userName: "John", profile: { firstName: "John" } }
209
+ const serverData = capitalizeDeep(clientData)
210
+ // Result: { UserId: 1, UserName: 'John', Profile: { FirstName: 'John' } }
211
+ ```
212
+
213
+ ### `uncapitalizeDeep(value)`
214
+
215
+ Recursively transforms all object keys to use uncapitalized (camelCase) format.
216
+
217
+ **Parameters:**
218
+
219
+ - `value: T` - The value to transform (can be object, array, or primitive)
220
+
221
+ **Returns:** A new object with all keys converted to camelCase
222
+
223
+ **Usage:**
224
+
225
+ ```typescript
226
+ import { uncapitalizeDeep } from "@leancodepl/utils"
227
+
228
+ const serverData = { UserId: 1, UserName: "John", Profile: { FirstName: "John" } }
229
+ const clientData = uncapitalizeDeep(serverData)
230
+ // Result: { userId: 1, userName: 'John', profile: { firstName: 'John' } }
231
+ ```
232
+
233
+ ### `toLowerFirst(value)`
234
+
235
+ Converts the first character of a string to lowercase.
236
+
237
+ **Parameters:**
238
+
239
+ - `value: string` - The string to transform
240
+
241
+ **Returns:** The string with the first character in lowercase
242
+
243
+ **Usage:**
244
+
245
+ ```typescript
246
+ import { toLowerFirst } from "@leancodepl/utils"
247
+
248
+ const result = toLowerFirst("HelloWorld")
249
+ // Result: 'helloWorld'
250
+ ```
251
+
252
+ ### `toUpperFirst(value)`
253
+
254
+ Converts the first character of a string to uppercase.
255
+
256
+ **Parameters:**
257
+
258
+ - `value: string` - The string to transform
259
+
260
+ **Returns:** The string with the first character in uppercase
261
+
262
+ **Usage:**
263
+
264
+ ```typescript
265
+ import { toUpperFirst } from "@leancodepl/utils"
266
+
267
+ const result = toUpperFirst("helloWorld")
268
+ // Result: 'HelloWorld'
269
+ ```
270
+
271
+ ### `downloadFile(dataOrUrl, options)`
272
+
273
+ Download a file from URL or Blob object.
274
+
275
+ **Parameters:**
276
+
277
+ - `url: string` OR `obj: Blob | MediaSource` - The URL to download from or the Blob/MediaSource object
278
+ - `options?: DownloadFileOptions` - Optional download options
279
+
280
+ **Usage:**
281
+
282
+ ```typescript
283
+ import { downloadFile } from "@leancodepl/utils"
284
+
285
+ // Download from URL
286
+ downloadFile("https://example.com/file.pdf", { name: "document.pdf" })
287
+
288
+ // Download from Blob
289
+ const blob = new Blob(["Hello World"], { type: "text/plain" })
290
+ downloadFile(blob, { name: "hello.txt" })
291
+ ```
292
+
293
+ ### `useDialog(onAfterClose)`
294
+
295
+ React hook for managing dialog state with optional callback after closing. Provides convenient open/close functions and
296
+ tracks the dialog's open state.
297
+
298
+ **Parameters:**
299
+
300
+ - `onAfterClose?: () => void` - Optional callback function to execute after the dialog closes
301
+
302
+ **Returns:** Object containing `{ isDialogOpen: boolean, openDialog: () => void, closeDialog: () => void }`
303
+
304
+ **Usage:**
305
+
306
+ ```typescript
307
+ import React from 'react';
308
+ import { useDialog } from '@leancodepl/utils';
309
+
310
+ function MyComponent() {
311
+ const { isDialogOpen, openDialog, closeDialog } = useDialog(() => {
312
+ console.log('Dialog closed');
313
+ });
314
+
315
+ return (
316
+ <div>
317
+ <button onClick={openDialog}>Open Dialog</button>
318
+ {isDialogOpen && (
319
+ <div className="dialog">
320
+ <p>Dialog content</p>
321
+ <button onClick={closeDialog}>Close</button>
322
+ </div>
323
+ )}
324
+ </div>
325
+ );
326
+ }
327
+ ```
328
+
329
+ ### `useRunInTask()`
330
+
331
+ React hook for tracking async task execution with loading state. Automatically manages a loading counter and provides a
332
+ wrapper function for tasks.
333
+
334
+ **Returns:** A tuple containing `[isLoading: boolean, runInTask: function]`
335
+
336
+ **Usage:**
337
+
338
+ ```typescript
339
+ import React from 'react';
340
+ import { useRunInTask } from '@leancodepl/utils';
341
+
342
+ async function saveData() {
343
+ console.log('Saving data...');
344
+ }
345
+
346
+ function MyComponent() {
347
+ const [isLoading, runInTask] = useRunInTask();
348
+
349
+ const handleSave = async () => {
350
+ await runInTask(async () => {
351
+ await saveData();
352
+ });
353
+ };
354
+
355
+ return (
356
+ <button onClick={handleSave} disabled={isLoading}>
357
+ {isLoading ? 'Saving...' : 'Save'}
358
+ </button>
359
+ );
360
+ }
361
+ ```
362
+
363
+ ### `useBoundRunInTask(block)`
364
+
365
+ React hook for bound task execution with loading state. Creates a wrapped version of a function that automatically
366
+ tracks loading state.
367
+
368
+ **Parameters:**
369
+
370
+ - `block: T | undefined` - The function to wrap with task tracking
371
+
372
+ **Returns:** A tuple containing `[isLoading: boolean, wrappedFunction: T]`
373
+
374
+ **Usage:**
375
+
376
+ ```typescript
377
+ import React, { useState, useEffect } from 'react';
378
+ import { useBoundRunInTask } from '@leancodepl/utils';
379
+
380
+ interface User {
381
+ name: string;
382
+ email: string;
383
+ }
384
+
385
+ async function fetchUser(userId: string): Promise<User> {
386
+ return { name: 'John Doe', email: 'john@example.com' };
387
+ }
388
+
389
+ function UserProfile({ userId }: { userId: string }) {
390
+ const [user, setUser] = useState<User | null>(null);
391
+
392
+ const [isLoading, loadUser] = useBoundRunInTask(async () => {
393
+ const userData = await fetchUser(userId);
394
+ setUser(userData);
395
+ });
396
+
397
+ useEffect(() => {
398
+ loadUser();
399
+ }, [userId, loadUser]);
400
+
401
+ if (isLoading) return <div>Loading...</div>;
402
+ return <div>{user?.name}</div>;
403
+ }
404
+ ```
405
+
406
+ ### `useKeyByRoute(routeMatches)`
407
+
408
+ React hook for generating keys based on current route matches.
409
+
410
+ **Parameters:**
411
+
412
+ - `routeMatches: Record<TKey, (object | null)[] | never | object | null>` - Record of route keys to match objects or
413
+ arrays
414
+
415
+ **Returns:** Array of active route keys
416
+
417
+ **Usage:**
418
+
419
+ ```typescript
420
+ import { useKeyByRoute } from '@leancodepl/utils';
421
+
422
+ function MyComponent() {
423
+ const routeKeys = useKeyByRoute({
424
+ home: { path: '/' },
425
+ profile: { path: '/profile' },
426
+ settings: null
427
+ });
428
+
429
+ return (
430
+ <div>
431
+ Active routes: {routeKeys.join(', ')}
432
+ </div>
433
+ );
434
+ }
435
+ ```
436
+
437
+ ### `useSetUnset(set)`
438
+
439
+ React hook for boolean state management helpers.
440
+
441
+ **Parameters:**
442
+
443
+ - `set: Dispatch<SetStateAction<boolean>>` - The state setter function from useState
444
+
445
+ **Returns:** A tuple containing `[setTrue: function, setFalse: function]`
446
+
447
+ **Usage:**
448
+
449
+ ```typescript
450
+ import React, { useState } from 'react';
451
+ import { useSetUnset } from '@leancodepl/utils';
452
+
453
+ function MyComponent() {
454
+ const [isVisible, setIsVisible] = useState(false);
455
+ const [show, hide] = useSetUnset(setIsVisible);
456
+
457
+ return (
458
+ <div>
459
+ <button onClick={show}>Show</button>
460
+ <button onClick={hide}>Hide</button>
461
+ {isVisible && <div>Visible content</div>}
462
+ </div>
463
+ );
464
+ }
465
+ ```
@@ -0,0 +1 @@
1
+ exports._default = require('./index.cjs.js').default;