bytekit 0.2.7 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -2288
- package/dist/cli/index.js +3 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/utils/core/RateLimiter.js +1 -1
- package/dist/utils/core/RateLimiter.js.map +1 -1
- package/package.json +208 -197
package/README.md
CHANGED
|
@@ -7,44 +7,17 @@
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## ✨ Highlights / Características
|
|
11
11
|
|
|
12
|
-
**EN:**
|
|
13
|
-
**
|
|
12
|
+
- ✅ **EN:** Fully ESM with `.d.ts` definitions. **ES:** Build 100% ESM con tipos listos.
|
|
13
|
+
- 🌐 **EN:** Works on Node.js 18+ and modern browsers (via `cross-fetch`). **ES:** Compatible con Node.js 18+ y navegadores modernos (usa `cross-fetch`).
|
|
14
|
+
- 🔁 **EN:** ApiClient with retries, localized errors, flexible options. **ES:** ApiClient con reintentos, errores localizados y configuración flexible.
|
|
15
|
+
- 🧩 **EN:** Helper modules (strings, dates, validators, env, storage). **ES:** Helpers para strings, fechas, validadores, env y storage.
|
|
16
|
+
- 🪵 **EN:** Structured logging/profiling: `createLogger`, `Profiler`, `withTiming`. **ES:** Logging/profiling estructurado: `createLogger`, `Profiler`, `withTiming`.
|
|
14
17
|
|
|
15
|
-
##
|
|
18
|
+
## 🚀 Quick Start / Inicio Rápido
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
- 🌐 **EN:** Works on Node.js 18+ and modern browsers (via `cross-fetch`). **ES:** Compatible con Node.js 18+ y navegadores modernos (usa `cross-fetch`).
|
|
19
|
-
- 🔁 **EN:** ApiClient with retries, localized errors, flexible options. **ES:** ApiClient con reintentos, errores localizados y configuración flexible.
|
|
20
|
-
- 🧩 **EN:** Helper modules (strings, dates, validators, env, storage). **ES:** Helpers para strings, fechas, validadores, env y storage.
|
|
21
|
-
- 🪵 **EN:** Structured logging/profiling: `createLogger`, `Profiler`, `withTiming`. **ES:** Logging/profiling estructurado: `createLogger`, `Profiler`, `withTiming`.
|
|
22
|
-
|
|
23
|
-
## Installation / Instalación
|
|
24
|
-
|
|
25
|
-
### Global Installation / Instalación Global
|
|
26
|
-
|
|
27
|
-
**EN:** Install the package globally to use the CLI tool (`sutils`) from anywhere.
|
|
28
|
-
**ES:** Instalá el paquete globalmente para usar la herramienta CLI (`sutils`) desde cualquier lugar.
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npm install -g bytekit
|
|
32
|
-
# or / o
|
|
33
|
-
pnpm add -g bytekit
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
**EN:** After global installation, you can use the `sutils` command:
|
|
37
|
-
**ES:** Después de la instalación global, podés usar el comando `sutils`:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
sutils create users
|
|
41
|
-
sutils types https://api.example.com/users
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### Project Installation / Instalación en Proyecto
|
|
45
|
-
|
|
46
|
-
**EN:** Install as a project dependency to use all utilities in your application.
|
|
47
|
-
**ES:** Instalá como dependencia del proyecto para usar todos los utilities en tu aplicación.
|
|
20
|
+
### Installation / Instalación
|
|
48
21
|
|
|
49
22
|
```bash
|
|
50
23
|
npm install bytekit
|
|
@@ -54,166 +27,16 @@ pnpm add bytekit
|
|
|
54
27
|
yarn add bytekit
|
|
55
28
|
```
|
|
56
29
|
|
|
57
|
-
###
|
|
58
|
-
|
|
59
|
-
**EN:** Import only the modules you need to reduce bundle size. Each utility can be imported individually.
|
|
60
|
-
**ES:** Importá solo los módulos que necesitás para reducir el tamaño del bundle. Cada utility se puede importar individualmente.
|
|
61
|
-
|
|
62
|
-
#### Core Modules / Módulos Core
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
// HTTP Client
|
|
66
|
-
import { ApiClient, createApiClient } from "bytekit/api-client";
|
|
67
|
-
|
|
68
|
-
// Retry & Circuit Breaker
|
|
69
|
-
import { RetryPolicy, CircuitBreaker } from "bytekit/retry-policy";
|
|
70
|
-
|
|
71
|
-
// Response Validation
|
|
72
|
-
import { ResponseValidator } from "bytekit/response-validator";
|
|
73
|
-
|
|
74
|
-
// Logging
|
|
75
|
-
import { Logger, createLogger } from "bytekit/logger";
|
|
76
|
-
|
|
77
|
-
// Profiling
|
|
78
|
-
import { Profiler } from "bytekit/profiler";
|
|
79
|
-
|
|
80
|
-
// Debug Utilities
|
|
81
|
-
import { createStopwatch, withTiming, measureAsync } from "bytekit/debug";
|
|
82
|
-
|
|
83
|
-
// Request Caching
|
|
84
|
-
import { RequestCache } from "bytekit/request-cache";
|
|
85
|
-
|
|
86
|
-
// Rate Limiting
|
|
87
|
-
import { RateLimiter, SlidingWindowRateLimiter } from "bytekit/rate-limiter";
|
|
88
|
-
|
|
89
|
-
// Request Deduplication
|
|
90
|
-
import { RequestDeduplicator } from "bytekit/request-deduplicator";
|
|
91
|
-
|
|
92
|
-
// Error Boundary
|
|
93
|
-
import { ErrorBoundary, getGlobalErrorBoundary } from "bytekit/error-boundary";
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
#### Helper Modules / Módulos Helpers
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
// Date Utilities
|
|
100
|
-
import { DateUtils } from "bytekit/date-utils";
|
|
101
|
-
|
|
102
|
-
// String Utilities
|
|
103
|
-
import { StringUtils } from "bytekit/string-utils";
|
|
104
|
-
|
|
105
|
-
// Validation
|
|
106
|
-
import { Validator } from "bytekit/validator";
|
|
107
|
-
|
|
108
|
-
// Environment Manager
|
|
109
|
-
import { EnvManager } from "bytekit/env-manager";
|
|
110
|
-
|
|
111
|
-
// Storage Utilities
|
|
112
|
-
import { StorageUtils } from "bytekit/storage-utils";
|
|
113
|
-
|
|
114
|
-
// File Upload
|
|
115
|
-
import { FileUploadHelper } from "bytekit/file-upload";
|
|
116
|
-
|
|
117
|
-
// Streaming
|
|
118
|
-
import { StreamingHelper } from "bytekit/streaming";
|
|
119
|
-
|
|
120
|
-
// WebSocket
|
|
121
|
-
import { WebSocketHelper } from "bytekit/websocket";
|
|
122
|
-
|
|
123
|
-
// Array Utilities
|
|
124
|
-
import { ArrayUtils } from "bytekit/array-utils";
|
|
125
|
-
|
|
126
|
-
// Object Utilities
|
|
127
|
-
import { ObjectUtils } from "bytekit/object-utils";
|
|
128
|
-
|
|
129
|
-
// Form Utilities
|
|
130
|
-
import { FormUtils, createForm } from "bytekit/form-utils";
|
|
131
|
-
|
|
132
|
-
// Time Utilities
|
|
133
|
-
import { TimeUtils } from "bytekit/time-utils";
|
|
134
|
-
|
|
135
|
-
// Event Emitter
|
|
136
|
-
import { EventEmitter, createEventEmitter } from "bytekit/event-emitter";
|
|
137
|
-
|
|
138
|
-
// Diff Utilities
|
|
139
|
-
import { DiffUtils } from "bytekit/diff-utils";
|
|
140
|
-
|
|
141
|
-
// Polling Helper
|
|
142
|
-
import { PollingHelper, createPoller } from "bytekit/polling-helper";
|
|
143
|
-
|
|
144
|
-
// Crypto Utilities
|
|
145
|
-
import { CryptoUtils } from "bytekit/crypto-utils";
|
|
146
|
-
|
|
147
|
-
// Pagination Helper
|
|
148
|
-
import { PaginationHelper, createPaginator } from "bytekit/pagination-helper";
|
|
149
|
-
|
|
150
|
-
// Cache Manager
|
|
151
|
-
import { CacheManager, createCacheManager } from "bytekit/cache-manager";
|
|
152
|
-
|
|
153
|
-
// Compression Utilities
|
|
154
|
-
import { CompressionUtils } from "bytekit/compression-utils";
|
|
155
|
-
|
|
156
|
-
// HTTP Status Helper
|
|
157
|
-
import { HTTP_STATUS, isSuccess, isRetryable } from "bytekit/http-status";
|
|
158
|
-
|
|
159
|
-
// URL Builder
|
|
160
|
-
import { UrlBuilder, createUrlBuilder } from "bytekit/url-builder";
|
|
161
|
-
|
|
162
|
-
// Batch Request
|
|
163
|
-
import { BatchRequest, createBatchRequest } from "bytekit/batch-request";
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
#### Import Everything / Importar Todo
|
|
167
|
-
|
|
168
|
-
**EN:** You can also import everything from the main entry point:
|
|
169
|
-
**ES:** También podés importar todo desde el punto de entrada principal:
|
|
30
|
+
### Global CLI Installation / Instalación CLI Global
|
|
170
31
|
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
Profiler,
|
|
177
|
-
RetryPolicy,
|
|
178
|
-
ResponseValidator,
|
|
179
|
-
RequestCache,
|
|
180
|
-
RateLimiter,
|
|
181
|
-
RequestDeduplicator,
|
|
182
|
-
ErrorBoundary,
|
|
183
|
-
|
|
184
|
-
// Helpers
|
|
185
|
-
DateUtils,
|
|
186
|
-
StringUtils,
|
|
187
|
-
Validator,
|
|
188
|
-
EnvManager,
|
|
189
|
-
StorageUtils,
|
|
190
|
-
FileUploadHelper,
|
|
191
|
-
StreamingHelper,
|
|
192
|
-
WebSocketHelper,
|
|
193
|
-
ArrayUtils,
|
|
194
|
-
ObjectUtils,
|
|
195
|
-
FormUtils,
|
|
196
|
-
TimeUtils,
|
|
197
|
-
EventEmitter,
|
|
198
|
-
DiffUtils,
|
|
199
|
-
PollingHelper,
|
|
200
|
-
CryptoUtils,
|
|
201
|
-
PaginationHelper,
|
|
202
|
-
CacheManager,
|
|
203
|
-
CompressionUtils,
|
|
204
|
-
|
|
205
|
-
// Factory functions
|
|
206
|
-
createLogger,
|
|
207
|
-
createApiClient,
|
|
208
|
-
createForm,
|
|
209
|
-
createEventEmitter,
|
|
210
|
-
createPoller,
|
|
211
|
-
createPaginator,
|
|
212
|
-
createCacheManager,
|
|
213
|
-
} from "bytekit";
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g bytekit
|
|
34
|
+
# Then use / Luego usa:
|
|
35
|
+
sutils create users
|
|
36
|
+
sutils types https://api.example.com/users
|
|
214
37
|
```
|
|
215
38
|
|
|
216
|
-
|
|
39
|
+
### Basic Usage / Uso Básico
|
|
217
40
|
|
|
218
41
|
```ts
|
|
219
42
|
import { ApiClient, createLogger, DateUtils, StringUtils } from "bytekit";
|
|
@@ -232,47 +55,39 @@ const users = await http.get<{ id: string; name: string }[]>("/users");
|
|
|
232
55
|
const logger = createLogger({ namespace: "users-service", level: "info" });
|
|
233
56
|
logger.info("Users synced", { count: users.length });
|
|
234
57
|
|
|
235
|
-
logger.debug("Next sync ETA (days)", {
|
|
236
|
-
etaDays: DateUtils.diffInDays(
|
|
237
|
-
new Date(),
|
|
238
|
-
DateUtils.add(new Date(), { days: 7 })
|
|
239
|
-
),
|
|
240
|
-
});
|
|
241
|
-
|
|
242
58
|
const slug = StringUtils.slugify("New Users – October 2024");
|
|
243
59
|
```
|
|
244
60
|
|
|
245
|
-
|
|
246
|
-
**ES:** Importá desde la raíz, configurá el ApiClient una sola vez y reutilizá los helpers en todos tus servicios.
|
|
247
|
-
|
|
248
|
-
## Framework Examples / Ejemplos con Frameworks
|
|
61
|
+
### Modular Imports / Importaciones Modulares
|
|
249
62
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
### 🎯 Works with / Compatible con
|
|
63
|
+
```ts
|
|
64
|
+
// Import specific modules to reduce bundle size
|
|
65
|
+
// Importa módulos específicos para reducir el tamaño del bundle
|
|
254
66
|
|
|
255
|
-
|
|
67
|
+
// Core modules / Módulos core
|
|
68
|
+
import { ApiClient } from "bytekit/api-client";
|
|
69
|
+
import { Logger } from "bytekit/logger";
|
|
70
|
+
import { RetryPolicy } from "bytekit/retry-policy";
|
|
256
71
|
|
|
257
|
-
|
|
72
|
+
// Helper modules / Módulos helpers
|
|
73
|
+
import { DateUtils } from "bytekit/date-utils";
|
|
74
|
+
import { StringUtils } from "bytekit/string-utils";
|
|
75
|
+
import { ArrayUtils } from "bytekit/array-utils";
|
|
76
|
+
```
|
|
258
77
|
|
|
259
|
-
|
|
78
|
+
## 🎯 Framework Support / Soporte de Frameworks
|
|
260
79
|
|
|
261
|
-
|
|
80
|
+
**EN:** Works seamlessly with React, Vue, Svelte, Angular, Next.js, Nuxt, SvelteKit, and more.
|
|
81
|
+
**ES:** Funciona perfectamente con React, Vue, Svelte, Angular, Next.js, Nuxt, SvelteKit y más.
|
|
262
82
|
|
|
263
|
-
|
|
83
|
+
### React Example / Ejemplo React
|
|
264
84
|
|
|
265
85
|
```jsx
|
|
266
86
|
import { createApiClient } from "bytekit";
|
|
267
87
|
import { useState, useEffect } from "react";
|
|
268
88
|
|
|
269
|
-
function useApiClient(config) {
|
|
270
|
-
const [client] = useState(() => createApiClient(config));
|
|
271
|
-
return client;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
89
|
function Users() {
|
|
275
|
-
const client =
|
|
90
|
+
const client = createApiClient({ baseURL: "https://api.example.com" });
|
|
276
91
|
const [users, setUsers] = useState([]);
|
|
277
92
|
|
|
278
93
|
useEffect(() => {
|
|
@@ -289,2106 +104,112 @@ function Users() {
|
|
|
289
104
|
}
|
|
290
105
|
```
|
|
291
106
|
|
|
292
|
-
**[📖
|
|
293
|
-
|
|
294
|
-
#### Vue 3
|
|
295
|
-
|
|
296
|
-
```vue
|
|
297
|
-
<script setup>
|
|
298
|
-
import { ref, onMounted } from "vue";
|
|
299
|
-
import { createApiClient } from "bytekit";
|
|
300
|
-
|
|
301
|
-
const client = createApiClient({ baseURL: "https://api.example.com" });
|
|
302
|
-
const users = ref([]);
|
|
303
|
-
|
|
304
|
-
onMounted(async () => {
|
|
305
|
-
users.value = await client.get("/users");
|
|
306
|
-
});
|
|
307
|
-
</script>
|
|
308
|
-
|
|
309
|
-
<template>
|
|
310
|
-
<div v-for="user in users" :key="user.id">{{ user.name }}</div>
|
|
311
|
-
</template>
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
**[📖 Full Vue Guide](./docs/examples/vue.md)** • **[💻 Working Example](./examples/vue-app)** • **[🚀 Try on CodeSandbox](https://codesandbox.io/p/devbox/df26fs)**
|
|
315
|
-
|
|
316
|
-
#### Svelte
|
|
317
|
-
|
|
318
|
-
```svelte
|
|
319
|
-
<script>
|
|
320
|
-
import { onMount } from 'svelte';
|
|
321
|
-
import { createApiClient } from 'bytekit';
|
|
322
|
-
|
|
323
|
-
const client = createApiClient({ baseURL: 'https://api.example.com' });
|
|
324
|
-
let users = [];
|
|
325
|
-
|
|
326
|
-
onMount(async () => {
|
|
327
|
-
users = await client.get('/users');
|
|
328
|
-
});
|
|
329
|
-
</script>
|
|
330
|
-
|
|
331
|
-
{#each users as user}
|
|
332
|
-
<div>{user.name}</div>
|
|
333
|
-
{/each}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
**[📖 Full Svelte Guide](./docs/examples/svelte.md)** • **[💻 Working Example](./examples/svelte-app)** • **[🚀 Try on CodeSandbox](https://codesandbox.io/p/devbox/lxvghg)**
|
|
337
|
-
|
|
338
|
-
### 🚀 Try the Examples / Probá los Ejemplos
|
|
339
|
-
|
|
340
|
-
**EN:** Each example is a standalone Vite app ready to run:
|
|
341
|
-
**ES:** Cada ejemplo es una app Vite lista para ejecutar:
|
|
342
|
-
|
|
343
|
-
```bash
|
|
344
|
-
cd examples/react-app # or vue-app, svelte-app
|
|
345
|
-
npm install
|
|
346
|
-
npm run dev
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
**[📁 View all examples](./examples)**
|
|
350
|
-
|
|
351
|
-
## API surface / Métodos expuestos
|
|
352
|
-
|
|
353
|
-
**EN:** Complete reference of all exported methods and classes. Use `npm info bytekit` to see the full list.
|
|
354
|
-
**ES:** Referencia completa de todos los métodos y clases exportados. Usa `npm info bytekit` para ver la lista completa.
|
|
355
|
-
|
|
356
|
-
### Core Modules / Módulos Core
|
|
357
|
-
|
|
358
|
-
#### ApiClient
|
|
359
|
-
|
|
360
|
-
```ts
|
|
361
|
-
class ApiClient {
|
|
362
|
-
get<T>(url: string, options?: RequestOptions): Promise<T>;
|
|
363
|
-
post<T>(url: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
364
|
-
put<T>(url: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
365
|
-
patch<T>(url: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
366
|
-
delete<T>(url: string, options?: RequestOptions): Promise<T>;
|
|
367
|
-
getList<T>(
|
|
368
|
-
url: string,
|
|
369
|
-
options?: GetListOptions
|
|
370
|
-
): Promise<PaginatedResponse<T>>;
|
|
371
|
-
request<T>(
|
|
372
|
-
method: string,
|
|
373
|
-
url: string,
|
|
374
|
-
options?: RequestOptions
|
|
375
|
-
): Promise<T>;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
function createApiClient(config: ApiClientConfig): ApiClient;
|
|
379
|
-
class HttpError extends Error {
|
|
380
|
-
status: number;
|
|
381
|
-
body: unknown;
|
|
382
|
-
}
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
#### Logger
|
|
386
|
-
|
|
387
|
-
```ts
|
|
388
|
-
class Logger {
|
|
389
|
-
setLevel(level: LogLevel): void;
|
|
390
|
-
child(namespace: string): Logger;
|
|
391
|
-
debug(message: string, data?: unknown): void;
|
|
392
|
-
info(message: string, data?: unknown): void;
|
|
393
|
-
warn(message: string, data?: unknown): void;
|
|
394
|
-
error(message: string, data?: unknown): void;
|
|
395
|
-
log(level: LogLevel, message: string, data?: unknown): void;
|
|
396
|
-
silent(): void;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function createLogger(config: LoggerConfig): Logger;
|
|
400
|
-
const consoleTransportNode: Transport;
|
|
401
|
-
const consoleTransportBrowser: Transport;
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
#### Profiler
|
|
405
|
-
|
|
406
|
-
```ts
|
|
407
|
-
class Profiler {
|
|
408
|
-
start(label: string): void;
|
|
409
|
-
end(label: string): number;
|
|
410
|
-
summary(): ProfilerSummary[];
|
|
411
|
-
}
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
#### Debug Utilities
|
|
415
|
-
|
|
416
|
-
```ts
|
|
417
|
-
function createStopwatch(options?: StopwatchOptions): Stopwatch;
|
|
418
|
-
interface Stopwatch {
|
|
419
|
-
stop(): number;
|
|
420
|
-
elapsed(): number;
|
|
421
|
-
log(data?: unknown): void;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
async function withTiming<T>(label: string, fn: () => Promise<T>): Promise<T>;
|
|
425
|
-
function measureSync<T>(
|
|
426
|
-
label: string,
|
|
427
|
-
fn: () => T
|
|
428
|
-
): { result: T; durationMs: number };
|
|
429
|
-
async function measureAsync<T>(
|
|
430
|
-
label: string,
|
|
431
|
-
fn: () => Promise<T>
|
|
432
|
-
): Promise<{ result: T; durationMs: number }>;
|
|
433
|
-
function captureDebug(label: string, data?: unknown): void;
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
#### RetryPolicy
|
|
437
|
-
|
|
438
|
-
```ts
|
|
439
|
-
class RetryPolicy {
|
|
440
|
-
constructor(config: RetryPolicyConfig);
|
|
441
|
-
async execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
442
|
-
getAttempts(): number;
|
|
443
|
-
getRemainingAttempts(): number;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
class CircuitBreaker {
|
|
447
|
-
constructor(config: CircuitBreakerConfig);
|
|
448
|
-
async execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
449
|
-
getState(): CircuitBreakerState;
|
|
450
|
-
reset(): void;
|
|
451
|
-
}
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
#### ResponseValidator
|
|
455
|
-
|
|
456
|
-
```ts
|
|
457
|
-
class ResponseValidator {
|
|
458
|
-
static validate(data: unknown, schema: ValidationSchema): ValidationResult;
|
|
459
|
-
static validateArray(
|
|
460
|
-
data: unknown[],
|
|
461
|
-
schema: ValidationSchema
|
|
462
|
-
): ValidationResult;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
interface ValidationSchema {
|
|
466
|
-
type: string;
|
|
467
|
-
properties?: Record<string, ValidationSchema>;
|
|
468
|
-
required?: string[];
|
|
469
|
-
pattern?: RegExp;
|
|
470
|
-
minimum?: number;
|
|
471
|
-
maximum?: number;
|
|
472
|
-
minLength?: number;
|
|
473
|
-
maxLength?: number;
|
|
474
|
-
}
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
#### RequestCache
|
|
478
|
-
|
|
479
|
-
```ts
|
|
480
|
-
class RequestCache {
|
|
481
|
-
set(key: string, value: unknown, ttl?: number): void;
|
|
482
|
-
get<T>(key: string): T | null;
|
|
483
|
-
has(key: string): boolean;
|
|
484
|
-
remove(key: string): void;
|
|
485
|
-
clear(): void;
|
|
486
|
-
invalidate(pattern: string): void;
|
|
487
|
-
invalidatePattern(pattern: string): void;
|
|
488
|
-
getStats(): CacheStats;
|
|
489
|
-
}
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
#### RateLimiter
|
|
493
|
-
|
|
494
|
-
```ts
|
|
495
|
-
class RateLimiter {
|
|
496
|
-
isAllowed(key: string): boolean;
|
|
497
|
-
async waitForAllowance(key: string): Promise<void>;
|
|
498
|
-
getStats(key: string): RateLimiterStats;
|
|
499
|
-
reset(key?: string): void;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
class SlidingWindowRateLimiter {
|
|
503
|
-
isAllowed(key: string): boolean;
|
|
504
|
-
async waitForAllowance(key: string): Promise<void>;
|
|
505
|
-
getStats(key: string): RateLimiterStats;
|
|
506
|
-
reset(key?: string): void;
|
|
507
|
-
}
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
#### RequestDeduplicator
|
|
511
|
-
|
|
512
|
-
```ts
|
|
513
|
-
class RequestDeduplicator {
|
|
514
|
-
async execute<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
515
|
-
getStats(): DeduplicatorStats;
|
|
516
|
-
getInFlightCount(): number;
|
|
517
|
-
clear(): void;
|
|
518
|
-
}
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
#### ErrorBoundary
|
|
522
|
-
|
|
523
|
-
```ts
|
|
524
|
-
class ErrorBoundary {
|
|
525
|
-
async execute<T>(fn: () => Promise<T>, context?: ErrorContext): Promise<T>;
|
|
526
|
-
executeSync<T>(fn: () => T, context?: ErrorContext): T;
|
|
527
|
-
wrap<T extends (...args: unknown[]) => Promise<unknown>>(fn: T): T;
|
|
528
|
-
wrapSync<T extends (...args: unknown[]) => unknown>(fn: T): T;
|
|
529
|
-
addHandler(handler: ErrorHandler): void;
|
|
530
|
-
getErrorHistory(limit?: number): ErrorEntry[];
|
|
531
|
-
createErrorReport(): ErrorReport;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
function getGlobalErrorBoundary(config?: ErrorBoundaryConfig): ErrorBoundary;
|
|
535
|
-
|
|
536
|
-
class AppError extends Error {
|
|
537
|
-
code: string;
|
|
538
|
-
context?: Record<string, unknown>;
|
|
539
|
-
}
|
|
540
|
-
class AppValidationError extends AppError {}
|
|
541
|
-
class NotFoundError extends AppError {}
|
|
542
|
-
class TimeoutError extends AppError {}
|
|
543
|
-
class RateLimitError extends AppError {
|
|
544
|
-
retryAfter?: number;
|
|
545
|
-
}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
### Helper Modules / Módulos Helpers
|
|
549
|
-
|
|
550
|
-
#### DateUtils
|
|
551
|
-
|
|
552
|
-
```ts
|
|
553
|
-
class DateUtils {
|
|
554
|
-
static parse(date: Date | string | number): Date;
|
|
555
|
-
static isValid(date: unknown): boolean;
|
|
556
|
-
static toISODate(date: Date | string): string;
|
|
557
|
-
static startOfDay(date: Date | string): Date;
|
|
558
|
-
static endOfDay(date: Date | string): Date;
|
|
559
|
-
static add(date: Date | string, duration: DateDuration): Date;
|
|
560
|
-
static diff(
|
|
561
|
-
from: Date | string,
|
|
562
|
-
to: Date | string,
|
|
563
|
-
options?: DiffOptions
|
|
564
|
-
): number;
|
|
565
|
-
static diffInDays(
|
|
566
|
-
from: Date | string,
|
|
567
|
-
to: Date | string,
|
|
568
|
-
options?: DiffOptions
|
|
569
|
-
): number;
|
|
570
|
-
static isSameDay(date1: Date | string, date2: Date | string): boolean;
|
|
571
|
-
static isBefore(date1: Date | string, date2: Date | string): boolean;
|
|
572
|
-
static isAfter(date1: Date | string, date2: Date | string): boolean;
|
|
573
|
-
static format(date: Date | string, locale?: string): string;
|
|
574
|
-
}
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
#### StringUtils
|
|
578
|
-
|
|
579
|
-
```ts
|
|
580
|
-
class StringUtils {
|
|
581
|
-
static removeDiacritics(str: string): string;
|
|
582
|
-
static slugify(str: string, options?: SlugifyOptions): string;
|
|
583
|
-
static compactWhitespace(str: string): string;
|
|
584
|
-
static capitalize(str: string): string;
|
|
585
|
-
static capitalizeWords(str: string): string;
|
|
586
|
-
static truncate(
|
|
587
|
-
str: string,
|
|
588
|
-
length: number,
|
|
589
|
-
options?: TruncateOptions
|
|
590
|
-
): string;
|
|
591
|
-
static mask(str: string, options?: MaskOptions): string;
|
|
592
|
-
static interpolate(
|
|
593
|
-
template: string,
|
|
594
|
-
values: Record<string, unknown>,
|
|
595
|
-
options?: InterpolateOptions
|
|
596
|
-
): string;
|
|
597
|
-
static initials(str: string, limit?: number): string;
|
|
598
|
-
static toQueryString(
|
|
599
|
-
obj: Record<string, unknown>,
|
|
600
|
-
options?: QueryStringOptions
|
|
601
|
-
): string;
|
|
602
|
-
}
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
#### Validator
|
|
606
|
-
|
|
607
|
-
```ts
|
|
608
|
-
class Validator {
|
|
609
|
-
static isEmail(email: string): boolean;
|
|
610
|
-
static isEmpty(value: unknown): boolean;
|
|
611
|
-
static minLength(value: string, min: number): boolean;
|
|
612
|
-
static maxLength(value: string, max: number): boolean;
|
|
613
|
-
static matches(value: string, pattern: RegExp): boolean;
|
|
614
|
-
static isUrl(url: string): boolean;
|
|
615
|
-
static isInternationalPhone(phone: string): boolean;
|
|
616
|
-
static isPhoneE164(phone: string): boolean;
|
|
617
|
-
static isUUIDv4(uuid: string): boolean;
|
|
618
|
-
static isLocalPhone(phone: string, locale?: string): boolean;
|
|
619
|
-
static isDni(dni: string, locale?: string): boolean;
|
|
620
|
-
static isCuit(cuit: string): boolean;
|
|
621
|
-
static isCbu(cbu: string): boolean;
|
|
622
|
-
static isStrongPassword(
|
|
623
|
-
password: string,
|
|
624
|
-
options?: PasswordOptions
|
|
625
|
-
): boolean;
|
|
626
|
-
static isDateRange(
|
|
627
|
-
date: Date | string,
|
|
628
|
-
from: Date | string,
|
|
629
|
-
to: Date | string
|
|
630
|
-
): boolean;
|
|
631
|
-
static isOneTimeCode(code: string, length?: number): boolean;
|
|
632
|
-
}
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
#### EnvManager
|
|
636
|
-
|
|
637
|
-
```ts
|
|
638
|
-
class EnvManager {
|
|
639
|
-
get(key: string, defaultValue?: string): string | undefined;
|
|
640
|
-
require(key: string): string;
|
|
641
|
-
isProd(): boolean;
|
|
642
|
-
isDev(): boolean;
|
|
643
|
-
}
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
#### StorageUtils
|
|
647
|
-
|
|
648
|
-
```ts
|
|
649
|
-
class StorageUtils {
|
|
650
|
-
constructor(storage?: Storage);
|
|
651
|
-
set<T>(key: string, value: T, ttl?: number): void;
|
|
652
|
-
get<T>(key: string): T | null;
|
|
653
|
-
remove(key: string): void;
|
|
654
|
-
clear(): void;
|
|
655
|
-
has(key: string): boolean;
|
|
656
|
-
}
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
#### FileUploadHelper
|
|
660
|
-
|
|
661
|
-
```ts
|
|
662
|
-
class FileUploadHelper {
|
|
663
|
-
static validateFile(
|
|
664
|
-
file: File,
|
|
665
|
-
options?: FileValidationOptions
|
|
666
|
-
): FileValidationResult;
|
|
667
|
-
static async uploadFile(
|
|
668
|
-
file: File,
|
|
669
|
-
url: string,
|
|
670
|
-
options?: UploadOptions
|
|
671
|
-
): Promise<UploadResponse>;
|
|
672
|
-
static async uploadChunked(
|
|
673
|
-
file: File,
|
|
674
|
-
url: string,
|
|
675
|
-
options?: ChunkedUploadOptions
|
|
676
|
-
): Promise<UploadResponse>;
|
|
677
|
-
}
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
#### StreamingHelper
|
|
681
|
-
|
|
682
|
-
```ts
|
|
683
|
-
class StreamingHelper {
|
|
684
|
-
static async streamJsonLines<T>(
|
|
685
|
-
url: string,
|
|
686
|
-
options?: StreamOptions<T>
|
|
687
|
-
): Promise<StreamResult<T>>;
|
|
688
|
-
static streamSSE<T>(
|
|
689
|
-
url: string,
|
|
690
|
-
options?: SSEOptions<T>
|
|
691
|
-
): SSESubscription<T>;
|
|
692
|
-
static async downloadStream(
|
|
693
|
-
url: string,
|
|
694
|
-
options?: DownloadOptions
|
|
695
|
-
): Promise<Blob>;
|
|
696
|
-
}
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
#### WebSocketHelper
|
|
700
|
-
|
|
701
|
-
```ts
|
|
702
|
-
class WebSocketHelper {
|
|
703
|
-
constructor(url: string, options?: WebSocketOptions);
|
|
704
|
-
async connect(): Promise<void>;
|
|
705
|
-
on<T>(event: string, listener: (data: T) => void): void;
|
|
706
|
-
once<T>(event: string, listener: (data: T) => void): void;
|
|
707
|
-
off(event: string, listener: Function): void;
|
|
708
|
-
send<T>(event: string, data: T): void;
|
|
709
|
-
async request<Req, Res>(event: string, data: Req): Promise<Res>;
|
|
710
|
-
onError(listener: (error: Error) => void): void;
|
|
711
|
-
close(): void;
|
|
712
|
-
isConnected(): boolean;
|
|
713
|
-
}
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
#### ArrayUtils
|
|
717
|
-
|
|
718
|
-
```ts
|
|
719
|
-
class ArrayUtils {
|
|
720
|
-
static chunk<T>(array: T[], size: number): T[][];
|
|
721
|
-
static flatten<T>(array: unknown[], depth?: number): T[];
|
|
722
|
-
static unique<T>(array: T[], by?: (item: T) => unknown): T[];
|
|
723
|
-
static shuffle<T>(array: T[]): T[];
|
|
724
|
-
static random<T>(array: T[]): T;
|
|
725
|
-
static randomN<T>(array: T[], n: number): T[];
|
|
726
|
-
static zip<T>(...arrays: T[][]): T[][];
|
|
727
|
-
static unzip<T>(array: T[][]): T[][];
|
|
728
|
-
static difference<T>(array1: T[], array2: T[]): T[];
|
|
729
|
-
static intersection<T>(array1: T[], array2: T[]): T[];
|
|
730
|
-
static union<T>(array1: T[], array2: T[]): T[];
|
|
731
|
-
static partition<T>(
|
|
732
|
-
array: T[],
|
|
733
|
-
predicate: (item: T) => boolean
|
|
734
|
-
): [T[], T[]];
|
|
735
|
-
static sum(array: number[]): number;
|
|
736
|
-
static average(array: number[]): number;
|
|
737
|
-
static min(array: number[]): number;
|
|
738
|
-
static max(array: number[]): number;
|
|
739
|
-
static range(start: number, end: number, step?: number): number[];
|
|
740
|
-
static rotate<T>(array: T[], steps: number): T[];
|
|
741
|
-
static transpose<T>(array: T[][]): T[][];
|
|
742
|
-
}
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
#### ObjectUtils
|
|
746
|
-
|
|
747
|
-
```ts
|
|
748
|
-
class ObjectUtils {
|
|
749
|
-
static isEmpty(obj: unknown): boolean;
|
|
750
|
-
static deepClone<T>(obj: T): T;
|
|
751
|
-
static merge<T>(...objects: Partial<T>[]): T;
|
|
752
|
-
static deepMerge<T>(...objects: Partial<T>[]): T;
|
|
753
|
-
static pick<T>(obj: T, keys: (keyof T)[]): Partial<T>;
|
|
754
|
-
static omit<T>(obj: T, keys: (keyof T)[]): Partial<T>;
|
|
755
|
-
static get<T>(obj: unknown, path: string): T | undefined;
|
|
756
|
-
static set<T>(obj: T, path: string, value: unknown): T;
|
|
757
|
-
static flatten<T>(obj: T, prefix?: string): Record<string, unknown>;
|
|
758
|
-
static unflatten(obj: Record<string, unknown>): Record<string, unknown>;
|
|
759
|
-
static filter<T>(
|
|
760
|
-
obj: T,
|
|
761
|
-
predicate: (key: string, value: unknown) => boolean
|
|
762
|
-
): Partial<T>;
|
|
763
|
-
static mapValues<T>(obj: T, mapper: (value: unknown) => unknown): T;
|
|
764
|
-
static hasKeys<T>(obj: T, keys: (keyof T)[]): boolean;
|
|
765
|
-
static invert<T>(obj: Record<string, T>): Record<T, string>;
|
|
766
|
-
static groupBy<T>(array: T[], key: keyof T): Record<string, T[]>;
|
|
767
|
-
static indexBy<T>(array: T[], key: keyof T): Record<string, T>;
|
|
768
|
-
static deepEqual(obj1: unknown, obj2: unknown): boolean;
|
|
769
|
-
static size(obj: Record<string, unknown>): number;
|
|
770
|
-
static entries<T>(obj: T): [string, unknown][];
|
|
771
|
-
static fromEntries(entries: [string, unknown][]): Record<string, unknown>;
|
|
772
|
-
static fromKeys<T>(keys: string[], value: T): Record<string, T>;
|
|
773
|
-
}
|
|
774
|
-
```
|
|
775
|
-
|
|
776
|
-
#### FormUtils
|
|
777
|
-
|
|
778
|
-
```ts
|
|
779
|
-
class FormUtils {
|
|
780
|
-
constructor(config: FormConfig);
|
|
781
|
-
setValue(field: string, value: unknown): void;
|
|
782
|
-
getValue(field: string): unknown;
|
|
783
|
-
getFieldError(field: string): string;
|
|
784
|
-
touchField(field: string): void;
|
|
785
|
-
isTouched(field: string): boolean;
|
|
786
|
-
isDirty(field: string): boolean;
|
|
787
|
-
async validateField(field: string): Promise<string | null>;
|
|
788
|
-
async validate(): Promise<Record<string, string>>;
|
|
789
|
-
async submit(): Promise<boolean>;
|
|
790
|
-
getState(): FormState;
|
|
791
|
-
createBinding(field: string): FieldBinding;
|
|
792
|
-
reset(): void;
|
|
793
|
-
serialize(): Record<string, unknown>;
|
|
794
|
-
deserialize(data: Record<string, unknown>): void;
|
|
795
|
-
}
|
|
107
|
+
**[📖 View More Framework Examples →](https://github.com/sebamar88/bytekit/wiki/Framework-Examples)**
|
|
796
108
|
|
|
797
|
-
|
|
109
|
+
## 📚 Complete Documentation / Documentación Completa
|
|
798
110
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
static email(value: string): boolean;
|
|
802
|
-
static minLength(value: string, min: number): boolean;
|
|
803
|
-
static maxLength(value: string, max: number): boolean;
|
|
804
|
-
static pattern(value: string, pattern: RegExp): boolean;
|
|
805
|
-
static url(value: string): boolean;
|
|
806
|
-
static match(value: string, other: string): boolean;
|
|
807
|
-
}
|
|
808
|
-
```
|
|
111
|
+
**EN:** For detailed documentation of all 28 modules, visit our comprehensive GitHub Wiki.
|
|
112
|
+
**ES:** Para documentación detallada de todos los 28 módulos, visita nuestra GitHub Wiki completa.
|
|
809
113
|
|
|
810
|
-
|
|
114
|
+
### 🔗 Quick Links by Category / Enlaces Rápidos por Categoría
|
|
811
115
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
delay: number
|
|
823
|
-
): T;
|
|
824
|
-
static timeout<T>(promise: Promise<T>, ms: number): Promise<T>;
|
|
825
|
-
static retry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
826
|
-
}
|
|
827
|
-
```
|
|
116
|
+
#### 🔧 Core Modules (9) - Essential functionality / Funcionalidad esencial
|
|
117
|
+
- **[ApiClient](https://github.com/sebamar88/bytekit/wiki/ApiClient)** - Typed HTTP client with retries, localized errors, and custom fetch support
|
|
118
|
+
- **[Logger](https://github.com/sebamar88/bytekit/wiki/Logger)** - Structured logger with levels, namespaces, and transports for Node/browser
|
|
119
|
+
- **[Profiler](https://github.com/sebamar88/bytekit/wiki/Profiler)** - Profiler utilities and helpers
|
|
120
|
+
- **[RetryPolicy](https://github.com/sebamar88/bytekit/wiki/RetryPolicy)** - RetryPolicy utilities and helpers
|
|
121
|
+
- **[ResponseValidator](https://github.com/sebamar88/bytekit/wiki/ResponseValidator)** - ResponseValidator utilities and helpers
|
|
122
|
+
- **[RequestCache](https://github.com/sebamar88/bytekit/wiki/RequestCache)** - RequestCache utilities and helpers
|
|
123
|
+
- **[RateLimiter](https://github.com/sebamar88/bytekit/wiki/RateLimiter)** - RateLimiter utilities and helpers
|
|
124
|
+
- **[RequestDeduplicator](https://github.com/sebamar88/bytekit/wiki/RequestDeduplicator)** - RequestDeduplicator utilities and helpers
|
|
125
|
+
- **[ErrorBoundary](https://github.com/sebamar88/bytekit/wiki/ErrorBoundary)** - ErrorBoundary utilities and helpers
|
|
828
126
|
|
|
829
|
-
####
|
|
127
|
+
#### 🛠️ Helper Modules (12) - Common utilities / Utilidades comunes
|
|
128
|
+
- **[DateUtils](https://github.com/sebamar88/bytekit/wiki/DateUtils)** - Safe date parsing, manipulation, and formatting utilities
|
|
129
|
+
- **[StringUtils](https://github.com/sebamar88/bytekit/wiki/StringUtils)** - Text processing utilities: slugify, capitalize, mask, interpolate
|
|
130
|
+
- **[Validator](https://github.com/sebamar88/bytekit/wiki/Validator)** - Validation utilities for emails, phones, passwords, and more
|
|
131
|
+
- **[EnvManager](https://github.com/sebamar88/bytekit/wiki/EnvManager)** - EnvManager utilities and helpers
|
|
132
|
+
- **[StorageUtils](https://github.com/sebamar88/bytekit/wiki/StorageUtils)** - StorageUtils utilities and helpers
|
|
133
|
+
- **[FileUploadHelper](https://github.com/sebamar88/bytekit/wiki/FileUploadHelper)** - FileUploadHelper utilities and helpers
|
|
134
|
+
- **[StreamingHelper](https://github.com/sebamar88/bytekit/wiki/StreamingHelper)** - StreamingHelper utilities and helpers
|
|
135
|
+
- **[WebSocketHelper](https://github.com/sebamar88/bytekit/wiki/WebSocketHelper)** - WebSocketHelper utilities and helpers
|
|
136
|
+
- **[ArrayUtils](https://github.com/sebamar88/bytekit/wiki/ArrayUtils)** - Array manipulation utilities: chunk, flatten, unique, shuffle, zip
|
|
137
|
+
- **[ObjectUtils](https://github.com/sebamar88/bytekit/wiki/ObjectUtils)** - Object manipulation utilities: merge, pick, omit, flatten, groupBy
|
|
138
|
+
- **[FormUtils](https://github.com/sebamar88/bytekit/wiki/FormUtils)** - FormUtils utilities and helpers
|
|
139
|
+
- **[TimeUtils](https://github.com/sebamar88/bytekit/wiki/TimeUtils)** - TimeUtils utilities and helpers
|
|
830
140
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
once<K extends keyof Events>(
|
|
840
|
-
event: K,
|
|
841
|
-
listener: EventListener<Events[K]>
|
|
842
|
-
): this;
|
|
843
|
-
off<K extends keyof Events>(
|
|
844
|
-
event: K,
|
|
845
|
-
listener: EventListener<Events[K]>
|
|
846
|
-
): this;
|
|
847
|
-
removeAllListeners<K extends keyof Events>(event?: K): this;
|
|
848
|
-
async emit<K extends keyof Events>(
|
|
849
|
-
event: K,
|
|
850
|
-
data: Events[K]
|
|
851
|
-
): Promise<boolean>;
|
|
852
|
-
emitSync<K extends keyof Events>(event: K, data: Events[K]): boolean;
|
|
853
|
-
onError(listener: EventListenerWithError): this;
|
|
854
|
-
listenerCount<K extends keyof Events>(event: K): number;
|
|
855
|
-
getListeners<K extends keyof Events>(event: K): EventListener<Events[K]>[];
|
|
856
|
-
eventNames(): (keyof Events)[];
|
|
857
|
-
setMaxListeners(n: number): this;
|
|
858
|
-
getMaxListeners(): number;
|
|
859
|
-
}
|
|
141
|
+
#### ⚡ Utility Modules (7) - Advanced features / Características avanzadas
|
|
142
|
+
- **[EventEmitter](https://github.com/sebamar88/bytekit/wiki/EventEmitter)** - EventEmitter utilities and helpers
|
|
143
|
+
- **[DiffUtils](https://github.com/sebamar88/bytekit/wiki/DiffUtils)** - DiffUtils utilities and helpers
|
|
144
|
+
- **[PollingHelper](https://github.com/sebamar88/bytekit/wiki/PollingHelper)** - PollingHelper utilities and helpers
|
|
145
|
+
- **[CryptoUtils](https://github.com/sebamar88/bytekit/wiki/CryptoUtils)** - Token/UUID generation, base64 encoding, hashing, and HMAC
|
|
146
|
+
- **[PaginationHelper](https://github.com/sebamar88/bytekit/wiki/PaginationHelper)** - PaginationHelper utilities and helpers
|
|
147
|
+
- **[CacheManager](https://github.com/sebamar88/bytekit/wiki/CacheManager)** - Multi-tier cache with TTL, LRU eviction, and statistics
|
|
148
|
+
- **[CompressionUtils](https://github.com/sebamar88/bytekit/wiki/CompressionUtils)** - CompressionUtils utilities and helpers
|
|
860
149
|
|
|
861
|
-
|
|
862
|
-
Events extends Record<string, unknown> = Record<string, unknown>
|
|
863
|
-
>(options?: EventEmitterOptions): EventEmitter<Events>;
|
|
864
|
-
```
|
|
150
|
+
**[🏠 Browse Full Wiki Index →](https://github.com/sebamar88/bytekit/wiki)**
|
|
865
151
|
|
|
866
|
-
|
|
152
|
+
## 🌟 Popular Use Cases / Casos de Uso Populares
|
|
867
153
|
|
|
154
|
+
### HTTP Client with Retries / Cliente HTTP con Reintentos
|
|
868
155
|
```ts
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
static createPatch(
|
|
875
|
-
old: Record<string, unknown>,
|
|
876
|
-
new_: Record<string, unknown>
|
|
877
|
-
): Patch[];
|
|
878
|
-
static applyPatch<T>(obj: T, patches: Patch[]): T;
|
|
879
|
-
static deepEqual(obj1: unknown, obj2: unknown): boolean;
|
|
880
|
-
}
|
|
156
|
+
const api = new ApiClient({
|
|
157
|
+
baseUrl: "https://api.example.com",
|
|
158
|
+
retryPolicy: { maxAttempts: 3, initialDelayMs: 100 },
|
|
159
|
+
circuitBreaker: { failureThreshold: 5 }
|
|
160
|
+
});
|
|
881
161
|
|
|
882
|
-
|
|
883
|
-
op: "add" | "remove" | "replace";
|
|
884
|
-
path: string;
|
|
885
|
-
value?: unknown;
|
|
886
|
-
}
|
|
162
|
+
const users = await api.get("/users");
|
|
887
163
|
```
|
|
888
164
|
|
|
889
|
-
|
|
890
|
-
|
|
165
|
+
### Structured Logging / Logging Estructurado
|
|
891
166
|
```ts
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
async start(): Promise<PollingResult>;
|
|
895
|
-
stop(): void;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
function createPoller(
|
|
899
|
-
fn: () => Promise<unknown>,
|
|
900
|
-
options?: PollingOptions
|
|
901
|
-
): PollingHelper;
|
|
902
|
-
|
|
903
|
-
interface PollingResult {
|
|
904
|
-
success: boolean;
|
|
905
|
-
attempts: number;
|
|
906
|
-
lastResult: unknown;
|
|
907
|
-
totalTimeMs: number;
|
|
908
|
-
}
|
|
167
|
+
const logger = createLogger({ namespace: "app", level: "info" });
|
|
168
|
+
logger.info("User created", { userId: 123, email: "user@example.com" });
|
|
909
169
|
```
|
|
910
170
|
|
|
911
|
-
|
|
912
|
-
|
|
171
|
+
### Date & String Utilities / Utilidades de Fecha y String
|
|
913
172
|
```ts
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
static base64Encode(str: string): string;
|
|
918
|
-
static base64Decode(str: string): string;
|
|
919
|
-
static base64UrlEncode(str: string): string;
|
|
920
|
-
static base64UrlDecode(str: string): string;
|
|
921
|
-
static async hash(str: string): Promise<string>;
|
|
922
|
-
static async verifyHash(str: string, hash: string): Promise<boolean>;
|
|
923
|
-
static constantTimeCompare(a: string, b: string): boolean;
|
|
924
|
-
static async hmac(message: string, secret: string): Promise<string>;
|
|
925
|
-
}
|
|
173
|
+
const formatted = DateUtils.format(new Date(), "es-AR");
|
|
174
|
+
const slug = StringUtils.slugify("Hello World! 🌍");
|
|
175
|
+
const masked = StringUtils.mask("1234567890", { start: 4, end: 2 });
|
|
926
176
|
```
|
|
927
177
|
|
|
928
|
-
|
|
929
|
-
|
|
178
|
+
### Array & Object Manipulation / Manipulación de Arrays y Objetos
|
|
930
179
|
```ts
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
next(): void;
|
|
935
|
-
previous(): void;
|
|
936
|
-
goToPage(page: number): void;
|
|
937
|
-
getState(): PaginationState;
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
function createPaginator(
|
|
941
|
-
items: unknown[],
|
|
942
|
-
options?: PaginationOptions
|
|
943
|
-
): PaginationHelper;
|
|
944
|
-
|
|
945
|
-
interface PaginationState {
|
|
946
|
-
currentPage: number;
|
|
947
|
-
pageSize: number;
|
|
948
|
-
total: number;
|
|
949
|
-
totalPages: number;
|
|
950
|
-
hasNextPage: boolean;
|
|
951
|
-
hasPreviousPage: boolean;
|
|
952
|
-
offset: number;
|
|
953
|
-
limit: number;
|
|
954
|
-
}
|
|
180
|
+
const chunks = ArrayUtils.chunk([1, 2, 3, 4, 5], 2); // [[1,2], [3,4], [5]]
|
|
181
|
+
const picked = ObjectUtils.pick(user, ["id", "name", "email"]);
|
|
182
|
+
const grouped = ObjectUtils.groupBy(users, "department");
|
|
955
183
|
```
|
|
956
184
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
```ts
|
|
960
|
-
class CacheManager {
|
|
961
|
-
constructor(options?: CacheManagerOptions);
|
|
962
|
-
set<T>(key: string, value: T, ttl?: number): void;
|
|
963
|
-
get<T>(key: string): T | null;
|
|
964
|
-
has(key: string): boolean;
|
|
965
|
-
remove(key: string): void;
|
|
966
|
-
clear(): void;
|
|
967
|
-
async getOrCompute<T>(
|
|
968
|
-
key: string,
|
|
969
|
-
fn: () => Promise<T>,
|
|
970
|
-
ttl?: number
|
|
971
|
-
): Promise<T>;
|
|
972
|
-
invalidatePattern(pattern: string): void;
|
|
973
|
-
getStats(): CacheStats;
|
|
974
|
-
}
|
|
185
|
+
## 🚀 Live Examples / Ejemplos en Vivo
|
|
975
186
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
interface CacheStats {
|
|
979
|
-
hits: number;
|
|
980
|
-
misses: number;
|
|
981
|
-
hitRate: number;
|
|
982
|
-
size: number;
|
|
983
|
-
maxSize: number;
|
|
984
|
-
}
|
|
985
|
-
```
|
|
187
|
+
**EN:** Try bytekit in your browser with these interactive examples:
|
|
188
|
+
**ES:** Prueba bytekit en tu navegador con estos ejemplos interactivos:
|
|
986
189
|
|
|
987
|
-
|
|
190
|
+
- **[React Example](https://codesandbox.io/p/devbox/bytekit-react-example-gr2k2j)** - Complete React app with ApiClient
|
|
191
|
+
- **[Vue Example](https://codesandbox.io/p/devbox/df26fs)** - Vue 3 composition API usage
|
|
192
|
+
- **[Svelte Example](https://codesandbox.io/p/devbox/lxvghg)** - Svelte integration example
|
|
988
193
|
|
|
989
|
-
|
|
990
|
-
class CompressionUtils {
|
|
991
|
-
static compress(str: string): string;
|
|
992
|
-
static decompress(compressed: string): string;
|
|
993
|
-
static base64Encode(str: string): string;
|
|
994
|
-
static base64Decode(str: string): string;
|
|
995
|
-
static base64UrlEncode(str: string): string;
|
|
996
|
-
static base64UrlDecode(str: string): string;
|
|
997
|
-
static serializeCompressed(obj: unknown): string;
|
|
998
|
-
static deserializeCompressed(compressed: string): unknown;
|
|
999
|
-
static getCompressionRatio(original: string, compressed: string): number;
|
|
1000
|
-
static minifyJSON(json: string): string;
|
|
1001
|
-
static prettyJSON(json: string, indent?: number): string;
|
|
1002
|
-
static async gzip(str: string): Promise<Buffer | string>;
|
|
1003
|
-
static async gunzip(data: Buffer | string): Promise<string>;
|
|
1004
|
-
static async deflate(str: string): Promise<Buffer | string>;
|
|
1005
|
-
static async inflate(data: Buffer | string): Promise<string>;
|
|
1006
|
-
static getSize(str: string): number;
|
|
1007
|
-
static formatBytes(bytes: number, decimals?: number): string;
|
|
1008
|
-
}
|
|
1009
|
-
```
|
|
194
|
+
**[📁 View Local Examples →](https://github.com/sebamar88/bytekit/tree/main/examples)**
|
|
1010
195
|
|
|
1011
|
-
##
|
|
196
|
+
## 🔗 Links / Enlaces
|
|
1012
197
|
|
|
1013
|
-
|
|
198
|
+
- **[📦 NPM Package](https://www.npmjs.com/package/bytekit)** - Install and version info
|
|
199
|
+
- **[📚 Full Documentation Wiki](https://github.com/sebamar88/bytekit/wiki)** - Complete API reference
|
|
200
|
+
- **[🚀 Live Examples](https://github.com/sebamar88/bytekit/tree/main/examples)** - Working code samples
|
|
201
|
+
- **[📋 Issues & Support](https://github.com/sebamar88/bytekit/issues)** - Bug reports and feature requests
|
|
202
|
+
- **[🔄 Changelog](https://github.com/sebamar88/bytekit/blob/main/CHANGELOG.md)** - Version history
|
|
1014
203
|
|
|
1015
|
-
##
|
|
204
|
+
## 🤝 Contributing / Contribuir
|
|
1016
205
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
- `locale` + `errorMessages`: **EN** localized HTTP errors. **ES** mensajes localizados por código HTTP.
|
|
1020
|
-
- `fetchImpl`: **EN** inject your own fetch (tests, custom environments). **ES** inyectá tu propio `fetch` (tests o entornos custom).
|
|
1021
|
-
- `retryPolicy`: **EN** configure automatic retries with exponential backoff. **ES** configura reintentos automáticos con backoff exponencial.
|
|
1022
|
-
- `circuitBreaker`: **EN** configure circuit breaker to prevent cascading failures. **ES** configura circuit breaker para evitar fallos en cascada.
|
|
206
|
+
**EN:** Contributions are welcome! Please read our contributing guidelines and feel free to submit issues and pull requests.
|
|
207
|
+
**ES:** ¡Las contribuciones son bienvenidas! Lee nuestras guías de contribución y no dudes en enviar issues y pull requests.
|
|
1023
208
|
|
|
1024
|
-
|
|
209
|
+
## 📄 License / Licencia
|
|
1025
210
|
|
|
1026
|
-
|
|
1027
|
-
- `body`: **EN** strings, serializable objects, or `FormData`. **ES** strings, objetos serializables o `FormData`.
|
|
1028
|
-
- `errorLocale`: **EN** override language per request. **ES** forzá un idioma específico.
|
|
1029
|
-
- Native `RequestInit` fields (`headers`, `signal`, etc.).
|
|
211
|
+
MIT © [Sebastián Martinez](https://github.com/sebamar88)
|
|
1030
212
|
|
|
1031
|
-
|
|
1032
|
-
import { HttpError } from "bytekit";
|
|
1033
|
-
|
|
1034
|
-
try {
|
|
1035
|
-
await http.get("/users");
|
|
1036
|
-
} catch (error) {
|
|
1037
|
-
if (error instanceof HttpError) {
|
|
1038
|
-
console.error("Server error", error.status, error.body);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
```
|
|
1042
|
-
|
|
1043
|
-
### Paginated Lists / Listados Paginados
|
|
1044
|
-
|
|
1045
|
-
- **getList**: **EN** fetch paginated data with built-in support for `pagination`, `sort`, and `filters`. Returns a typed `PaginatedResponse` with metadata. **ES** obtiene datos paginados con soporte para `pagination`, `sort` y `filters`. Devuelve `PaginatedResponse` con metadatos.
|
|
1046
|
-
|
|
1047
|
-
```ts
|
|
1048
|
-
import { ApiClient } from "bytekit";
|
|
1049
|
-
|
|
1050
|
-
const api = new ApiClient({ baseUrl: "https://api.example.com" });
|
|
1051
|
-
|
|
1052
|
-
// Fetch first page with 10 items per page
|
|
1053
|
-
const response = await api.getList<User>("/users", {
|
|
1054
|
-
pagination: { page: 1, limit: 10 },
|
|
1055
|
-
sort: { field: "name", order: "asc" },
|
|
1056
|
-
filters: { status: "active" },
|
|
1057
|
-
});
|
|
1058
|
-
|
|
1059
|
-
console.log(response.data); // User[]
|
|
1060
|
-
console.log(response.pagination);
|
|
1061
|
-
// {
|
|
1062
|
-
// page: 1,
|
|
1063
|
-
// limit: 10,
|
|
1064
|
-
// total: 42,
|
|
1065
|
-
// totalPages: 5,
|
|
1066
|
-
// hasNextPage: true,
|
|
1067
|
-
// hasPreviousPage: false,
|
|
1068
|
-
// }
|
|
1069
|
-
|
|
1070
|
-
// Fetch with custom filters
|
|
1071
|
-
const filtered = await api.getList<User>("/users", {
|
|
1072
|
-
pagination: { page: 2, limit: 20 },
|
|
1073
|
-
sort: { field: "createdAt", order: "desc" },
|
|
1074
|
-
filters: { role: "admin", department: "engineering" },
|
|
1075
|
-
});
|
|
1076
|
-
```
|
|
1077
|
-
|
|
1078
|
-
## Advanced Features / Características Avanzadas
|
|
1079
|
-
|
|
1080
|
-
### Retry Policy & Circuit Breaker
|
|
1081
|
-
|
|
1082
|
-
- **RetryPolicy**: **EN** automatic retry with exponential backoff for transient failures. **ES** reintentos automáticos con backoff exponencial para fallos transitorios.
|
|
1083
|
-
- **CircuitBreaker**: **EN** prevent cascading failures by stopping requests when service is down. **ES** evita fallos en cascada deteniendo requests cuando el servicio está caído.
|
|
1084
|
-
|
|
1085
|
-
```ts
|
|
1086
|
-
import { ApiClient } from "bytekit";
|
|
1087
|
-
|
|
1088
|
-
const api = new ApiClient({
|
|
1089
|
-
baseUrl: "https://api.example.com",
|
|
1090
|
-
retryPolicy: {
|
|
1091
|
-
maxAttempts: 3,
|
|
1092
|
-
initialDelayMs: 100,
|
|
1093
|
-
backoffMultiplier: 2,
|
|
1094
|
-
},
|
|
1095
|
-
circuitBreaker: {
|
|
1096
|
-
failureThreshold: 5,
|
|
1097
|
-
successThreshold: 2,
|
|
1098
|
-
timeoutMs: 60000,
|
|
1099
|
-
},
|
|
1100
|
-
});
|
|
1101
|
-
|
|
1102
|
-
// Requests automatically retry and respect circuit breaker state
|
|
1103
|
-
const data = await api.get("/users");
|
|
1104
|
-
```
|
|
1105
|
-
|
|
1106
|
-
### Response Validation
|
|
1107
|
-
|
|
1108
|
-
- **ResponseValidator**: **EN** validate API responses against schemas before using them. **ES** valida respuestas de API contra esquemas antes de usarlas.
|
|
1109
|
-
|
|
1110
|
-
```ts
|
|
1111
|
-
import { ApiClient, ValidationSchema } from "bytekit";
|
|
1112
|
-
|
|
1113
|
-
const userSchema: ValidationSchema = {
|
|
1114
|
-
type: "object",
|
|
1115
|
-
properties: {
|
|
1116
|
-
id: { type: "number", required: true },
|
|
1117
|
-
email: { type: "string", pattern: /.+@.+\..+/ },
|
|
1118
|
-
age: { type: "number", minimum: 0, maximum: 150 },
|
|
1119
|
-
},
|
|
1120
|
-
};
|
|
1121
|
-
|
|
1122
|
-
const users = await api.get<User[]>("/users", {
|
|
1123
|
-
validateResponse: {
|
|
1124
|
-
type: "array",
|
|
1125
|
-
items: userSchema,
|
|
1126
|
-
},
|
|
1127
|
-
});
|
|
1128
|
-
```
|
|
1129
|
-
|
|
1130
|
-
### File Upload Helper
|
|
1131
|
-
|
|
1132
|
-
- **FileUploadHelper**: **EN** upload files with progress tracking, chunking, and retry support. **ES** sube archivos con seguimiento de progreso, chunking y reintentos.
|
|
1133
|
-
|
|
1134
|
-
```ts
|
|
1135
|
-
import { FileUploadHelper } from "bytekit";
|
|
1136
|
-
|
|
1137
|
-
const file = document.querySelector<HTMLInputElement>("#file")?.files?.[0];
|
|
1138
|
-
if (file) {
|
|
1139
|
-
const validation = FileUploadHelper.validateFile(file, {
|
|
1140
|
-
maxSize: 50 * 1024 * 1024, // 50MB
|
|
1141
|
-
allowedTypes: ["image/jpeg", "image/png"],
|
|
1142
|
-
});
|
|
1143
|
-
|
|
1144
|
-
if (validation.valid) {
|
|
1145
|
-
const response = await FileUploadHelper.uploadFile(
|
|
1146
|
-
file,
|
|
1147
|
-
"/api/upload",
|
|
1148
|
-
{
|
|
1149
|
-
chunkSize: 5 * 1024 * 1024, // 5MB chunks
|
|
1150
|
-
onProgress: (progress) => {
|
|
1151
|
-
console.log(`${progress.percentage}% uploaded`);
|
|
1152
|
-
},
|
|
1153
|
-
}
|
|
1154
|
-
);
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
```
|
|
1158
|
-
|
|
1159
|
-
### Streaming Helper
|
|
1160
|
-
|
|
1161
|
-
- **StreamingHelper**: **EN** stream JSON lines, Server-Sent Events, or download files with progress. **ES** transmite JSON lines, Server-Sent Events o descarga archivos con progreso.
|
|
1162
|
-
|
|
1163
|
-
```ts
|
|
1164
|
-
import { StreamingHelper } from "bytekit";
|
|
1165
|
-
|
|
1166
|
-
// Stream JSON lines (NDJSON)
|
|
1167
|
-
const { data, complete } = await StreamingHelper.streamJsonLines<User>(
|
|
1168
|
-
"/api/users/stream",
|
|
1169
|
-
{
|
|
1170
|
-
onChunk: (line) => console.log("Received:", line),
|
|
1171
|
-
onComplete: () => console.log("Stream complete"),
|
|
1172
|
-
}
|
|
1173
|
-
);
|
|
1174
|
-
|
|
1175
|
-
// Stream Server-Sent Events
|
|
1176
|
-
const sse = StreamingHelper.streamSSE<Message>("/api/messages", {
|
|
1177
|
-
eventType: "message",
|
|
1178
|
-
});
|
|
1179
|
-
|
|
1180
|
-
const unsubscribe = sse.subscribe((message) => {
|
|
1181
|
-
console.log("New message:", message);
|
|
1182
|
-
});
|
|
1183
|
-
|
|
1184
|
-
// Download with progress
|
|
1185
|
-
const blob = await StreamingHelper.downloadStream("/api/export.csv", {
|
|
1186
|
-
onProgress: (percentage) => console.log(`Downloaded: ${percentage}%`),
|
|
1187
|
-
});
|
|
1188
|
-
```
|
|
1189
|
-
|
|
1190
|
-
### WebSocket Helper
|
|
1191
|
-
|
|
1192
|
-
- **WebSocketHelper**: **EN** manage WebSocket connections with auto-reconnect, heartbeat, and typed messages. **ES** gestiona conexiones WebSocket con reconexión automática, heartbeat y mensajes tipados.
|
|
1193
|
-
|
|
1194
|
-
```ts
|
|
1195
|
-
import { WebSocketHelper } from "bytekit";
|
|
1196
|
-
|
|
1197
|
-
const ws = new WebSocketHelper("wss://api.example.com/ws", {
|
|
1198
|
-
reconnect: true,
|
|
1199
|
-
maxReconnectAttempts: 5,
|
|
1200
|
-
heartbeatIntervalMs: 30000,
|
|
1201
|
-
});
|
|
1202
|
-
|
|
1203
|
-
await ws.connect();
|
|
1204
|
-
|
|
1205
|
-
// Subscribe to messages
|
|
1206
|
-
ws.on<{ userId: string; text: string }>("message", (data) => {
|
|
1207
|
-
console.log(`${data.userId}: ${data.text}`);
|
|
1208
|
-
});
|
|
1209
|
-
|
|
1210
|
-
// Send messages
|
|
1211
|
-
ws.send("message", { text: "Hello!" });
|
|
1212
|
-
|
|
1213
|
-
// Request-response pattern
|
|
1214
|
-
const response = await ws.request<{ query: string }, { result: string }>(
|
|
1215
|
-
"query",
|
|
1216
|
-
{ query: "SELECT * FROM users" }
|
|
1217
|
-
);
|
|
1218
|
-
|
|
1219
|
-
// Handle errors
|
|
1220
|
-
ws.onError((error) => console.error("WebSocket error:", error));
|
|
1221
|
-
|
|
1222
|
-
// Close connection
|
|
1223
|
-
ws.close();
|
|
1224
|
-
```
|
|
1225
|
-
|
|
1226
|
-
### Request Caching
|
|
1227
|
-
|
|
1228
|
-
- **RequestCache**: **EN** cache HTTP responses with TTL and stale-while-revalidate support. **ES** cachea respuestas HTTP con TTL y soporte para stale-while-revalidate.
|
|
1229
|
-
|
|
1230
|
-
```ts
|
|
1231
|
-
import { RequestCache } from "bytekit";
|
|
1232
|
-
|
|
1233
|
-
const cache = new RequestCache({
|
|
1234
|
-
ttl: 5 * 60 * 1000, // 5 minutes
|
|
1235
|
-
staleWhileRevalidate: 60 * 1000, // 1 minute
|
|
1236
|
-
});
|
|
1237
|
-
|
|
1238
|
-
// Cache a response
|
|
1239
|
-
cache.set("/users", users);
|
|
1240
|
-
|
|
1241
|
-
// Retrieve from cache
|
|
1242
|
-
const cached = cache.get("/users");
|
|
1243
|
-
|
|
1244
|
-
// Check if stale but still valid
|
|
1245
|
-
if (cache.isStale("/users")) {
|
|
1246
|
-
// Revalidate in background
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
// Invalidate specific entry
|
|
1250
|
-
cache.invalidate("/users");
|
|
1251
|
-
|
|
1252
|
-
// Invalidate by pattern
|
|
1253
|
-
cache.invalidatePattern("/users/*");
|
|
1254
|
-
|
|
1255
|
-
// Get statistics
|
|
1256
|
-
const stats = cache.getStats();
|
|
1257
|
-
console.log(`Hit rate: ${stats.hitRate * 100}%`);
|
|
1258
|
-
```
|
|
1259
|
-
|
|
1260
|
-
### Rate Limiting
|
|
1261
|
-
|
|
1262
|
-
- **RateLimiter**: **EN** token bucket rate limiter for smooth request throttling. **ES** limitador de tasa con token bucket para throttling suave.
|
|
1263
|
-
- **SlidingWindowRateLimiter**: **EN** sliding window rate limiter for precise rate control. **ES** limitador de ventana deslizante para control preciso de tasa.
|
|
1264
|
-
|
|
1265
|
-
```ts
|
|
1266
|
-
import { RateLimiter, SlidingWindowRateLimiter } from "bytekit";
|
|
1267
|
-
|
|
1268
|
-
// Token bucket limiter (allows bursts)
|
|
1269
|
-
const limiter = new RateLimiter({
|
|
1270
|
-
maxRequests: 100,
|
|
1271
|
-
windowMs: 60 * 1000, // 1 minute
|
|
1272
|
-
});
|
|
1273
|
-
|
|
1274
|
-
if (limiter.isAllowed("https://api.example.com/users")) {
|
|
1275
|
-
// Make request
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
// Wait for allowance if rate limited
|
|
1279
|
-
await limiter.waitForAllowance("https://api.example.com/users");
|
|
1280
|
-
|
|
1281
|
-
// Get stats
|
|
1282
|
-
const stats = limiter.getStats("https://api.example.com/users");
|
|
1283
|
-
console.log(`Remaining: ${stats.remaining}/${stats.limit}`);
|
|
1284
|
-
|
|
1285
|
-
// Sliding window limiter (more accurate)
|
|
1286
|
-
const slidingLimiter = new SlidingWindowRateLimiter({
|
|
1287
|
-
maxRequests: 100,
|
|
1288
|
-
windowMs: 60 * 1000,
|
|
1289
|
-
});
|
|
1290
|
-
|
|
1291
|
-
if (slidingLimiter.isAllowed("https://api.example.com/users")) {
|
|
1292
|
-
// Make request
|
|
1293
|
-
}
|
|
1294
|
-
```
|
|
1295
|
-
|
|
1296
|
-
### Request Deduplication
|
|
1297
|
-
|
|
1298
|
-
- **RequestDeduplicator**: **EN** deduplicate in-flight requests to avoid redundant API calls. **ES** deduplica requests en vuelo para evitar llamadas redundantes.
|
|
1299
|
-
|
|
1300
|
-
```ts
|
|
1301
|
-
import { RequestDeduplicator } from "bytekit";
|
|
1302
|
-
|
|
1303
|
-
const dedup = new RequestDeduplicator();
|
|
1304
|
-
|
|
1305
|
-
// Multiple consumers of the same request share the same response
|
|
1306
|
-
const [users1, users2] = await Promise.all([
|
|
1307
|
-
dedup.execute("/users", () => api.get("/users")),
|
|
1308
|
-
dedup.execute("/users", () => api.get("/users")), // Deduplicated!
|
|
1309
|
-
]);
|
|
1310
|
-
|
|
1311
|
-
// Different requests execute separately
|
|
1312
|
-
const [users, posts] = await Promise.all([
|
|
1313
|
-
dedup.execute("/users", () => api.get("/users")),
|
|
1314
|
-
dedup.execute("/posts", () => api.get("/posts")),
|
|
1315
|
-
]);
|
|
1316
|
-
|
|
1317
|
-
// Get statistics
|
|
1318
|
-
const stats = dedup.getStats();
|
|
1319
|
-
console.log(`Deduplication rate: ${stats.deduplicationRate * 100}%`);
|
|
1320
|
-
|
|
1321
|
-
// Check in-flight requests
|
|
1322
|
-
console.log(`In-flight: ${dedup.getInFlightCount()}`);
|
|
1323
|
-
```
|
|
1324
|
-
|
|
1325
|
-
### Object Utilities
|
|
1326
|
-
|
|
1327
|
-
- **ObjectUtils**: **EN** everyday object manipulation utilities (isEmpty, deepClone, merge, pick, omit, flatten, groupBy, etc.). **ES** utilidades cotidianas para manipular objetos.
|
|
1328
|
-
|
|
1329
|
-
```ts
|
|
1330
|
-
import { ObjectUtils } from "bytekit";
|
|
1331
|
-
|
|
1332
|
-
// Check if empty
|
|
1333
|
-
ObjectUtils.isEmpty(null); // true
|
|
1334
|
-
ObjectUtils.isEmpty({}); // true
|
|
1335
|
-
ObjectUtils.isEmpty([1, 2]); // false
|
|
1336
|
-
|
|
1337
|
-
// Deep clone
|
|
1338
|
-
const original = { a: { b: 1 } };
|
|
1339
|
-
const cloned = ObjectUtils.deepClone(original);
|
|
1340
|
-
|
|
1341
|
-
// Merge objects
|
|
1342
|
-
const merged = ObjectUtils.merge({ a: 1 }, { b: 2 }, { c: 3 });
|
|
1343
|
-
// { a: 1, b: 2, c: 3 }
|
|
1344
|
-
|
|
1345
|
-
// Pick/omit keys
|
|
1346
|
-
const user = {
|
|
1347
|
-
id: 1,
|
|
1348
|
-
name: "John",
|
|
1349
|
-
email: "john@example.com",
|
|
1350
|
-
password: "secret",
|
|
1351
|
-
};
|
|
1352
|
-
const safe = ObjectUtils.omit(user, ["password"]);
|
|
1353
|
-
// { id: 1, name: "John", email: "john@example.com" }
|
|
1354
|
-
|
|
1355
|
-
// Nested access with dot notation
|
|
1356
|
-
const config = { db: { host: "localhost", port: 5432 } };
|
|
1357
|
-
const host = ObjectUtils.get(config, "db.host"); // "localhost"
|
|
1358
|
-
ObjectUtils.set(config, "db.ssl", true);
|
|
1359
|
-
|
|
1360
|
-
// Flatten/unflatten
|
|
1361
|
-
const flat = ObjectUtils.flatten({ a: { b: { c: 1 } } });
|
|
1362
|
-
// { "a.b.c": 1 }
|
|
1363
|
-
|
|
1364
|
-
// Group and index arrays
|
|
1365
|
-
const users = [
|
|
1366
|
-
{ id: 1, role: "admin" },
|
|
1367
|
-
{ id: 2, role: "user" },
|
|
1368
|
-
{ id: 3, role: "admin" },
|
|
1369
|
-
];
|
|
1370
|
-
const byRole = ObjectUtils.groupBy(users, "role");
|
|
1371
|
-
const byId = ObjectUtils.indexBy(users, "id");
|
|
1372
|
-
|
|
1373
|
-
// Filter and map
|
|
1374
|
-
const filtered = ObjectUtils.filter(
|
|
1375
|
-
user,
|
|
1376
|
-
(_, value) => typeof value === "string"
|
|
1377
|
-
);
|
|
1378
|
-
const doubled = ObjectUtils.mapValues({ a: 1, b: 2 }, (v) => v * 2);
|
|
1379
|
-
|
|
1380
|
-
// Deep equality
|
|
1381
|
-
ObjectUtils.deepEqual({ a: 1 }, { a: 1 }); // true
|
|
1382
|
-
```
|
|
1383
|
-
|
|
1384
|
-
### Array Utilities
|
|
1385
|
-
|
|
1386
|
-
- **ArrayUtils**: **EN** everyday array manipulation utilities (chunk, flatten, unique, shuffle, zip, partition, etc.). **ES** utilidades cotidianas para manipular arrays.
|
|
1387
|
-
|
|
1388
|
-
```ts
|
|
1389
|
-
import { ArrayUtils } from "bytekit";
|
|
1390
|
-
|
|
1391
|
-
// Chunk array into smaller pieces
|
|
1392
|
-
ArrayUtils.chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
|
|
1393
|
-
|
|
1394
|
-
// Flatten nested arrays
|
|
1395
|
-
ArrayUtils.flatten(
|
|
1396
|
-
[
|
|
1397
|
-
[1, 2],
|
|
1398
|
-
[3, [4, 5]],
|
|
1399
|
-
],
|
|
1400
|
-
2
|
|
1401
|
-
); // [1, 2, 3, 4, 5]
|
|
1402
|
-
|
|
1403
|
-
// Get unique values
|
|
1404
|
-
ArrayUtils.unique([1, 2, 2, 3, 3, 3]); // [1, 2, 3]
|
|
1405
|
-
ArrayUtils.unique([{ id: 1 }, { id: 2 }, { id: 1 }], (item) => item.id); // [{ id: 1 }, { id: 2 }]
|
|
1406
|
-
|
|
1407
|
-
// Shuffle and random selection
|
|
1408
|
-
const shuffled = ArrayUtils.shuffle([1, 2, 3, 4, 5]);
|
|
1409
|
-
const random = ArrayUtils.random([1, 2, 3, 4, 5]);
|
|
1410
|
-
const randomN = ArrayUtils.randomN([1, 2, 3, 4, 5], 3);
|
|
1411
|
-
|
|
1412
|
-
// Zip and unzip
|
|
1413
|
-
ArrayUtils.zip([1, 2, 3], ["a", "b", "c"]); // [[1, "a"], [2, "b"], [3, "c"]]
|
|
1414
|
-
|
|
1415
|
-
// Set operations
|
|
1416
|
-
ArrayUtils.difference([1, 2, 3, 4], [2, 4]); // [1, 3]
|
|
1417
|
-
ArrayUtils.intersection([1, 2, 3], [2, 3, 4]); // [2, 3]
|
|
1418
|
-
ArrayUtils.union([1, 2], [2, 3]); // [1, 2, 3]
|
|
1419
|
-
|
|
1420
|
-
// Partition by predicate
|
|
1421
|
-
const [evens, odds] = ArrayUtils.partition([1, 2, 3, 4, 5], (n) => n % 2 === 0);
|
|
1422
|
-
// evens: [2, 4], odds: [1, 3, 5]
|
|
1423
|
-
|
|
1424
|
-
// Math operations
|
|
1425
|
-
ArrayUtils.sum([1, 2, 3, 4]); // 10
|
|
1426
|
-
ArrayUtils.average([1, 2, 3, 4]); // 2.5
|
|
1427
|
-
ArrayUtils.min([3, 1, 4, 1, 5]); // 1
|
|
1428
|
-
ArrayUtils.max([3, 1, 4, 1, 5]); // 5
|
|
1429
|
-
|
|
1430
|
-
// Range generation
|
|
1431
|
-
ArrayUtils.range(1, 5); // [1, 2, 3, 4]
|
|
1432
|
-
ArrayUtils.range(0, 10, 2); // [0, 2, 4, 6, 8]
|
|
1433
|
-
|
|
1434
|
-
// Rotate and transpose
|
|
1435
|
-
ArrayUtils.rotate([1, 2, 3, 4], 2); // [3, 4, 1, 2]
|
|
1436
|
-
ArrayUtils.transpose([
|
|
1437
|
-
[1, 2],
|
|
1438
|
-
[3, 4],
|
|
1439
|
-
]); // [[1, 3], [2, 4]]
|
|
1440
|
-
```
|
|
1441
|
-
|
|
1442
|
-
### Error Boundary
|
|
1443
|
-
|
|
1444
|
-
- **ErrorBoundary**: **EN** comprehensive error handling with automatic retry logic, error history tracking, and global error handlers. **ES** manejo completo de errores con reintentos automáticos, historial de errores y handlers globales.
|
|
1445
|
-
|
|
1446
|
-
```ts
|
|
1447
|
-
import {
|
|
1448
|
-
ErrorBoundary,
|
|
1449
|
-
AppError,
|
|
1450
|
-
AppValidationError,
|
|
1451
|
-
NotFoundError,
|
|
1452
|
-
TimeoutError,
|
|
1453
|
-
RateLimitError,
|
|
1454
|
-
} from "bytekit";
|
|
1455
|
-
|
|
1456
|
-
// Create error boundary with custom config
|
|
1457
|
-
const boundary = new ErrorBoundary({
|
|
1458
|
-
maxRetries: 3,
|
|
1459
|
-
retryDelay: 1000,
|
|
1460
|
-
});
|
|
1461
|
-
|
|
1462
|
-
// Handle errors with custom handlers
|
|
1463
|
-
boundary.addHandler(async (error, context) => {
|
|
1464
|
-
console.error(`Error in ${context.component}:`, error.message);
|
|
1465
|
-
// Send to error tracking service
|
|
1466
|
-
});
|
|
1467
|
-
|
|
1468
|
-
// Execute async function with automatic retry
|
|
1469
|
-
try {
|
|
1470
|
-
const data = await boundary.execute(
|
|
1471
|
-
async () => {
|
|
1472
|
-
return await fetchData();
|
|
1473
|
-
},
|
|
1474
|
-
{ component: "DataFetcher", userId: "user_123" }
|
|
1475
|
-
);
|
|
1476
|
-
} catch (error) {
|
|
1477
|
-
if (error instanceof TimeoutError) {
|
|
1478
|
-
console.log("Request timed out after retries");
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
// Execute sync function with error handling
|
|
1483
|
-
const result = boundary.executeSync(
|
|
1484
|
-
() => {
|
|
1485
|
-
return JSON.parse(jsonString);
|
|
1486
|
-
},
|
|
1487
|
-
{ context: "json-parsing" }
|
|
1488
|
-
);
|
|
1489
|
-
|
|
1490
|
-
// Wrap functions for automatic error handling
|
|
1491
|
-
const wrappedAsync = boundary.wrap(async (id: string) => {
|
|
1492
|
-
return await api.get(`/users/${id}`);
|
|
1493
|
-
});
|
|
1494
|
-
|
|
1495
|
-
const wrappedSync = boundary.wrapSync((data: string) => {
|
|
1496
|
-
return JSON.parse(data);
|
|
1497
|
-
});
|
|
1498
|
-
|
|
1499
|
-
// Get error history
|
|
1500
|
-
const history = boundary.getErrorHistory(10);
|
|
1501
|
-
history.forEach((entry) => {
|
|
1502
|
-
console.log(`${entry.timestamp}: ${entry.error.message}`);
|
|
1503
|
-
});
|
|
1504
|
-
|
|
1505
|
-
// Create error report
|
|
1506
|
-
const report = boundary.createErrorReport();
|
|
1507
|
-
console.log(report);
|
|
1508
|
-
// {
|
|
1509
|
-
// timestamp: "2024-12-20T10:30:00.000Z",
|
|
1510
|
-
// errors: [
|
|
1511
|
-
// {
|
|
1512
|
-
// code: "TIMEOUT",
|
|
1513
|
-
// message: "Request timeout",
|
|
1514
|
-
// statusCode: 408,
|
|
1515
|
-
// timestamp: 1703068200000
|
|
1516
|
-
// }
|
|
1517
|
-
// ]
|
|
1518
|
-
// }
|
|
1519
|
-
|
|
1520
|
-
// Use global error boundary
|
|
1521
|
-
import { getGlobalErrorBoundary } from "bytekit";
|
|
1522
|
-
|
|
1523
|
-
const globalBoundary = getGlobalErrorBoundary({
|
|
1524
|
-
maxRetries: 2,
|
|
1525
|
-
retryDelay: 500,
|
|
1526
|
-
});
|
|
1527
|
-
|
|
1528
|
-
// Automatically catches unhandled rejections and global errors
|
|
1529
|
-
globalBoundary.addHandler((error) => {
|
|
1530
|
-
console.error("Unhandled error:", error);
|
|
1531
|
-
});
|
|
1532
|
-
|
|
1533
|
-
// Custom error types
|
|
1534
|
-
try {
|
|
1535
|
-
throw new AppValidationError("Invalid email format", {
|
|
1536
|
-
component: "SignupForm",
|
|
1537
|
-
});
|
|
1538
|
-
} catch (error) {
|
|
1539
|
-
if (error instanceof AppValidationError) {
|
|
1540
|
-
console.log("Validation failed:", error.message);
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
try {
|
|
1545
|
-
throw new RateLimitError("Too many requests", 60, {
|
|
1546
|
-
context: "api-call",
|
|
1547
|
-
});
|
|
1548
|
-
} catch (error) {
|
|
1549
|
-
if (error instanceof RateLimitError) {
|
|
1550
|
-
const retryAfter = error.context?.metadata?.retryAfter;
|
|
1551
|
-
console.log(`Retry after ${retryAfter} seconds`);
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
```
|
|
1555
|
-
|
|
1556
|
-
### Form Utilities
|
|
1557
|
-
|
|
1558
|
-
- **FormUtils**: **EN** form validation and state management with built-in validators, async validation, and framework-agnostic design. **ES** validación de formularios y gestión de estado con validadores integrados, validación async y agnóstico del framework.
|
|
1559
|
-
|
|
1560
|
-
```ts
|
|
1561
|
-
import { FormUtils, createForm, Validators } from "bytekit";
|
|
1562
|
-
|
|
1563
|
-
// Create form with validation rules
|
|
1564
|
-
const form = new FormUtils({
|
|
1565
|
-
initialValues: {
|
|
1566
|
-
email: "",
|
|
1567
|
-
password: "",
|
|
1568
|
-
confirmPassword: "",
|
|
1569
|
-
},
|
|
1570
|
-
rules: {
|
|
1571
|
-
email: {
|
|
1572
|
-
required: "Email is required",
|
|
1573
|
-
email: "Invalid email format",
|
|
1574
|
-
},
|
|
1575
|
-
password: {
|
|
1576
|
-
required: true,
|
|
1577
|
-
minLength: 8,
|
|
1578
|
-
custom: (value) => {
|
|
1579
|
-
return /[A-Z]/.test(value)
|
|
1580
|
-
? true
|
|
1581
|
-
: "Must contain uppercase letter";
|
|
1582
|
-
},
|
|
1583
|
-
},
|
|
1584
|
-
confirmPassword: {
|
|
1585
|
-
required: true,
|
|
1586
|
-
custom: (value) => {
|
|
1587
|
-
return Validators.match(value, form.getValue("password"))
|
|
1588
|
-
? true
|
|
1589
|
-
: "Passwords do not match";
|
|
1590
|
-
},
|
|
1591
|
-
},
|
|
1592
|
-
},
|
|
1593
|
-
validateOnChange: true,
|
|
1594
|
-
validateOnBlur: true,
|
|
1595
|
-
onSubmit: async (values) => {
|
|
1596
|
-
console.log("Form submitted:", values);
|
|
1597
|
-
},
|
|
1598
|
-
onError: (errors) => {
|
|
1599
|
-
console.error("Validation errors:", errors);
|
|
1600
|
-
},
|
|
1601
|
-
});
|
|
1602
|
-
|
|
1603
|
-
// Update field value
|
|
1604
|
-
form.setValue("email", "user@example.com");
|
|
1605
|
-
|
|
1606
|
-
// Get field state
|
|
1607
|
-
const emailError = form.getFieldError("email");
|
|
1608
|
-
const isTouched = form.isTouched("email");
|
|
1609
|
-
const isDirty = form.isDirty("email");
|
|
1610
|
-
|
|
1611
|
-
// Validate single field
|
|
1612
|
-
await form.validateField("email");
|
|
1613
|
-
|
|
1614
|
-
// Validate all fields
|
|
1615
|
-
const errors = await form.validate();
|
|
1616
|
-
|
|
1617
|
-
// Submit form
|
|
1618
|
-
const success = await form.submit();
|
|
1619
|
-
|
|
1620
|
-
// Get form state
|
|
1621
|
-
const state = form.getState();
|
|
1622
|
-
console.log(state);
|
|
1623
|
-
// {
|
|
1624
|
-
// values: { email: "...", password: "...", confirmPassword: "..." },
|
|
1625
|
-
// errors: { email: "", password: "", confirmPassword: "" },
|
|
1626
|
-
// touched: { email: true, password: true, confirmPassword: false },
|
|
1627
|
-
// dirty: { email: true, password: true, confirmPassword: false },
|
|
1628
|
-
// isValidating: false,
|
|
1629
|
-
// isValid: true
|
|
1630
|
-
// }
|
|
1631
|
-
|
|
1632
|
-
// Create bindings for framework integration (React, Vue, etc.)
|
|
1633
|
-
const emailBinding = form.createBinding("email");
|
|
1634
|
-
// {
|
|
1635
|
-
// value: "user@example.com",
|
|
1636
|
-
// onChange: (value) => form.setValue("email", value),
|
|
1637
|
-
// onBlur: () => form.touchField("email"),
|
|
1638
|
-
// error: "",
|
|
1639
|
-
// touched: true,
|
|
1640
|
-
// dirty: true
|
|
1641
|
-
// }
|
|
1642
|
-
|
|
1643
|
-
// Reset form
|
|
1644
|
-
form.reset();
|
|
1645
|
-
|
|
1646
|
-
// Serialize/deserialize
|
|
1647
|
-
const data = form.serialize();
|
|
1648
|
-
form.deserialize({ email: "new@example.com" });
|
|
1649
|
-
|
|
1650
|
-
// Built-in validators
|
|
1651
|
-
Validators.required("value"); // true
|
|
1652
|
-
Validators.email("test@example.com"); // true
|
|
1653
|
-
Validators.minLength("password", 8); // true
|
|
1654
|
-
Validators.maxLength("username", 20); // true
|
|
1655
|
-
Validators.pattern("12345", /^\d+$/); // true
|
|
1656
|
-
Validators.url("https://example.com"); // true
|
|
1657
|
-
Validators.match("password", "password"); // true
|
|
1658
|
-
|
|
1659
|
-
// Factory function
|
|
1660
|
-
const signupForm = createForm({
|
|
1661
|
-
initialValues: { username: "", email: "" },
|
|
1662
|
-
rules: {
|
|
1663
|
-
username: { required: true, minLength: 3 },
|
|
1664
|
-
email: { required: true, email: true },
|
|
1665
|
-
},
|
|
1666
|
-
});
|
|
1667
|
-
```
|
|
1668
|
-
|
|
1669
|
-
```
|
|
1670
|
-
|
|
1671
|
-
import {
|
|
1672
|
-
createLogger,
|
|
1673
|
-
withTiming,
|
|
1674
|
-
createStopwatch,
|
|
1675
|
-
StorageManager,
|
|
1676
|
-
EnvManager,
|
|
1677
|
-
} from "bytekit";
|
|
1678
|
-
|
|
1679
|
-
const logger = createLogger({ namespace: "payments", level: "debug" });
|
|
1680
|
-
|
|
1681
|
-
await withTiming("settlements", async () => {
|
|
1682
|
-
const stopwatch = createStopwatch({ label: "batch-download", logger });
|
|
1683
|
-
const batch = await downloadPayments();
|
|
1684
|
-
stopwatch.log({ records: batch.length });
|
|
1685
|
-
});
|
|
1686
|
-
|
|
1687
|
-
const storage = new StorageManager();
|
|
1688
|
-
storage.set("token", "abc123", 60_000);
|
|
1689
|
-
const env = new EnvManager();
|
|
1690
|
-
const apiKey = env.require("API_KEY");
|
|
1691
|
-
|
|
1692
|
-
```
|
|
1693
|
-
|
|
1694
|
-
- `DateUtils`: **EN** safe parsing, add/subtract, configurable diffs, `isSameDay`. **ES** parseo seguro, sumas/restas, diferencias configurables e `isSameDay`.
|
|
1695
|
-
- `StringUtils`: **EN** slugify, capitalize, masking, interpolation, query strings. **ES** slugify, capitalización, máscaras, interpolación, query strings.
|
|
1696
|
-
- `Validator`: **EN** lightweight synchronous validators. **ES** validadores sincrónicos livianos.
|
|
1697
|
-
- `StorageManager`: **EN** safe wrapper for `localStorage`/`sessionStorage`. **ES** adaptador seguro para storage del navegador.
|
|
1698
|
-
|
|
1699
|
-
## Toolkit Catalog / Catálogo de herramientas
|
|
1700
|
-
|
|
1701
|
-
### ApiClient
|
|
1702
|
-
|
|
1703
|
-
- **EN**: Typed HTTP client with retries, localized errors, interceptors, and custom fetch support for Node/browsers.
|
|
1704
|
-
**ES**: Cliente HTTP tipado con reintentos, errores localizados, interceptores y `fetch` personalizable para Node/navegadores.
|
|
1705
|
-
|
|
1706
|
-
```ts
|
|
1707
|
-
import { ApiClient } from "bytekit";
|
|
1708
|
-
|
|
1709
|
-
const api = new ApiClient({
|
|
1710
|
-
baseUrl: "https://api.example.com",
|
|
1711
|
-
defaultHeaders: { "X-Team": "development" },
|
|
1712
|
-
});
|
|
1713
|
-
|
|
1714
|
-
const user = await api.get("/users/1", {
|
|
1715
|
-
searchParams: { locale: "es" },
|
|
1716
|
-
});
|
|
1717
|
-
```
|
|
1718
|
-
|
|
1719
|
-
### createLogger
|
|
1720
|
-
|
|
1721
|
-
- **EN**: Structured logger with levels, namespaces, transports for Node/browser, and child loggers.
|
|
1722
|
-
**ES**: Logger estructurado con niveles, namespaces, transports para Node/browser y loggers hijos.
|
|
1723
|
-
|
|
1724
|
-
```ts
|
|
1725
|
-
import { createLogger } from "bytekit";
|
|
1726
|
-
|
|
1727
|
-
const logger = createLogger({ namespace: "payments", level: "info" });
|
|
1728
|
-
logger.warn("payment delayed", { id: "tx_1" });
|
|
1729
|
-
|
|
1730
|
-
const workerLogger = logger.child("worker");
|
|
1731
|
-
workerLogger.debug("processing batch", { size: 20 });
|
|
1732
|
-
```
|
|
1733
|
-
|
|
1734
|
-
### Timing & Debug Utilities
|
|
1735
|
-
|
|
1736
|
-
- **EN**: `createStopwatch`, `withTiming`, `measureAsync`, `captureDebug`, and `Profiler` help you capture execution times and emit logs automatically.
|
|
1737
|
-
**ES**: `createStopwatch`, `withTiming`, `measureAsync`, `captureDebug` y `Profiler` facilitan medir tiempos y loguear automáticamente.
|
|
1738
|
-
|
|
1739
|
-
```ts
|
|
1740
|
-
import { createStopwatch, withTiming, measureAsync, Profiler } from "bytekit";
|
|
1741
|
-
|
|
1742
|
-
const stopwatch = createStopwatch({ label: "sync-users" });
|
|
1743
|
-
// ... run task
|
|
1744
|
-
stopwatch.log({ records: 42 });
|
|
1745
|
-
|
|
1746
|
-
await withTiming("refresh-cache", async () => fetchCache());
|
|
1747
|
-
const { result, durationMs } = await measureAsync("bill-run", () =>
|
|
1748
|
-
processBills()
|
|
1749
|
-
);
|
|
1750
|
-
|
|
1751
|
-
const profiler = new Profiler();
|
|
1752
|
-
profiler.start("db");
|
|
1753
|
-
await queryDb();
|
|
1754
|
-
profiler.end("db");
|
|
1755
|
-
console.table(profiler.summary());
|
|
1756
|
-
```
|
|
1757
|
-
|
|
1758
|
-
### DateUtils
|
|
1759
|
-
|
|
1760
|
-
- **parse / isValid**: **EN** accept `Date`, ISO strings, timestamps; return normalized Date or boolean. **ES** aceptan `Date`, string ISO o timestamp y devuelven Date normalizada o booleano.
|
|
1761
|
-
- **toISODate**: **EN** format to `YYYY-MM-DD` without timezone surprises. **ES** formatea como `YYYY-MM-DD` evitando problemas de zona horaria.
|
|
1762
|
-
- **startOfDay / endOfDay**: **EN** clamp hours to `00:00:00.000` or `23:59:59.999`. **ES** ajusta horas al inicio o final del día.
|
|
1763
|
-
- **add**: **EN** add duration (`days`, `hours`, `minutes`, `seconds`, `milliseconds`). **ES** suma duraciones con granularidad configurable.
|
|
1764
|
-
- **diff / diffInDays**: **EN** difference between two dates with unit + rounding + absolute options. **ES** diferencia entre fechas con unidad, redondeo y valor absoluto configurable.
|
|
1765
|
-
- **isSameDay / isBefore / isAfter**: **EN** compare normalized dates. **ES** compara fechas normalizadas.
|
|
1766
|
-
- **format**: **EN** locale-aware short date (`es-AR` default). **ES** formatea con `toLocaleDateString`.
|
|
1767
|
-
|
|
1768
|
-
```ts
|
|
1769
|
-
DateUtils.isSameDay("2024-10-10", new Date());
|
|
1770
|
-
DateUtils.diff(new Date("2024-01-01"), Date.now(), {
|
|
1771
|
-
unit: "days",
|
|
1772
|
-
rounding: "round",
|
|
1773
|
-
absolute: true,
|
|
1774
|
-
});
|
|
1775
|
-
```
|
|
1776
|
-
|
|
1777
|
-
### StringUtils
|
|
1778
|
-
|
|
1779
|
-
- **removeDiacritics / compactWhitespace**: **EN** normalize text for comparisons or rendering. **ES** normalizan texto para comparaciones o UI.
|
|
1780
|
-
- **slugify**: **EN** create URL-friendly IDs with configurable separator/lowercase. **ES** genera slugs configurables.
|
|
1781
|
-
- **capitalize / capitalizeWords**: **EN/ES** capitaliza respetando locale.
|
|
1782
|
-
- **truncate**: **EN** trims long strings with optional ellipsis + word boundaries. **ES** recorta textos largos respetando palabras.
|
|
1783
|
-
- **mask**: **EN** hide sensitive parts with custom `maskChar`, `visibleStart`, `visibleEnd`. **ES** oculta secciones sensibles con máscara configurable.
|
|
1784
|
-
- **interpolate**: **EN** replace `{{placeholders}}` with nested object values (strict/fallback/transform). **ES** interpolación con soporte para rutas y validación.
|
|
1785
|
-
- **initials**: **EN** generate up to `limit` initials. **ES** genera iniciales rápido.
|
|
1786
|
-
- **toQueryString**: **EN** serialize nested objects/arrays with formats (`repeat`, `bracket`, `comma`). **ES** serializa objetos y arrays a query strings.
|
|
1787
|
-
|
|
1788
|
-
```ts
|
|
1789
|
-
StringUtils.mask("4242424242424242", { visibleStart: 4, visibleEnd: 2 });
|
|
1790
|
-
StringUtils.toQueryString({
|
|
1791
|
-
page: 1,
|
|
1792
|
-
tags: ["lab", "team"],
|
|
1793
|
-
filters: { status: "active" },
|
|
1794
|
-
});
|
|
1795
|
-
```
|
|
1796
|
-
|
|
1797
|
-
### StorageManager
|
|
1798
|
-
|
|
1799
|
-
- **StorageManager**: **EN** wraps any `Storage` (default `localStorage`) with safe JSON parsing and TTL support. **ES** envuelve cualquier `Storage` con parseo seguro y expiración opcional.
|
|
1800
|
-
- **set/get/remove/clear**: **EN** persist typed values, remove expired entries automatically. **ES** guarda valores tipados y limpia expirados.
|
|
1801
|
-
|
|
1802
|
-
```ts
|
|
1803
|
-
const storage = new StorageManager(sessionStorage);
|
|
1804
|
-
storage.set("session", { token: "abc" }, 60_000);
|
|
1805
|
-
const session = storage.get<{ token: string }>("session");
|
|
1806
|
-
```
|
|
1807
|
-
|
|
1808
|
-
### EnvManager
|
|
1809
|
-
|
|
1810
|
-
- **get / require**: **EN** read ENV vars from Node (via `process.env`) or Vite-style browser builds (`import.meta.env`). **ES** lee env vars en Node o navegador y marca obligatorias con `require`.
|
|
1811
|
-
- **isProd**: **EN** check `NODE_ENV`/`MODE`. **ES** detecta modo producción.
|
|
1812
|
-
|
|
1813
|
-
```ts
|
|
1814
|
-
const env = new EnvManager();
|
|
1815
|
-
const apiBase = env.require("API_BASE_URL");
|
|
1816
|
-
if (env.isProd()) {
|
|
1817
|
-
// toggle prod-only behavior
|
|
1818
|
-
}
|
|
1819
|
-
```
|
|
1820
|
-
|
|
1821
|
-
### Validator
|
|
1822
|
-
|
|
1823
|
-
- **Identity**: **EN** `isEmail`, `isUUIDv4`, `isDni`, `isCuit`, `isCbu`. **ES** validaciones de identidad y banking locales.
|
|
1824
|
-
- **Phones**: **EN** `isInternationalPhone`, `isPhoneE164`, `isLocalPhone(locale)`. **ES** valida teléfonos internacionales y locales con patrones por país.
|
|
1825
|
-
- **Security**: **EN** `isStrongPassword`, `isOneTimeCode`. **ES** contraseñas fuertes y códigos OTP.
|
|
1826
|
-
- **General**: **EN** `isUrl`, `isEmpty`, length guards, regex matcher, `isDateRange`. **ES** helpers generales para formularios.
|
|
1827
|
-
|
|
1828
|
-
```ts
|
|
1829
|
-
Validator.isStrongPassword("SecurePass!2024", { minLength: 10 });
|
|
1830
|
-
Validator.isLocalPhone("11 5555-7777", "es-AR");
|
|
1831
|
-
```
|
|
1832
|
-
|
|
1833
|
-
### EventEmitter
|
|
1834
|
-
|
|
1835
|
-
- **EN**: Pub/sub event system with sync/async listeners, once listeners, error handling, and listener tracking.
|
|
1836
|
-
**ES**: Sistema de eventos pub/sub con listeners síncronos/asíncronos, listeners únicos, manejo de errores y seguimiento.
|
|
1837
|
-
|
|
1838
|
-
```ts
|
|
1839
|
-
import { EventEmitter, createEventEmitter } from "bytekit";
|
|
1840
|
-
|
|
1841
|
-
// Create event emitter with typed events
|
|
1842
|
-
const emitter = new EventEmitter<{
|
|
1843
|
-
"user:login": { userId: string; timestamp: number };
|
|
1844
|
-
"user:logout": { userId: string };
|
|
1845
|
-
error: Error;
|
|
1846
|
-
}>();
|
|
1847
|
-
|
|
1848
|
-
// Register listeners
|
|
1849
|
-
emitter.on("user:login", async (data) => {
|
|
1850
|
-
console.log(`User ${data.userId} logged in`);
|
|
1851
|
-
await trackEvent(data);
|
|
1852
|
-
});
|
|
1853
|
-
|
|
1854
|
-
// One-time listener
|
|
1855
|
-
emitter.once("user:logout", (data) => {
|
|
1856
|
-
console.log(`User ${data.userId} logged out`);
|
|
1857
|
-
});
|
|
1858
|
-
|
|
1859
|
-
// Emit events
|
|
1860
|
-
await emitter.emit("user:login", {
|
|
1861
|
-
userId: "user_123",
|
|
1862
|
-
timestamp: Date.now(),
|
|
1863
|
-
});
|
|
1864
|
-
|
|
1865
|
-
// Sync emit
|
|
1866
|
-
emitter.emitSync("user:logout", { userId: "user_123" });
|
|
1867
|
-
|
|
1868
|
-
// Error handling
|
|
1869
|
-
emitter.onError((data, error) => {
|
|
1870
|
-
console.error("Event error:", error);
|
|
1871
|
-
});
|
|
1872
|
-
|
|
1873
|
-
// Listener management
|
|
1874
|
-
const count = emitter.listenerCount("user:login");
|
|
1875
|
-
emitter.removeAllListeners("user:login");
|
|
1876
|
-
|
|
1877
|
-
// Factory function
|
|
1878
|
-
const events = createEventEmitter<{ message: string }>();
|
|
1879
|
-
```
|
|
1880
|
-
|
|
1881
|
-
### DiffUtils
|
|
1882
|
-
|
|
1883
|
-
- **EN**: Deep object comparison, patch generation/application, and merge strategies for tracking changes.
|
|
1884
|
-
**ES**: Comparación profunda de objetos, generación/aplicación de parches y estrategias de merge para rastrear cambios.
|
|
1885
|
-
|
|
1886
|
-
```ts
|
|
1887
|
-
import { DiffUtils } from "bytekit";
|
|
1888
|
-
|
|
1889
|
-
// Detect changes
|
|
1890
|
-
const old = { name: "John", age: 30, email: "john@example.com" };
|
|
1891
|
-
const new_ = { name: "Jane", age: 31, phone: "555-1234" };
|
|
1892
|
-
|
|
1893
|
-
const diff = DiffUtils.diff(old, new_);
|
|
1894
|
-
console.log(diff);
|
|
1895
|
-
// {
|
|
1896
|
-
// changed: ["name", "age"],
|
|
1897
|
-
// added: ["phone"],
|
|
1898
|
-
// removed: ["email"]
|
|
1899
|
-
// }
|
|
1900
|
-
|
|
1901
|
-
// Create patches (JSON Patch format)
|
|
1902
|
-
const patches = DiffUtils.createPatch(old, new_);
|
|
1903
|
-
console.log(patches);
|
|
1904
|
-
// [
|
|
1905
|
-
// { op: "replace", path: "name", value: "Jane" },
|
|
1906
|
-
// { op: "replace", path: "age", value: 31 },
|
|
1907
|
-
// { op: "add", path: "phone", value: "555-1234" },
|
|
1908
|
-
// { op: "remove", path: "email" }
|
|
1909
|
-
// ]
|
|
1910
|
-
|
|
1911
|
-
// Apply patches
|
|
1912
|
-
const result = DiffUtils.applyPatch(old, patches);
|
|
1913
|
-
console.log(result); // { name: "Jane", age: 31, phone: "555-1234" }
|
|
1914
|
-
|
|
1915
|
-
// Deep equality check
|
|
1916
|
-
DiffUtils.deepEqual({ a: { b: 1 } }, { a: { b: 1 } }); // true
|
|
1917
|
-
DiffUtils.deepEqual({ a: { b: 1 } }, { a: { b: 2 } }); // false
|
|
1918
|
-
```
|
|
1919
|
-
|
|
1920
|
-
### PollingHelper
|
|
1921
|
-
|
|
1922
|
-
- **EN**: Intelligent polling with exponential backoff, stop conditions, and max attempts for async operations.
|
|
1923
|
-
**ES**: Polling inteligente con backoff exponencial, condiciones de parada e intentos máximos para operaciones async.
|
|
1924
|
-
|
|
1925
|
-
```ts
|
|
1926
|
-
import { PollingHelper, createPoller } from "bytekit";
|
|
1927
|
-
|
|
1928
|
-
// Poll until condition is met
|
|
1929
|
-
const poller = new PollingHelper(
|
|
1930
|
-
async () => {
|
|
1931
|
-
const status = await checkJobStatus();
|
|
1932
|
-
return status === "completed";
|
|
1933
|
-
},
|
|
1934
|
-
{
|
|
1935
|
-
interval: 1000, // Start with 1 second
|
|
1936
|
-
maxAttempts: 30,
|
|
1937
|
-
backoffMultiplier: 1.5, // Exponential backoff
|
|
1938
|
-
stopCondition: (result) => result === true,
|
|
1939
|
-
}
|
|
1940
|
-
);
|
|
1941
|
-
|
|
1942
|
-
const result = await poller.start();
|
|
1943
|
-
console.log(result);
|
|
1944
|
-
// {
|
|
1945
|
-
// success: true,
|
|
1946
|
-
// attempts: 5,
|
|
1947
|
-
// lastResult: true,
|
|
1948
|
-
// totalTimeMs: 3500
|
|
1949
|
-
// }
|
|
1950
|
-
|
|
1951
|
-
// Handle failure
|
|
1952
|
-
if (!result.success) {
|
|
1953
|
-
console.log(`Failed after ${result.attempts} attempts`);
|
|
1954
|
-
}
|
|
1955
|
-
|
|
1956
|
-
// Factory function
|
|
1957
|
-
const filePoller = createPoller(
|
|
1958
|
-
async () => {
|
|
1959
|
-
return await fileExists("output.txt");
|
|
1960
|
-
},
|
|
1961
|
-
{ interval: 500, maxAttempts: 60 }
|
|
1962
|
-
);
|
|
1963
|
-
```
|
|
1964
|
-
|
|
1965
|
-
### CryptoUtils
|
|
1966
|
-
|
|
1967
|
-
- **EN**: Token/UUID generation, base64 encoding, hashing, HMAC, and constant-time comparison for security.
|
|
1968
|
-
**ES**: Generación de tokens/UUIDs, codificación base64, hashing, HMAC y comparación en tiempo constante para seguridad.
|
|
1969
|
-
|
|
1970
|
-
```ts
|
|
1971
|
-
import { CryptoUtils } from "bytekit";
|
|
1972
|
-
|
|
1973
|
-
// Generate secure tokens
|
|
1974
|
-
const token = CryptoUtils.generateToken(32); // 64 hex chars
|
|
1975
|
-
const uuid = CryptoUtils.generateUUID(); // v4 UUID
|
|
1976
|
-
|
|
1977
|
-
// Base64 encoding
|
|
1978
|
-
const encoded = CryptoUtils.base64Encode("Hello, World!");
|
|
1979
|
-
const decoded = CryptoUtils.base64Decode(encoded);
|
|
1980
|
-
|
|
1981
|
-
// URL-safe base64 (no +, /, =)
|
|
1982
|
-
const urlSafe = CryptoUtils.base64UrlEncode("data+with/special=chars");
|
|
1983
|
-
const restored = CryptoUtils.base64UrlDecode(urlSafe);
|
|
1984
|
-
|
|
1985
|
-
// Hashing
|
|
1986
|
-
const hash = await CryptoUtils.hash("password");
|
|
1987
|
-
const isValid = await CryptoUtils.verifyHash("password", hash);
|
|
1988
|
-
|
|
1989
|
-
// Constant-time comparison (prevents timing attacks)
|
|
1990
|
-
const match = CryptoUtils.constantTimeCompare(token1, token2);
|
|
1991
|
-
|
|
1992
|
-
// HMAC signing
|
|
1993
|
-
const signature = await CryptoUtils.hmac("message", "secret");
|
|
1994
|
-
```
|
|
1995
|
-
|
|
1996
|
-
### PaginationHelper
|
|
1997
|
-
|
|
1998
|
-
- **EN**: Offset-limit and cursor-based pagination with state tracking and navigation.
|
|
1999
|
-
**ES**: Paginación offset-limit y basada en cursores con seguimiento de estado y navegación.
|
|
2000
|
-
|
|
2001
|
-
```ts
|
|
2002
|
-
import { PaginationHelper, createPaginator } from "bytekit";
|
|
2003
|
-
|
|
2004
|
-
const items = Array.from({ length: 100 }, (_, i) => ({ id: i + 1 }));
|
|
2005
|
-
|
|
2006
|
-
// Offset-limit pagination
|
|
2007
|
-
const paginator = new PaginationHelper(items, {
|
|
2008
|
-
pageSize: 10,
|
|
2009
|
-
mode: "offset",
|
|
2010
|
-
});
|
|
2011
|
-
|
|
2012
|
-
// Get current page
|
|
2013
|
-
const page1 = paginator.getCurrentPage(); // First 10 items
|
|
2014
|
-
|
|
2015
|
-
// Navigate
|
|
2016
|
-
paginator.next();
|
|
2017
|
-
const page2 = paginator.getCurrentPage(); // Items 11-20
|
|
2018
|
-
|
|
2019
|
-
paginator.previous();
|
|
2020
|
-
const backToPage1 = paginator.getCurrentPage();
|
|
2021
|
-
|
|
2022
|
-
// Jump to specific page
|
|
2023
|
-
paginator.goToPage(5);
|
|
2024
|
-
|
|
2025
|
-
// Get state
|
|
2026
|
-
const state = paginator.getState();
|
|
2027
|
-
console.log(state);
|
|
2028
|
-
// {
|
|
2029
|
-
// currentPage: 5,
|
|
2030
|
-
// pageSize: 10,
|
|
2031
|
-
// total: 100,
|
|
2032
|
-
// totalPages: 10,
|
|
2033
|
-
// hasNextPage: true,
|
|
2034
|
-
// hasPreviousPage: true,
|
|
2035
|
-
// offset: 40,
|
|
2036
|
-
// limit: 10
|
|
2037
|
-
// }
|
|
2038
|
-
|
|
2039
|
-
// Cursor-based pagination
|
|
2040
|
-
const cursorPaginator = new PaginationHelper(items, {
|
|
2041
|
-
pageSize: 10,
|
|
2042
|
-
mode: "cursor",
|
|
2043
|
-
cursorField: "id",
|
|
2044
|
-
});
|
|
2045
|
-
|
|
2046
|
-
// Factory function
|
|
2047
|
-
const userPaginator = createPaginator(users, { pageSize: 20 });
|
|
2048
|
-
```
|
|
2049
|
-
|
|
2050
|
-
### CacheManager
|
|
2051
|
-
|
|
2052
|
-
- **EN**: Multi-tier cache (memory + localStorage) with TTL, LRU eviction, and statistics for performance optimization.
|
|
2053
|
-
**ES**: Cache multi-nivel (memoria + localStorage) con TTL, evicción LRU y estadísticas para optimización de rendimiento.
|
|
2054
|
-
|
|
2055
|
-
```ts
|
|
2056
|
-
import { CacheManager, createCacheManager } from "bytekit";
|
|
2057
|
-
|
|
2058
|
-
const cache = new CacheManager({
|
|
2059
|
-
maxSize: 100, // Max entries
|
|
2060
|
-
ttl: 5 * 60 * 1000, // 5 minutes default
|
|
2061
|
-
useLocalStorage: true, // Enable persistent cache
|
|
2062
|
-
});
|
|
2063
|
-
|
|
2064
|
-
// Set and get
|
|
2065
|
-
cache.set("user:123", { id: 123, name: "John" }, 60_000); // 1 minute TTL
|
|
2066
|
-
const user = cache.get("user:123");
|
|
2067
|
-
|
|
2068
|
-
// Get or compute
|
|
2069
|
-
const data = await cache.getOrCompute(
|
|
2070
|
-
"expensive:key",
|
|
2071
|
-
async () => {
|
|
2072
|
-
return await fetchExpensiveData();
|
|
2073
|
-
},
|
|
2074
|
-
10 * 60 * 1000
|
|
2075
|
-
); // Cache for 10 minutes
|
|
2076
|
-
|
|
2077
|
-
// Check if exists
|
|
2078
|
-
if (cache.has("user:123")) {
|
|
2079
|
-
console.log("User cached");
|
|
2080
|
-
}
|
|
2081
|
-
|
|
2082
|
-
// Remove and clear
|
|
2083
|
-
cache.remove("user:123");
|
|
2084
|
-
cache.clear();
|
|
2085
|
-
|
|
2086
|
-
// Statistics
|
|
2087
|
-
const stats = cache.getStats();
|
|
2088
|
-
console.log(stats);
|
|
2089
|
-
// {
|
|
2090
|
-
// hits: 42,
|
|
2091
|
-
// misses: 8,
|
|
2092
|
-
// hitRate: 0.84,
|
|
2093
|
-
// size: 15,
|
|
2094
|
-
// maxSize: 100
|
|
2095
|
-
// }
|
|
2096
|
-
|
|
2097
|
-
// Invalidate by pattern
|
|
2098
|
-
cache.invalidatePattern("user:*");
|
|
2099
|
-
|
|
2100
|
-
// Factory function
|
|
2101
|
-
const apiCache = createCacheManager({ maxSize: 50 });
|
|
2102
|
-
```
|
|
2103
|
-
|
|
2104
|
-
### CompressionUtils
|
|
2105
|
-
|
|
2106
|
-
- **EN**: String compression, base64 encoding, JSON minification, and gzip/deflate support for data optimization.
|
|
2107
|
-
**ES**: Compresión de strings, codificación base64, minificación JSON y soporte gzip/deflate para optimización de datos.
|
|
2108
|
-
|
|
2109
|
-
```ts
|
|
2110
|
-
import { CompressionUtils } from "bytekit";
|
|
2111
|
-
|
|
2112
|
-
// Compress and decompress
|
|
2113
|
-
const original = "Hello World Hello World Hello World";
|
|
2114
|
-
const compressed = CompressionUtils.compress(original);
|
|
2115
|
-
const decompressed = CompressionUtils.decompress(compressed);
|
|
2116
|
-
|
|
2117
|
-
// Base64 encoding
|
|
2118
|
-
const encoded = CompressionUtils.base64Encode("data");
|
|
2119
|
-
const decoded = CompressionUtils.base64Decode(encoded);
|
|
2120
|
-
|
|
2121
|
-
// URL-safe base64
|
|
2122
|
-
const urlSafe = CompressionUtils.base64UrlEncode("data+special/chars=");
|
|
2123
|
-
|
|
2124
|
-
// JSON utilities
|
|
2125
|
-
const json = '{ "name": "John", "age": 30 }';
|
|
2126
|
-
const minified = CompressionUtils.minifyJSON(json);
|
|
2127
|
-
const pretty = CompressionUtils.prettyJSON(minified, 2);
|
|
2128
|
-
|
|
2129
|
-
// Compression ratio
|
|
2130
|
-
const ratio = CompressionUtils.getCompressionRatio(original, compressed);
|
|
2131
|
-
console.log(`Compression: ${ratio.toFixed(2)}%`);
|
|
2132
|
-
|
|
2133
|
-
// Format bytes
|
|
2134
|
-
CompressionUtils.formatBytes(1024); // "1 KB"
|
|
2135
|
-
CompressionUtils.formatBytes(1024 * 1024); // "1 MB"
|
|
2136
|
-
|
|
2137
|
-
// Serialize/deserialize with compression
|
|
2138
|
-
const obj = { users: [{ id: 1, name: "John" }] };
|
|
2139
|
-
const serialized = CompressionUtils.serializeCompressed(obj);
|
|
2140
|
-
const restored = CompressionUtils.deserializeCompressed(serialized);
|
|
2141
|
-
|
|
2142
|
-
// Gzip (Node.js)
|
|
2143
|
-
const gzipped = await CompressionUtils.gzip(original);
|
|
2144
|
-
const gunzipped = await CompressionUtils.gunzip(gzipped);
|
|
2145
|
-
|
|
2146
|
-
// Deflate (Node.js)
|
|
2147
|
-
const deflated = await CompressionUtils.deflate(original);
|
|
2148
|
-
const inflated = await CompressionUtils.inflate(deflated);
|
|
2149
|
-
```
|
|
2150
|
-
|
|
2151
|
-
## Compatibility / Compatibilidad
|
|
2152
|
-
|
|
2153
|
-
- Node.js >= 18 (ESM, `fetch`, `AbortController`, `URL`).
|
|
2154
|
-
- Modern browsers (ships optional `cross-fetch` polyfill).
|
|
2155
|
-
|
|
2156
|
-
## CLI scaffolding / Generador CLI
|
|
2157
|
-
|
|
2158
|
-
**EN:** Install the package globally or invoke it with `npx`. The `sutils` binary provides two powerful commands: scaffolding resource folders with CRUD helpers and generating TypeScript types from API responses.
|
|
2159
|
-
**ES:** Instalá el paquete globalmente o usalo con `npx`. El binario `sutils` proporciona dos comandos poderosos: crear carpetas de recursos con helpers CRUD y generar tipos TypeScript desde respuestas de API.
|
|
2160
|
-
|
|
2161
|
-
### Create Command / Comando Create
|
|
2162
|
-
|
|
2163
|
-
```bash
|
|
2164
|
-
npx sutils create users
|
|
2165
|
-
```
|
|
2166
|
-
|
|
2167
|
-
**What is generated / Qué se genera:**
|
|
2168
|
-
|
|
2169
|
-
- `api/<resource>/index.ts`: typed CRUD helpers built on `bytekit`' `ApiClient`, including shape placeholders, filter helpers, and `list/get/create/update/delete` functions.
|
|
2170
|
-
- `hooks/<resource>/use<ResourcePlural>.ts`: Query library hooks (React Query or RTK Query, configurable via `--queryLib`) that invalidate the corresponding queries and wire mutations.
|
|
2171
|
-
- `hooks/<resource>/index.ts`: re-exports the generated hooks.
|
|
2172
|
-
|
|
2173
|
-
The generator accepts `--apiDir`, `--hooksDir`, `--route`, `--queryLib`, and `--force`; directories default to `api`/`hooks`, the route defaults to the resource name, `--queryLib` defaults to `react-query`, and `--force` overwrites existing files. It also respects nested resource paths like `admin/users`.
|
|
2174
|
-
|
|
2175
|
-
**Query Library Options / Opciones de Librería de Queries:**
|
|
2176
|
-
|
|
2177
|
-
- `--queryLib=react-query` (default): Generates hooks using `@tanstack/react-query` (React Query).
|
|
2178
|
-
- `--queryLib=rtk-query`: Generates API slice using `@reduxjs/toolkit/query/react` (RTK Query).
|
|
2179
|
-
|
|
2180
|
-
**Examples / Ejemplos:**
|
|
2181
|
-
|
|
2182
|
-
```bash
|
|
2183
|
-
# Create with React Query (default)
|
|
2184
|
-
npx sutils create users
|
|
2185
|
-
|
|
2186
|
-
# Create with RTK Query
|
|
2187
|
-
npx sutils create users --queryLib=rtk-query
|
|
2188
|
-
|
|
2189
|
-
# Create with custom directories and RTK Query
|
|
2190
|
-
npx sutils create payments --apiDir=services --hooksDir=app/hooks --route=/billing/payments --queryLib=rtk-query --force
|
|
2191
|
-
```
|
|
2192
|
-
|
|
2193
|
-
**React Query Setup / Configuración de React Query:**
|
|
2194
|
-
|
|
2195
|
-
React Query must be available in the consuming project:
|
|
2196
|
-
|
|
2197
|
-
```bash
|
|
2198
|
-
npm install @tanstack/react-query
|
|
2199
|
-
```
|
|
2200
|
-
|
|
2201
|
-
The generated hooks expect an `ApiClient` instance that you pass as the first argument:
|
|
2202
|
-
|
|
2203
|
-
```ts
|
|
2204
|
-
import { useUsers } from "./hooks/users";
|
|
2205
|
-
import { apiClient } from "./api/client";
|
|
2206
|
-
|
|
2207
|
-
export function UsersList() {
|
|
2208
|
-
const { data, isLoading } = useUsers(apiClient);
|
|
2209
|
-
// ...
|
|
2210
|
-
}
|
|
2211
|
-
```
|
|
2212
|
-
|
|
2213
|
-
**RTK Query Setup / Configuración de RTK Query:**
|
|
2214
|
-
|
|
2215
|
-
RTK Query must be available in the consuming project:
|
|
2216
|
-
|
|
2217
|
-
```bash
|
|
2218
|
-
npm install @reduxjs/toolkit react-redux
|
|
2219
|
-
```
|
|
2220
|
-
|
|
2221
|
-
The generated API slice should be added to your Redux store:
|
|
2222
|
-
|
|
2223
|
-
```ts
|
|
2224
|
-
import { configureStore } from "@reduxjs/toolkit";
|
|
2225
|
-
import { userApi } from "./hooks/users";
|
|
2226
|
-
|
|
2227
|
-
export const store = configureStore({
|
|
2228
|
-
reducer: {
|
|
2229
|
-
[userApi.reducerPath]: userApi.reducer,
|
|
2230
|
-
},
|
|
2231
|
-
middleware: (getDefaultMiddleware) =>
|
|
2232
|
-
getDefaultMiddleware().concat(userApi.middleware),
|
|
2233
|
-
});
|
|
2234
|
-
```
|
|
2235
|
-
|
|
2236
|
-
Then use the generated hooks in your components:
|
|
2237
|
-
|
|
2238
|
-
```ts
|
|
2239
|
-
import { useListUsersQuery, useCreateUserMutation } from "./hooks/users";
|
|
2240
|
-
|
|
2241
|
-
export function UsersList() {
|
|
2242
|
-
const { data, isLoading } = useListUsersQuery();
|
|
2243
|
-
const [createUser] = useCreateUserMutation();
|
|
2244
|
-
// ...
|
|
2245
|
-
}
|
|
2246
|
-
```
|
|
2247
|
-
|
|
2248
|
-
### Types Command / Comando Types
|
|
2249
|
-
|
|
2250
|
-
**EN:** Generate TypeScript type definitions automatically from API responses. Perfect for quickly creating types without manual work.
|
|
2251
|
-
**ES:** Genera definiciones de tipos TypeScript automáticamente desde respuestas de API. Perfecto para crear tipos rápidamente sin trabajo manual.
|
|
2252
|
-
|
|
2253
|
-
```bash
|
|
2254
|
-
# Generate types from GET endpoint
|
|
2255
|
-
npx sutils types https://api.example.com/users
|
|
2256
|
-
|
|
2257
|
-
# Generate types with custom output and name
|
|
2258
|
-
npx sutils types https://api.example.com/users --output=user-types.ts --name=User
|
|
2259
|
-
|
|
2260
|
-
# Generate types from POST endpoint with custom headers
|
|
2261
|
-
npx sutils types https://api.example.com/users --method=POST --header=Authorization:Bearer\ token
|
|
2262
|
-
```
|
|
2263
|
-
|
|
2264
|
-
**Options / Opciones:**
|
|
2265
|
-
|
|
2266
|
-
- `--method=<METHOD>`: HTTP method (default: GET). Supports GET, POST, PUT, PATCH, DELETE.
|
|
2267
|
-
- `--output=<file>`: Output file path (default: types.ts).
|
|
2268
|
-
- `--name=<name>`: Type name (default: ApiResponse).
|
|
2269
|
-
- `--header=<key:value>`: Custom header (can be used multiple times).
|
|
2270
|
-
|
|
2271
|
-
**Example output / Ejemplo de salida:**
|
|
2272
|
-
|
|
2273
|
-
```ts
|
|
2274
|
-
// Auto-generated types from API response
|
|
2275
|
-
// Generated at 2024-12-20T10:30:00.000Z
|
|
2276
|
-
|
|
2277
|
-
export interface ApiResponse {
|
|
2278
|
-
id: string;
|
|
2279
|
-
name: string;
|
|
2280
|
-
email: string;
|
|
2281
|
-
createdAt: string;
|
|
2282
|
-
updatedAt: string;
|
|
2283
|
-
}
|
|
2284
|
-
```
|
|
2285
|
-
|
|
2286
|
-
## TypeScript Compatibility / Compatibilidad con TypeScript
|
|
2287
|
-
|
|
2288
|
-
**EN:** The package is tested and verified to work with multiple TypeScript versions.
|
|
2289
|
-
**ES:** El paquete está testeado y verificado para funcionar con múltiples versiones de TypeScript.
|
|
2290
|
-
|
|
2291
|
-
### Supported Versions / Versiones Soportadas
|
|
2292
|
-
|
|
2293
|
-
| Version | Status | Notes |
|
|
2294
|
-
| ------- | ------------------- | --------------------------------------------- |
|
|
2295
|
-
| 5.9.3+ | ✅ **Recommended** | Full ES2023 support, modern module resolution |
|
|
2296
|
-
| 6.0.0+ | ✅ **Future-proof** | Compatible with upcoming TypeScript versions |
|
|
2297
|
-
| 5.6.3 | ⚠️ Partial | Requires ES2022 target instead of ES2023 |
|
|
2298
|
-
| < 5.6 | ❌ Not supported | Missing ES2023 features and modern tooling |
|
|
2299
|
-
|
|
2300
|
-
### Recommended Configuration / Configuración Recomendada
|
|
2301
|
-
|
|
2302
|
-
**EN:** Use TypeScript 5.9.3 or later for optimal compatibility and zero deprecation warnings.
|
|
2303
|
-
**ES:** Usá TypeScript 5.9.3 o posterior para compatibilidad óptima y sin warnings de deprecación.
|
|
2304
|
-
|
|
2305
|
-
```bash
|
|
2306
|
-
npm install --save-dev typescript@^5.9.3
|
|
2307
|
-
```
|
|
2308
|
-
|
|
2309
|
-
### ES2023 Features Used / Características ES2023 Utilizadas
|
|
2310
|
-
|
|
2311
|
-
The package leverages these ES2023 features:
|
|
2312
|
-
|
|
2313
|
-
- `Array.prototype.findLast()` - Find last element matching predicate
|
|
2314
|
-
- `Array.prototype.findLastIndex()` - Find last index matching predicate
|
|
2315
|
-
- `Array.prototype.at()` - Access array elements with negative indices
|
|
2316
|
-
- `String.prototype.at()` - Access string characters with negative indices
|
|
2317
|
-
- `Object.hasOwn()` - Check object property ownership
|
|
2318
|
-
- Experimental Decorators
|
|
2319
|
-
|
|
2320
|
-
### Migration from Older Versions / Migración desde Versiones Antiguas
|
|
2321
|
-
|
|
2322
|
-
**From TypeScript 5.6 to 5.9:**
|
|
2323
|
-
|
|
2324
|
-
```bash
|
|
2325
|
-
# 1. Update TypeScript
|
|
2326
|
-
npm install --save-dev typescript@^5.9.3
|
|
2327
|
-
|
|
2328
|
-
# 2. Update tsconfig.json
|
|
2329
|
-
# Change target from ES2022 to ES2023
|
|
2330
|
-
# Change moduleResolution from node to bundler
|
|
2331
|
-
|
|
2332
|
-
# 3. No code changes needed
|
|
2333
|
-
npm run build
|
|
2334
|
-
```
|
|
2335
|
-
|
|
2336
|
-
**From TypeScript 5.9 to 6.0+:**
|
|
2337
|
-
|
|
2338
|
-
```bash
|
|
2339
|
-
# 1. Update TypeScript
|
|
2340
|
-
npm install --save-dev typescript@^6.0.0
|
|
2341
|
-
|
|
2342
|
-
# 2. No tsconfig changes needed (already future-proof)
|
|
2343
|
-
|
|
2344
|
-
# 3. No code changes needed
|
|
2345
|
-
npm run build
|
|
2346
|
-
```
|
|
2347
|
-
|
|
2348
|
-
### Testing TypeScript Compatibility / Testeando Compatibilidad
|
|
2349
|
-
|
|
2350
|
-
**EN:** Run the test suite to verify TypeScript compatibility:
|
|
2351
|
-
**ES:** Ejecutá la suite de tests para verificar compatibilidad con TypeScript:
|
|
2352
|
-
|
|
2353
|
-
```bash
|
|
2354
|
-
npm run test
|
|
2355
|
-
```
|
|
2356
|
-
|
|
2357
|
-
## Migration from @sebamar88/utils / Migración desde @sebamar88/utils
|
|
2358
|
-
|
|
2359
|
-
**EN:** If you were using `@sebamar88/utils`, simply update your package name to `bytekit`. No code changes are required—all APIs remain the same.
|
|
2360
|
-
|
|
2361
|
-
**ES:** Si estabas usando `@sebamar88/utils`, simplemente actualiza el nombre del paquete a `bytekit`. No se requieren cambios de código—todas las APIs permanecen igual.
|
|
2362
|
-
|
|
2363
|
-
### Before / Antes
|
|
2364
|
-
|
|
2365
|
-
```bash
|
|
2366
|
-
npm install @sebamar88/utils
|
|
2367
|
-
```
|
|
2368
|
-
|
|
2369
|
-
```typescript
|
|
2370
|
-
import { ApiClient, DateUtils } from "@sebamar88/utils";
|
|
2371
|
-
```
|
|
2372
|
-
|
|
2373
|
-
### After / Después
|
|
2374
|
-
|
|
2375
|
-
```bash
|
|
2376
|
-
npm install bytekit
|
|
2377
|
-
```
|
|
2378
|
-
|
|
2379
|
-
```typescript
|
|
2380
|
-
import { ApiClient, DateUtils } from "bytekit";
|
|
2381
|
-
```
|
|
2382
|
-
|
|
2383
|
-
### Version History / Historial de Versiones
|
|
2384
|
-
|
|
2385
|
-
- **v0.1.10+** - `bytekit` (current)
|
|
2386
|
-
- **v0.1.9 and earlier** - `@sebamar88/utils` (deprecated)
|
|
2387
|
-
|
|
2388
|
-
**EN:** The package was renamed from `@sebamar88/utils` to `bytekit` starting with v0.1.10. All functionality remains the same.
|
|
2389
|
-
|
|
2390
|
-
**ES:** El paquete fue renombrado de `@sebamar88/utils` a `bytekit` a partir de v0.1.10. Toda la funcionalidad permanece igual.
|
|
2391
|
-
|
|
2392
|
-
## License / Licencia
|
|
213
|
+
---
|
|
2393
214
|
|
|
2394
|
-
|
|
215
|
+
**💡 Need help?** Check the **[Wiki](https://github.com/sebamar88/bytekit/wiki)** or **[open an issue](https://github.com/sebamar88/bytekit/issues)**.
|