bytekit 0.1.12

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