@gzl10/ts-helpers 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/CHANGELOG.md +320 -0
  2. package/README.md +233 -0
  3. package/USAGE-GUIDE.md +800 -0
  4. package/dist/browser/async.js +15 -0
  5. package/dist/browser/async.js.map +1 -0
  6. package/dist/browser/chunk-4O7ZPIJN.js +383 -0
  7. package/dist/browser/chunk-4O7ZPIJN.js.map +1 -0
  8. package/dist/browser/chunk-75XNTC34.js +60 -0
  9. package/dist/browser/chunk-75XNTC34.js.map +1 -0
  10. package/dist/browser/chunk-C3D7YZVE.js +299 -0
  11. package/dist/browser/chunk-C3D7YZVE.js.map +1 -0
  12. package/dist/browser/chunk-CZL6C2EI.js +452 -0
  13. package/dist/browser/chunk-CZL6C2EI.js.map +1 -0
  14. package/dist/browser/chunk-D4FZFIVA.js +240 -0
  15. package/dist/browser/chunk-D4FZFIVA.js.map +1 -0
  16. package/dist/browser/chunk-IL7NG7IC.js +72 -0
  17. package/dist/browser/chunk-IL7NG7IC.js.map +1 -0
  18. package/dist/browser/chunk-NSBPE2FW.js +17 -0
  19. package/dist/browser/chunk-NSBPE2FW.js.map +1 -0
  20. package/dist/browser/chunk-SLQVNPTH.js +27 -0
  21. package/dist/browser/chunk-SLQVNPTH.js.map +1 -0
  22. package/dist/browser/chunk-WG7ILCUB.js +195 -0
  23. package/dist/browser/chunk-WG7ILCUB.js.map +1 -0
  24. package/dist/browser/chunk-WJA4JDMZ.js +278 -0
  25. package/dist/browser/chunk-WJA4JDMZ.js.map +1 -0
  26. package/dist/browser/chunk-ZFVYLUTT.js +65 -0
  27. package/dist/browser/chunk-ZFVYLUTT.js.map +1 -0
  28. package/dist/browser/chunk-ZYTSVMTI.js +263 -0
  29. package/dist/browser/chunk-ZYTSVMTI.js.map +1 -0
  30. package/dist/browser/dates.js +78 -0
  31. package/dist/browser/dates.js.map +1 -0
  32. package/dist/browser/environment-detection.js +21 -0
  33. package/dist/browser/environment-detection.js.map +1 -0
  34. package/dist/browser/environment.js +34 -0
  35. package/dist/browser/environment.js.map +1 -0
  36. package/dist/browser/errors.js +18 -0
  37. package/dist/browser/errors.js.map +1 -0
  38. package/dist/browser/index.js +412 -0
  39. package/dist/browser/index.js.map +1 -0
  40. package/dist/browser/math.js +51 -0
  41. package/dist/browser/math.js.map +1 -0
  42. package/dist/browser/number.js +10 -0
  43. package/dist/browser/number.js.map +1 -0
  44. package/dist/browser/objects.js +31 -0
  45. package/dist/browser/objects.js.map +1 -0
  46. package/dist/browser/strings.js +80 -0
  47. package/dist/browser/strings.js.map +1 -0
  48. package/dist/browser/validation-core.js +54 -0
  49. package/dist/browser/validation-core.js.map +1 -0
  50. package/dist/browser/validation-crypto.js +28 -0
  51. package/dist/browser/validation-crypto.js.map +1 -0
  52. package/dist/browser/validators.js +98 -0
  53. package/dist/browser/validators.js.map +1 -0
  54. package/dist/cjs/async.js +86 -0
  55. package/dist/cjs/async.js.map +1 -0
  56. package/dist/cjs/dates.js +285 -0
  57. package/dist/cjs/dates.js.map +1 -0
  58. package/dist/cjs/environment-detection.js +84 -0
  59. package/dist/cjs/environment-detection.js.map +1 -0
  60. package/dist/cjs/environment.js +261 -0
  61. package/dist/cjs/environment.js.map +1 -0
  62. package/dist/cjs/errors.js +80 -0
  63. package/dist/cjs/errors.js.map +1 -0
  64. package/dist/cjs/index.js +2035 -0
  65. package/dist/cjs/index.js.map +1 -0
  66. package/dist/cjs/math.js +388 -0
  67. package/dist/cjs/math.js.map +1 -0
  68. package/dist/cjs/number.js +37 -0
  69. package/dist/cjs/number.js.map +1 -0
  70. package/dist/cjs/objects.js +249 -0
  71. package/dist/cjs/objects.js.map +1 -0
  72. package/dist/cjs/strings.js +253 -0
  73. package/dist/cjs/strings.js.map +1 -0
  74. package/dist/cjs/validation.js +450 -0
  75. package/dist/cjs/validation.js.map +1 -0
  76. package/dist/esm/async.js +15 -0
  77. package/dist/esm/async.js.map +1 -0
  78. package/dist/esm/chunk-4O7ZPIJN.js +383 -0
  79. package/dist/esm/chunk-4O7ZPIJN.js.map +1 -0
  80. package/dist/esm/chunk-75XNTC34.js +60 -0
  81. package/dist/esm/chunk-75XNTC34.js.map +1 -0
  82. package/dist/esm/chunk-BDOBKBKA.js +72 -0
  83. package/dist/esm/chunk-BDOBKBKA.js.map +1 -0
  84. package/dist/esm/chunk-C3D7YZVE.js +299 -0
  85. package/dist/esm/chunk-C3D7YZVE.js.map +1 -0
  86. package/dist/esm/chunk-CZL6C2EI.js +452 -0
  87. package/dist/esm/chunk-CZL6C2EI.js.map +1 -0
  88. package/dist/esm/chunk-EBLSTOEC.js +263 -0
  89. package/dist/esm/chunk-EBLSTOEC.js.map +1 -0
  90. package/dist/esm/chunk-NSBPE2FW.js +17 -0
  91. package/dist/esm/chunk-NSBPE2FW.js.map +1 -0
  92. package/dist/esm/chunk-SLQVNPTH.js +27 -0
  93. package/dist/esm/chunk-SLQVNPTH.js.map +1 -0
  94. package/dist/esm/chunk-WG7ILCUB.js +195 -0
  95. package/dist/esm/chunk-WG7ILCUB.js.map +1 -0
  96. package/dist/esm/chunk-WJA4JDMZ.js +278 -0
  97. package/dist/esm/chunk-WJA4JDMZ.js.map +1 -0
  98. package/dist/esm/chunk-ZFVYLUTT.js +65 -0
  99. package/dist/esm/chunk-ZFVYLUTT.js.map +1 -0
  100. package/dist/esm/dates.js +78 -0
  101. package/dist/esm/dates.js.map +1 -0
  102. package/dist/esm/environment-detection.js +21 -0
  103. package/dist/esm/environment-detection.js.map +1 -0
  104. package/dist/esm/environment.js +34 -0
  105. package/dist/esm/environment.js.map +1 -0
  106. package/dist/esm/errors.js +18 -0
  107. package/dist/esm/errors.js.map +1 -0
  108. package/dist/esm/index.js +380 -0
  109. package/dist/esm/index.js.map +1 -0
  110. package/dist/esm/math.js +51 -0
  111. package/dist/esm/math.js.map +1 -0
  112. package/dist/esm/number.js +10 -0
  113. package/dist/esm/number.js.map +1 -0
  114. package/dist/esm/objects.js +31 -0
  115. package/dist/esm/objects.js.map +1 -0
  116. package/dist/esm/strings.js +80 -0
  117. package/dist/esm/strings.js.map +1 -0
  118. package/dist/esm/validation.js +54 -0
  119. package/dist/esm/validation.js.map +1 -0
  120. package/dist/node/async.js +93 -0
  121. package/dist/node/async.js.map +1 -0
  122. package/dist/node/csv.js +102 -0
  123. package/dist/node/csv.js.map +1 -0
  124. package/dist/node/data.js +880 -0
  125. package/dist/node/data.js.map +1 -0
  126. package/dist/node/dates.js +324 -0
  127. package/dist/node/dates.js.map +1 -0
  128. package/dist/node/environment.js +278 -0
  129. package/dist/node/environment.js.map +1 -0
  130. package/dist/node/errors.js +89 -0
  131. package/dist/node/errors.js.map +1 -0
  132. package/dist/node/index.js +3151 -0
  133. package/dist/node/index.js.map +1 -0
  134. package/dist/node/json.js +107 -0
  135. package/dist/node/json.js.map +1 -0
  136. package/dist/node/math.js +413 -0
  137. package/dist/node/math.js.map +1 -0
  138. package/dist/node/number.js +42 -0
  139. package/dist/node/number.js.map +1 -0
  140. package/dist/node/objects.js +264 -0
  141. package/dist/node/objects.js.map +1 -0
  142. package/dist/node/strings.js +293 -0
  143. package/dist/node/strings.js.map +1 -0
  144. package/dist/node/tree.js +89 -0
  145. package/dist/node/tree.js.map +1 -0
  146. package/dist/node/validation-core.js +477 -0
  147. package/dist/node/validation-core.js.map +1 -0
  148. package/dist/node/validation-crypto.js +179 -0
  149. package/dist/node/validation-crypto.js.map +1 -0
  150. package/dist/node/validation.js +677 -0
  151. package/dist/node/validation.js.map +1 -0
  152. package/dist/node/validators.js +123 -0
  153. package/dist/node/validators.js.map +1 -0
  154. package/dist/node-esm/async.js +15 -0
  155. package/dist/node-esm/async.js.map +1 -0
  156. package/dist/node-esm/chunk-3YOF7NPT.js +299 -0
  157. package/dist/node-esm/chunk-3YOF7NPT.js.map +1 -0
  158. package/dist/node-esm/chunk-64TBXJQS.js +263 -0
  159. package/dist/node-esm/chunk-64TBXJQS.js.map +1 -0
  160. package/dist/node-esm/chunk-75XNTC34.js +60 -0
  161. package/dist/node-esm/chunk-75XNTC34.js.map +1 -0
  162. package/dist/node-esm/chunk-C4PKXIPB.js +278 -0
  163. package/dist/node-esm/chunk-C4PKXIPB.js.map +1 -0
  164. package/dist/node-esm/chunk-CMDFZME3.js +452 -0
  165. package/dist/node-esm/chunk-CMDFZME3.js.map +1 -0
  166. package/dist/node-esm/chunk-DZZPUYMP.js +74 -0
  167. package/dist/node-esm/chunk-DZZPUYMP.js.map +1 -0
  168. package/dist/node-esm/chunk-HTSEHRHI.js +195 -0
  169. package/dist/node-esm/chunk-HTSEHRHI.js.map +1 -0
  170. package/dist/node-esm/chunk-JCAUVOPH.js +27 -0
  171. package/dist/node-esm/chunk-JCAUVOPH.js.map +1 -0
  172. package/dist/node-esm/chunk-KBHE3K2F.js +505 -0
  173. package/dist/node-esm/chunk-KBHE3K2F.js.map +1 -0
  174. package/dist/node-esm/chunk-LYTET5NX.js +65 -0
  175. package/dist/node-esm/chunk-LYTET5NX.js.map +1 -0
  176. package/dist/node-esm/chunk-PZ5AY32C.js +10 -0
  177. package/dist/node-esm/chunk-PZ5AY32C.js.map +1 -0
  178. package/dist/node-esm/chunk-UKGXL2QO.js +383 -0
  179. package/dist/node-esm/chunk-UKGXL2QO.js.map +1 -0
  180. package/dist/node-esm/chunk-XAEYT23H.js +164 -0
  181. package/dist/node-esm/chunk-XAEYT23H.js.map +1 -0
  182. package/dist/node-esm/csv.js +63 -0
  183. package/dist/node-esm/csv.js.map +1 -0
  184. package/dist/node-esm/data.js +32 -0
  185. package/dist/node-esm/data.js.map +1 -0
  186. package/dist/node-esm/dates.js +78 -0
  187. package/dist/node-esm/dates.js.map +1 -0
  188. package/dist/node-esm/environment.js +34 -0
  189. package/dist/node-esm/environment.js.map +1 -0
  190. package/dist/node-esm/errors.js +18 -0
  191. package/dist/node-esm/errors.js.map +1 -0
  192. package/dist/node-esm/index.js +426 -0
  193. package/dist/node-esm/index.js.map +1 -0
  194. package/dist/node-esm/json.js +68 -0
  195. package/dist/node-esm/json.js.map +1 -0
  196. package/dist/node-esm/math.js +51 -0
  197. package/dist/node-esm/math.js.map +1 -0
  198. package/dist/node-esm/number.js +10 -0
  199. package/dist/node-esm/number.js.map +1 -0
  200. package/dist/node-esm/objects.js +31 -0
  201. package/dist/node-esm/objects.js.map +1 -0
  202. package/dist/node-esm/strings.js +80 -0
  203. package/dist/node-esm/strings.js.map +1 -0
  204. package/dist/node-esm/tree.js +8 -0
  205. package/dist/node-esm/tree.js.map +1 -0
  206. package/dist/node-esm/validation-core.js +54 -0
  207. package/dist/node-esm/validation-core.js.map +1 -0
  208. package/dist/node-esm/validation-crypto.js +26 -0
  209. package/dist/node-esm/validation-crypto.js.map +1 -0
  210. package/dist/node-esm/validation.js +606 -0
  211. package/dist/node-esm/validation.js.map +1 -0
  212. package/dist/node-esm/validators.js +98 -0
  213. package/dist/node-esm/validators.js.map +1 -0
  214. package/dist/types/async-C8gvbSG-.d.ts +453 -0
  215. package/dist/types/async.d.ts +1 -0
  216. package/dist/types/csv.d.ts +226 -0
  217. package/dist/types/data.d.ts +1561 -0
  218. package/dist/types/dates-hTiE0Z11.d.ts +298 -0
  219. package/dist/types/dates.d.ts +1 -0
  220. package/dist/types/environment-B8eLS7KT.d.ts +420 -0
  221. package/dist/types/environment-detection.d.ts +102 -0
  222. package/dist/types/environment.d.ts +1 -0
  223. package/dist/types/errors.d.ts +147 -0
  224. package/dist/types/index.d.ts +211 -0
  225. package/dist/types/json.d.ts +284 -0
  226. package/dist/types/math-BQ9Lwdp7.d.ts +2060 -0
  227. package/dist/types/math.d.ts +1 -0
  228. package/dist/types/number-CYnQfLWj.d.ts +44 -0
  229. package/dist/types/number.d.ts +1 -0
  230. package/dist/types/objects-BohS8GCS.d.ts +1185 -0
  231. package/dist/types/objects.d.ts +1 -0
  232. package/dist/types/strings-CiqRPYLL.d.ts +1349 -0
  233. package/dist/types/strings.d.ts +1 -0
  234. package/dist/types/tree.d.ts +284 -0
  235. package/dist/types/validation-core-DfHF8rCG.d.ts +238 -0
  236. package/dist/types/validation-crypto-browser.d.ts +56 -0
  237. package/dist/types/validation-crypto-node.d.ts +31 -0
  238. package/dist/types/validation.d.ts +1 -0
  239. package/dist/types/validators.d.ts +216 -0
  240. package/package.json +253 -0
package/USAGE-GUIDE.md ADDED
@@ -0,0 +1,800 @@
1
+ # @g10/ts-helpers v4.1.x - Guía Técnica Completa
2
+
3
+ 🚀 **v4.1.0** - Nuevas funciones para manejo de configuración, parsing de env
4
+ vars y manipulación de objetos anidados.
5
+
6
+ ## 📦 Instalación y Configuración
7
+
8
+ ```bash
9
+ # Instalación
10
+ pnpm add @g10/ts-helpers # Recomendado
11
+ npm install @g10/ts-helpers
12
+
13
+ # Requisitos
14
+ Node.js >=18.0.0
15
+ TypeScript >=5.0.0
16
+ pnpm >=8.0.0 (recomendado)
17
+ ```
18
+
19
+ ## 🎯 Patrones de Uso v4.0.0
20
+
21
+ ### 1. API Plana (Recomendado - 150+ funciones)
22
+
23
+ ```typescript
24
+ import g from '@g10/ts-helpers'
25
+
26
+ // 🔐 Validación Española (30+ funciones)
27
+ g.validateNIF('12345678Z') // Validador NIF
28
+ g.validateNIE('X0000000T') // Validador NIE
29
+ g.validateCIF('A12345674') // Validador CIF
30
+ g.isValidSpanishIBAN('ES...') // IBAN español
31
+ g.isValidSpanishPhone('+34612345678') // Teléfono móvil
32
+ g.isValidSpanishPostalCode('28001') // Código postal
33
+
34
+ // 🔧 Generadores Españoles (15+ funciones)
35
+ g.generateSpanishNIF() // '12345678Z'
36
+ g.generateSpanishNIE() // 'X1234567L'
37
+ g.generateSpanishCIF() // 'A12345674'
38
+ g.generateSpanishIBAN() // 'ES9121000418450200051332'
39
+ g.generateSpanishPostalCode() // '28001'
40
+ g.generatePassword({ length: 12 }) // Contraseña segura
41
+
42
+ // 📝 String Manipulation (25+ funciones)
43
+ g.toCamelCase('hello-world') // 'helloWorld'
44
+ g.toKebabCase('HelloWorld') // 'hello-world'
45
+ g.toSnakeCase('HelloWorld') // 'hello_world'
46
+ g.toPascalCase('hello-world') // 'HelloWorld'
47
+ g.capitalizeFirst('hello') // 'Hello'
48
+ g.sanitizeString('<script>alert</script>') // ''
49
+ g.removeAccents('café') // 'cafe'
50
+ g.truncateString('long text', 10) // 'long te...'
51
+
52
+ // 🗂️ Objects & Arrays (20+ funciones)
53
+ g.deepEqual(obj1, obj2) // Comparación profunda
54
+ g.setDeepValue(config, 'database.host', 'localhost') // 🆕 v4.1.0
55
+ g.getDeepValue(config, 'database.port', 5432) // 🆕 v4.1.0
56
+ g.updateArrayElementsBy(array, criteria, updates)
57
+ g.deleteArrayElementsBy(array, criteria)
58
+ g.getShallowProperties(obj) // Solo propiedades primitivas
59
+ g.calculateDifferences(oldObj, newObj)
60
+ g.generateCrcHash(data) // Hash CRC32
61
+
62
+ // 📅 Fechas con dayjs (25+ funciones)
63
+ g.formatNow('DD/MM/YYYY') // '15/01/2025'
64
+ g.format(date, 'DD/MM/YYYY HH:mm') // Formato personalizado
65
+ g.addDays(date, 7) // Añadir días
66
+ g.diffDays(date1, date2) // Diferencia en días
67
+ g.diffBusinessDays(date1, date2) // Días laborables
68
+ g.isWeekday(date) // true si es día laborable
69
+ g.fromNow(date) // 'hace 2 horas'
70
+
71
+ // 🔢 Math & Statistics (30+ funciones)
72
+ g.calculateMedian([1, 2, 3, 4, 5]) // 3
73
+ g.calculateMode([1, 1, 2, 3]) // [1]
74
+ g.calculateStandardDeviation(data) // Desviación estándar
75
+ g.calculatePercentile(data, 95) // Percentil 95
76
+ g.calculateNPV([-1000, 300, 400], 0.1) // NPV financiero
77
+ g.calculateIRR(cashFlows) // Tasa interna retorno
78
+ g.simpleKMeans(points, 3) // Clustering K-means
79
+
80
+ // ⏱️ Async Operations (5+ funciones)
81
+ await g.sleep(1000) // Pausa 1 segundo
82
+ await g.wait(1000) // Alias de sleep
83
+ await g.runBatch(promises, 5) // Ejecuta en lotes de 5
84
+
85
+ // 📊 Data Import/Export (17+ funciones)
86
+ await g.exportData(data, 'file.csv') // Auto-detecta formato
87
+ await g.exportData(data, 'file.json')
88
+ await g.importData('file.xlsx') // Importa automáticamente
89
+ g.detectFormatFromFilename('file.csv') // 'csv'
90
+
91
+ // 📁 Detección Universal de Formatos (80+ tipos)
92
+ g.detectFileExtension('report.xlsx') // 'xlsx'
93
+ g.detectFileExtension('presentation.pptx') // 'pptx'
94
+ g.detectUniversalFormat('document.pdf')
95
+ // { extension: 'pdf', category: 'document', mimeType: 'application/pdf', isText: false, isBinary: true }
96
+
97
+ // 🌍 Environment Detection (15+ funciones)
98
+ g.isNode // true en Node.js
99
+ g.isBrowser // true en browser
100
+ g.isDevelopment() // true en desarrollo
101
+ g.isDevelopment(req) // Con soporte Express
102
+ g.isProduction() // true en producción
103
+ g.parseEnvValue('true') // true (boolean) 🆕 v4.1.0
104
+ g.parseEnvValue('[1,2,3]') // [1,2,3] (array) 🆕 v4.1.0
105
+ g.parseEnvValue('{"key":"val"}') // {key: 'val'} 🆕 v4.1.0
106
+ g.detectProtocol(req) // 'https' con headers proxy
107
+ g.detectHostname(req) // Hostname con X-Forwarded-Host
108
+ g.isLocalhost('localhost') // true
109
+ g.isPrivateIP('192.168.1.1') // true
110
+
111
+ // ⚙️ Path & Config Utilities (5+ funciones) 🆕 v4.1.0
112
+ g.matchPathPattern('features.auth', 'features.*') // true
113
+ g.matchPathPattern('features.auth.enabled', 'features.*.enabled') // true
114
+ g.isValidDotNotationPath('database.host') // true
115
+ g.isValidDotNotationPath('.invalid') // false
116
+ g.envKeyToPath('NX_FEATURES_AUTH') // 'features.auth'
117
+ g.pathToEnvKey('features.auth', 'APP') // 'APP_FEATURES_AUTH'
118
+ g.getEnvironmentInfo(req) // Info completa con criterios
119
+ ```
120
+
121
+ ### 2. Importaciones Específicas (Tree-shaking)
122
+
123
+ ```typescript
124
+ // Módulos específicos para bundles mínimos
125
+ import {
126
+ validateNIF,
127
+ generateSpanishNIF,
128
+ isValidCIF,
129
+ } from '@g10/ts-helpers/validation'
130
+ import {
131
+ toCamelCase,
132
+ sanitizeString,
133
+ removeAccents,
134
+ } from '@g10/ts-helpers/strings'
135
+ import { deepEqual, updateArrayElementsBy } from '@g10/ts-helpers/objects'
136
+ import { formatNow, addDays, diffDays } from '@g10/ts-helpers/dates'
137
+ import { calculateNPV, calculateMedian } from '@g10/ts-helpers/math'
138
+ import { sleep, runBatch } from '@g10/ts-helpers/async'
139
+ import { exportData, importData } from '@g10/ts-helpers/data'
140
+ import {
141
+ isDevelopment,
142
+ detectProtocol,
143
+ getEnvironmentInfo,
144
+ parseEnvValue, // 🆕 v4.1.0
145
+ } from '@g10/ts-helpers/environment'
146
+ import {
147
+ setDeepValue, // 🆕 v4.1.0
148
+ getDeepValue, // 🆕 v4.1.0
149
+ deepEqual,
150
+ updateArrayElementsBy,
151
+ } from '@g10/ts-helpers/objects'
152
+ import {
153
+ matchPathPattern, // 🆕 v4.1.0
154
+ envKeyToPath, // 🆕 v4.1.0
155
+ pathToEnvKey, // 🆕 v4.1.0
156
+ toCamelCase,
157
+ sanitizeString,
158
+ } from '@g10/ts-helpers/strings'
159
+ import {
160
+ isValidDotNotationPath, // 🆕 v4.1.0
161
+ validateNIF,
162
+ } from '@g10/ts-helpers/validation'
163
+ ```
164
+
165
+ ## 🆕 Novedades v4.1.0: Casos de Uso Prácticos
166
+
167
+ ### Caso 1: Configuración desde Variables de Entorno
168
+
169
+ ```typescript
170
+ import g from '@g10/ts-helpers'
171
+
172
+ // Parsing automático de env vars a tipos nativos
173
+ const config = {
174
+ debug: g.parseEnvValue(process.env.DEBUG), // 'true' → true
175
+ port: g.parseEnvValue(process.env.PORT), // '3000' → 3000
176
+ features: g.parseEnvValue(process.env.FEATURES), // 'auth,api' → ['auth','api']
177
+ database: g.parseEnvValue(process.env.DB_CONFIG), // '{"host":"localhost"}' → object
178
+ }
179
+
180
+ // Construir config anidada desde env vars planas
181
+ const appConfig = {}
182
+ Object.entries(process.env).forEach(([key, value]) => {
183
+ if (key.startsWith('APP_')) {
184
+ const path = g.envKeyToPath(key, 'APP') // 'APP_DATABASE_HOST' → 'database.host'
185
+ g.setDeepValue(appConfig, path, g.parseEnvValue(value))
186
+ }
187
+ })
188
+ // Resultado: { database: { host: 'localhost', port: 5432 }, cache: { ttl: 3600 } }
189
+ ```
190
+
191
+ ### Caso 2: Sistema de Permisos con Wildcards
192
+
193
+ ```typescript
194
+ import g from '@g10/ts-helpers'
195
+
196
+ const userPermissions = [
197
+ 'admin.users.read',
198
+ 'admin.users.write',
199
+ 'reports.view',
200
+ ]
201
+
202
+ // Verificar permisos con patrones
203
+ function hasPermission(requiredPermission: string): boolean {
204
+ return userPermissions.some(perm =>
205
+ g.matchPathPattern(perm, requiredPermission)
206
+ )
207
+ }
208
+
209
+ hasPermission('admin.users.*') // true (tiene read y write)
210
+ hasPermission('admin.*.write') // true (tiene admin.users.write)
211
+ hasPermission('admin.products.*') // false (no tiene permisos de productos)
212
+
213
+ // Feature flags con wildcards
214
+ const enabledFeatures = [
215
+ 'features.auth.oauth',
216
+ 'features.auth.saml',
217
+ 'features.payments.stripe',
218
+ ]
219
+
220
+ const hasAuthFeature = enabledFeatures.some(f =>
221
+ g.matchPathPattern(f, 'features.auth.*')
222
+ )
223
+ // true
224
+ ```
225
+
226
+ ### Caso 3: Configuración Dinámica con Validación
227
+
228
+ ```typescript
229
+ import g from '@g10/ts-helpers'
230
+
231
+ class ConfigManager {
232
+ private config: Record<string, any> = {}
233
+
234
+ set(path: string, value: any): void {
235
+ // Validar path antes de establecer
236
+ if (!g.isValidDotNotationPath(path)) {
237
+ throw new Error(`Invalid config path: ${path}`)
238
+ }
239
+ g.setDeepValue(this.config, path, value)
240
+ }
241
+
242
+ get<T = any>(path: string, defaultValue?: T): T | undefined {
243
+ if (!g.isValidDotNotationPath(path)) {
244
+ throw new Error(`Invalid config path: ${path}`)
245
+ }
246
+ return g.getDeepValue(this.config, path, defaultValue)
247
+ }
248
+ }
249
+
250
+ const config = new ConfigManager()
251
+ config.set('database.host', 'localhost')
252
+ config.set('database.port', 5432)
253
+ config.get('database.host') // 'localhost'
254
+ config.get('database.timeout', 5000) // 5000 (default)
255
+ config.set('.invalid', 'value') // throws Error
256
+ ```
257
+
258
+ ### Caso 4: Generación de .env desde Configuración
259
+
260
+ ```typescript
261
+ import g from '@g10/ts-helpers'
262
+
263
+ const config = {
264
+ database: { host: 'localhost', port: 5432 },
265
+ cache: { ttl: 3600, enabled: true },
266
+ features: { auth: true, payments: false },
267
+ }
268
+
269
+ function generateEnvFile(obj: any, prefix = 'APP', currentPath = ''): string[] {
270
+ const lines: string[] = []
271
+
272
+ for (const [key, value] of Object.entries(obj)) {
273
+ const path = currentPath ? `${currentPath}.${key}` : key
274
+
275
+ if (typeof value === 'object' && !Array.isArray(value)) {
276
+ // Recursivo para objetos anidados
277
+ lines.push(...generateEnvFile(value, prefix, path))
278
+ } else {
279
+ const envKey = g.pathToEnvKey(path, prefix)
280
+ const envValue = Array.isArray(value)
281
+ ? JSON.stringify(value)
282
+ : String(value)
283
+ lines.push(`${envKey}=${envValue}`)
284
+ }
285
+ }
286
+
287
+ return lines
288
+ }
289
+
290
+ const envFile = generateEnvFile(config).join('\n')
291
+ console.log(envFile)
292
+ // APP_DATABASE_HOST=localhost
293
+ // APP_DATABASE_PORT=5432
294
+ // APP_CACHE_TTL=3600
295
+ // APP_CACHE_ENABLED=true
296
+ // APP_FEATURES_AUTH=true
297
+ // APP_FEATURES_PAYMENTS=false
298
+ ```
299
+
300
+ ## 📋 Catálogo Completo de Funciones
301
+
302
+ ### 🔐 Validation Module (validation.ts)
303
+
304
+ **Validadores Españoles:**
305
+
306
+ - `validateNIF(nif)` - Valida NIF español
307
+ - `validateNIE(nie)` - Valida NIE español
308
+ - `validateCIF(cif)` - Valida CIF español
309
+ - `isValidSpanishIBAN(iban)` - IBAN español
310
+ - `isValidSpanishPhone(phone)` - Teléfono español
311
+ - `isValidSpanishPostalCode(code)` - Código postal
312
+
313
+ **Validadores Generales:**
314
+
315
+ - `isValidEmail(email)` - Email válido
316
+ - `isValidURL(url)` - URL válida (incluye localhost)
317
+ - `isValidJSON(str)` - JSON válido
318
+ - `isValidBase64(str)` - Base64 válido
319
+ - `isValidJWTFormat(token)` - Formato JWT
320
+ - `validatePassword(password, options)` - Contraseña segura
321
+
322
+ **Generadores:**
323
+
324
+ - `generateSpanishNIF()` - NIF aleatorio válido
325
+ - `generateSpanishNIE()` - NIE aleatorio válido
326
+ - `generateSpanishCIF()` - CIF aleatorio válido
327
+ - `generateSpanishIBAN()` - IBAN español válido
328
+ - `generateSpanishPostalCode()` - Código postal español
329
+ - `generatePassword(options)` - Contraseña segura
330
+ - `generateSecureToken(length)` - Token criptográfico
331
+ - `generateNonce(length)` - Nonce único
332
+
333
+ ### 📝 Strings Module (strings.ts)
334
+
335
+ **Transformaciones de Caso:**
336
+
337
+ - `toCamelCase(str)` - camelCase
338
+ - `toKebabCase(str)` - kebab-case
339
+ - `toSnakeCase(str)` - snake_case
340
+ - `toPascalCase(str)` - PascalCase
341
+ - `toLowerCase(str)` - minúsculas
342
+ - `toUpperCase(str)` - MAYÚSCULAS
343
+
344
+ **Capitalización:**
345
+
346
+ - `capitalizeFirst(str)` - Primera letra mayúscula
347
+ - `capitalizeEachWord(str)` - Cada palabra capitalizada
348
+
349
+ **Seguridad:**
350
+
351
+ - `sanitizeString(str)` - Elimina caracteres peligrosos
352
+ - `sanitizeHtml(html)` - Sanitiza HTML
353
+ - `escapeHtmlChars(str)` - Escapa caracteres HTML
354
+ - `unescapeHtmlChars(str)` - Desescapa HTML
355
+ - `removeDangerousChars(str)` - Elimina caracteres peligrosos
356
+ - `escapeShellCommand(cmd)` - Escapa comandos shell
357
+
358
+ **Manipulación:**
359
+
360
+ - `removeAccents(str)` - Elimina acentos y tildes
361
+ - `truncateString(str, length)` - Trunca con '...'
362
+ - `padStart(str, length, char)` - Rellena al inicio
363
+ - `padEnd(str, length, char)` - Rellena al final
364
+ - `repeatString(str, times)` - Repite string
365
+ - `reverseString(str)` - Invierte string
366
+ - `trim/trimStart/trimEnd(str)` - Elimina espacios
367
+
368
+ ### 🗂️ Objects Module (objects.ts)
369
+
370
+ **Comparación:**
371
+
372
+ - `deepEqual(obj1, obj2)` - Comparación profunda
373
+ - `comparator(obj1, obj2, options)` - Comparador personalizable
374
+ - `hasSameType(obj1, obj2)` - Mismo tipo incluyendo typed arrays
375
+
376
+ **Manipulación de Arrays:**
377
+
378
+ - `updateArrayElementsBy(array, criteria, updates)` - Actualiza elementos
379
+ - `updateArrayElementById(array, id, updates)` - Actualiza por ID
380
+ - `deleteArrayElementsBy(array, criteria)` - Elimina elementos
381
+
382
+ **Análisis de Objetos:**
383
+
384
+ - `getShallowProperties(obj)` - Solo propiedades primitivas
385
+ - `calculateDifferences(oldObj, newObj, tolerance)` - Diferencias con tolerancia
386
+ - `formatToReadableString(obj)` - String legible del objeto
387
+
388
+ **Hash y Utilidades:**
389
+
390
+ - `generateCrcHash(data)` - Hash CRC32 de datos
391
+ - `hashString(str)` - Hash simple de string
392
+
393
+ ### 📅 Dates Module (dates.ts)
394
+
395
+ **Formato:**
396
+
397
+ - `formatNow(format)` - Fecha actual formateada
398
+ - `format(date, format)` - Formato personalizado
399
+ - `toISO(date)` - Formato ISO
400
+ - `fromNow(date)` - Tiempo relativo ('hace 2 horas')
401
+
402
+ **Manipulación:**
403
+
404
+ - `addDays/addMonths/addYears(date, amount)` - Añadir tiempo
405
+ - `addNowDays/addNowMonths/addNowYears(amount)` - Añadir a fecha actual
406
+
407
+ **Cálculos:**
408
+
409
+ - `diffDays/diffMonths/diffYears(date1, date2)` - Diferencias
410
+ - `diffBusinessDays(date1, date2)` - Días laborables
411
+ - `diffMilliseconds(date1, date2)` - Diferencia en ms
412
+
413
+ **Utilidades:**
414
+
415
+ - `isWeekday(date)` - Es día laborable
416
+ - `dayOfWeek(date)` - Día de la semana
417
+ - `weekOfYear(date)` - Semana del año
418
+ - `getFirstDayOfYear/getLastDayOfYear(year)` - Primer/último día
419
+
420
+ ### 🔢 Math Module (math.ts)
421
+
422
+ **Estadísticas Básicas:**
423
+
424
+ - `calculateMedian(numbers)` - Mediana
425
+ - `calculateMode(numbers)` - Moda (valores más frecuentes)
426
+ - `calculateStandardDeviation(numbers)` - Desviación estándar
427
+ - `calculateVariance(numbers)` - Varianza
428
+ - `calculatePercentile(numbers, percentile)` - Percentil específico
429
+
430
+ **Estadísticas Avanzadas:**
431
+
432
+ - `calculateQuartiles(numbers)` - Q1, Q2, Q3
433
+ - `calculateIQR(numbers)` - Rango intercuartílico
434
+ - `detectOutliers(numbers, multiplier)` - Detección de outliers
435
+ - `calculateCorrelation(x, y)` - Correlación de Pearson
436
+
437
+ **Finanzas:**
438
+
439
+ - `calculateNPV(cashFlows, discountRate)` - Valor presente neto
440
+ - `calculateIRR(cashFlows)` - Tasa interna de retorno
441
+ - `calculateFutureValue(pv, rate, periods)` - Valor futuro
442
+ - `calculatePresentValue(fv, rate, periods)` - Valor presente
443
+ - `calculateAnnuityPayment(pv, rate, periods)` - Pago de anualidad
444
+
445
+ **Machine Learning:**
446
+
447
+ - `simpleKMeans(points, k, maxIterations)` - Clustering K-means
448
+ - `calculateEuclideanDistance(point1, point2)` - Distancia euclidiana
449
+ - `calculateManhattanDistance(point1, point2)` - Distancia Manhattan
450
+
451
+ ### ⏱️ Async Module (async.ts)
452
+
453
+ - `sleep(ms)` - Pausa asíncrona
454
+ - `wait(ms)` - Alias de sleep
455
+ - `runBatch(promises, batchSize)` - Ejecuta promesas en lotes
456
+ - `handleOperation(operation, options)` - Manejo con retry
457
+
458
+ ### 📊 Data Module (data.ts)
459
+
460
+ **Detección de Formato:**
461
+
462
+ - `detectFormatFromFilename(filename)` - Auto-detecta por extensión
463
+
464
+ **Export/Import:**
465
+
466
+ - `exportData(data, filename)` - Exporta con auto-detección
467
+ - `importData(filename)` - Importa con auto-detección
468
+ - `exportTree(data, filename)` - Exporta estructura de árbol
469
+ - `importTree(filename)` - Importa estructura de árbol
470
+ - `exportTxt(data, filename, options)` - Exporta como texto
471
+ - `importTxt(filename, options)` - Importa texto con validación
472
+
473
+ **Validación:**
474
+
475
+ - `validateExportData(data)` - Valida datos para export
476
+ - `validateCSVData(data)` - Alias para validación CSV
477
+
478
+ ### 🌍 Environment Module (environment.ts)
479
+
480
+ **Detección de Plataforma:**
481
+
482
+ - `isNodeEnvironment()` - Detecta runtime Node.js
483
+ - `isBrowserEnvironment()` - Detecta runtime browser
484
+ - `isNode` - Alias para isNodeEnvironment
485
+ - `isBrowser` - Alias para isBrowserEnvironment
486
+
487
+ **Detección de Entorno:**
488
+
489
+ - `isDevelopment(req?)` - Detecta desarrollo (NODE_ENV, puertos, localhost)
490
+ - `isProduction()` - Detecta producción
491
+ - `getEnvironmentInfo(req?)` - Info completa con criterios debugging
492
+
493
+ **Detección de Red:**
494
+
495
+ - `detectProtocol(req?)` - Protocolo HTTP/HTTPS con soporte proxy
496
+ - `detectHostname(req?)` - Hostname con headers X-Forwarded-Host
497
+ - `isLocalhost(hostname)` - Es localhost o .local/.localhost
498
+ - `isPrivateIP(hostname)` - Es IP privada (RFC 1918) o IPv6 local
499
+
500
+ ## 🔄 Migración Detallada desde v3.x
501
+
502
+ ### Cambios en la API
503
+
504
+ ```typescript
505
+ // ❌ v3.x - Estructura anidada redundante
506
+ import g from '@g10/ts-helpers'
507
+
508
+ // Validación
509
+ g.validation.validators.validateNIF('12345678Z')
510
+ g.validation.generators.generateSpanishNIF()
511
+
512
+ // Strings
513
+ g.primitives.string.toCamelCase('hello-world')
514
+ g.primitives.string.capitalizeFirst('hello')
515
+
516
+ // Objetos
517
+ g.primitives.object.deepEqual(obj1, obj2)
518
+ g.primitives.array.updateArrayElementsBy(array, criteria, updates)
519
+
520
+ // Async
521
+ g.core.async.sleep(1000)
522
+ g.core.async.runBatch(promises, 5)
523
+
524
+ // Data
525
+ g.data.exportData(data, 'file.csv')
526
+ g.data.importData('file.json')
527
+ ```
528
+
529
+ ```typescript
530
+ // ✅ v4.0.0 - API completamente plana
531
+ import g from '@g10/ts-helpers'
532
+
533
+ // Validación - acceso directo
534
+ g.validateNIF('12345678Z')
535
+ g.generateSpanishNIF()
536
+
537
+ // Strings - acceso directo
538
+ g.toCamelCase('hello-world')
539
+ g.capitalizeFirst('hello')
540
+
541
+ // Objetos - acceso directo
542
+ g.deepEqual(obj1, obj2)
543
+ g.updateArrayElementsBy(array, criteria, updates)
544
+
545
+ // Async - acceso directo
546
+ g.sleep(1000)
547
+ g.runBatch(promises, 5)
548
+
549
+ // Data - acceso directo
550
+ g.exportData(data, 'file.csv')
551
+ g.importData('file.json')
552
+
553
+ // Environment - acceso directo
554
+ g.isDevelopment()
555
+ g.detectProtocol(req)
556
+ g.getEnvironmentInfo(req)
557
+ ```
558
+
559
+ ### Re-exportaciones Eliminadas
560
+
561
+ ```typescript
562
+ // ❌ v3.x - Re-exportaciones incluidas
563
+ import g from '@g10/ts-helpers'
564
+ g.lodash.isNull(value) // Eliminado
565
+ g.axios.get('/api/data') // Eliminado
566
+ g.dayjs().format('DD/MM/YYYY') // Eliminado
567
+ g.numeral(1234).format('0,0') // Eliminado
568
+
569
+ // ✅ v4.0.0 - Instalar dependencias por separado
570
+ npm install lodash axios dayjs numeral
571
+
572
+ import _ from 'lodash'
573
+ import axios from 'axios'
574
+ import dayjs from 'dayjs'
575
+ import numeral from 'numeral'
576
+
577
+ _.isNull(value)
578
+ axios.get('/api/data')
579
+ dayjs().format('DD/MM/YYYY')
580
+ numeral(1234).format('0,0')
581
+
582
+ // O usar implementaciones nativas de @g10/ts-helpers
583
+ import g from '@g10/ts-helpers'
584
+ g.formatNow('DD/MM/YYYY') // Reemplazo nativo para dayjs
585
+ g.deepEqual(obj1, obj2) // Reemplazo nativo para lodash
586
+ ```
587
+
588
+ ## ⚡ Optimización de Bundle
589
+
590
+ ### Tree-shaking Efectivo
591
+
592
+ ```typescript
593
+ // Bundle mínimo - solo importa funciones específicas
594
+ import { validateNIF, isValidEmail } from '@g10/ts-helpers/validation'
595
+ import { toCamelCase } from '@g10/ts-helpers/strings'
596
+ import { sleep } from '@g10/ts-helpers/async'
597
+
598
+ // Resultado: ~5KB vs ~185KB completo
599
+ ```
600
+
601
+ ### Comparación de Tamaños
602
+
603
+ ```
604
+ v3.x: 603KB (con re-exportaciones)
605
+ v4.0.0: 185KB (-69% reducción)
606
+ Tree-shaking específico: ~5-20KB según funciones usadas
607
+ ```
608
+
609
+ ## 🧪 Testing y Calidad
610
+
611
+ La librería incluye **270 tests** que cubren todas las funciones:
612
+
613
+ ```bash
614
+ pnpm test # Ejecuta todos los tests
615
+ pnpm test:watch # Modo watch
616
+ pnpm test:coverage # Cobertura de código
617
+ ```
618
+
619
+ **Cobertura actual: 100% de funciones principales**
620
+
621
+ ## 🔧 Configuración de Desarrollo
622
+
623
+ ```bash
624
+ # Instalación para desarrollo
625
+ git clone https://gitlab.gzl10.com/g10libs/ts-helpers.git
626
+ cd ts-helpers
627
+ pnpm install
628
+
629
+ # Comandos disponibles
630
+ pnpm run build # Build completo (~40ms)
631
+ pnpm run typecheck # Verificación TypeScript
632
+ pnpm run lint # ESLint con auto-fix
633
+ pnpm run format # Prettier con auto-fix
634
+ pnpm run quality # Todo junto (sin tests)
635
+
636
+ # Gestión de versiones con Changeset
637
+ pnpm release:prepare # Crear changeset
638
+ pnpm release:version # Actualizar versión y CHANGELOG
639
+ pnpm release:publish # Publicar a npm
640
+ ```
641
+
642
+ ## 📁 Universal Format Detection
643
+
644
+ ### Detección Simple de Extensiones
645
+
646
+ ```typescript
647
+ import g from '@g10/ts-helpers'
648
+
649
+ // Extrae solo la extensión del archivo
650
+ g.detectFileExtension('report.xlsx') // 'xlsx'
651
+ g.detectFileExtension('image.png') // 'png'
652
+ g.detectFileExtension('archive.tar.gz') // 'gz' (última extensión)
653
+ g.detectFileExtension('filename') // null (sin extensión)
654
+ ```
655
+
656
+ ### Detección Inteligente con Categorización
657
+
658
+ ```typescript
659
+ // Análisis completo con 80+ formatos soportados
660
+ const analysis = g.detectUniversalFormat('presentation.pptx')
661
+ // {
662
+ // extension: 'pptx',
663
+ // category: 'presentation',
664
+ // mimeType: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
665
+ // isText: false,
666
+ // isBinary: true
667
+ // }
668
+
669
+ // Microsoft Office
670
+ g.detectUniversalFormat('report.xlsx') // { category: 'spreadsheet' }
671
+ g.detectUniversalFormat('document.docx') // { category: 'document' }
672
+ g.detectUniversalFormat('slides.pptx') // { category: 'presentation' }
673
+
674
+ // Programación
675
+ g.detectUniversalFormat('app.ts') // { category: 'code', isText: true }
676
+ g.detectUniversalFormat('script.py') // { category: 'code', isText: true }
677
+ g.detectUniversalFormat('Main.java') // { category: 'code', isText: true }
678
+
679
+ // Media
680
+ g.detectUniversalFormat('video.mp4') // { category: 'video', isBinary: true }
681
+ g.detectUniversalFormat('song.mp3') // { category: 'audio', isBinary: true }
682
+ g.detectUniversalFormat('photo.jpg') // { category: 'image', isBinary: true }
683
+
684
+ // Archivos
685
+ g.detectUniversalFormat('backup.zip') // { category: 'archive' }
686
+ g.detectUniversalFormat('package.7z') // { category: 'archive' }
687
+
688
+ // Fuentes
689
+ g.detectUniversalFormat('font.ttf') // { category: 'font' }
690
+ g.detectUniversalFormat('webfont.woff2') // { category: 'font' }
691
+ ```
692
+
693
+ ### Formatos Soportados (80+)
694
+
695
+ #### Microsoft Office
696
+
697
+ - **Documentos**: doc, docx, odt (LibreOffice)
698
+ - **Hojas de cálculo**: xls, xlsx, ods
699
+ - **Presentaciones**: ppt, pptx, odp
700
+
701
+ #### Programación
702
+
703
+ - **JavaScript/TypeScript**: js, mjs, jsx, ts, tsx
704
+ - **Backend**: py, java, php, rb, go, rs, c, cpp, cs
705
+ - **Shell**: sh, bash, ps1
706
+
707
+ #### Web Development
708
+
709
+ - **Estilos**: css, scss, sass, less
710
+ - **Markup**: html, htm, xml, svg
711
+
712
+ #### Media
713
+
714
+ - **Imágenes**: jpg, jpeg, png, gif, svg, webp, avif, bmp, tiff, ico, psd
715
+ - **Audio**: mp3, wav, flac, ogg, aac, m4a, wma
716
+ - **Video**: mp4, avi, mov, wmv, flv, webm, mkv, 3gp
717
+
718
+ #### Archivos y Configuración
719
+
720
+ - **Archivos**: zip, rar, 7z, tar, gz, bz2, xz
721
+ - **Configuración**: ini, cfg, conf, toml, env, yaml, yml
722
+ - **Fuentes**: ttf, otf, woff, woff2, eot
723
+
724
+ #### Datos
725
+
726
+ - **Estructurados**: json, csv, xml, yaml, sql
727
+ - **Base de datos**: db, sqlite
728
+ - **Documentos**: pdf, epub, mobi, rtf
729
+
730
+ ### Casos de Uso
731
+
732
+ ```typescript
733
+ // Validación de uploads
734
+ const uploadValidator = (filename: string) => {
735
+ const format = g.detectUniversalFormat(filename)
736
+
737
+ if (format.category === 'image' && format.extension !== 'svg') {
738
+ return { allowed: true, type: 'image' }
739
+ }
740
+
741
+ if (format.category === 'document') {
742
+ return { allowed: true, type: 'document' }
743
+ }
744
+
745
+ return { allowed: false, reason: 'Formato no permitido' }
746
+ }
747
+
748
+ // Content-Type automático
749
+ const getContentType = (filename: string) => {
750
+ const format = g.detectUniversalFormat(filename)
751
+ return format.mimeType || 'application/octet-stream'
752
+ }
753
+
754
+ // Procesamiento condicional
755
+ const processFile = (filename: string, content: string | Buffer) => {
756
+ const format = g.detectUniversalFormat(filename)
757
+
758
+ if (format.isText) {
759
+ return content.toString('utf-8') // Procesar como texto
760
+ } else {
761
+ return content // Mantener como binario
762
+ }
763
+ }
764
+ ```
765
+
766
+ ## 📈 Performance
767
+
768
+ ### Build Performance
769
+
770
+ ```
771
+ TypeScript Compiler: ~45s
772
+ tsup (v4.0.0): ~40ms
773
+ Mejora: 70-90% más rápido
774
+ ```
775
+
776
+ ### Bundle Optimization
777
+
778
+ ```
779
+ Antes (v3.x): 603KB
780
+ Después (v4.0.0): 185KB
781
+ Reducción: 69%
782
+ Tree-shaking: 5-20KB según uso
783
+ ```
784
+
785
+ ### Runtime Performance
786
+
787
+ - **Funciones nativas**: Sin overhead de dependencias externas
788
+ - **Tree-shaking preservado**: Solo código usado en bundle final
789
+ - **TypeScript strict**: Optimización máxima del compilador
790
+
791
+ ---
792
+
793
+ **🎉 @g10/ts-helpers v4.0.4 - Ready for Production**
794
+
795
+ Para más información, consulta:
796
+
797
+ - [README.md](README.md) - Overview y instalación
798
+ - [CHANGELOG.md](CHANGELOG.md) - Historial de cambios
799
+ - [GitLab Issues](https://gitlab.gzl10.com/g10libs/ts-helpers/-/issues) -
800
+ Reportar bugs