@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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/environment.ts","../../src/tree.ts","../../src/validators.ts","../../src/csv.ts","../../src/json.ts","../../src/data.ts","../../src/errors.ts"],"sourcesContent":["/**\n * Environment detection utilities for universal browser/Node.js compatibility\n */\n\n/**\n * Detailed criteria used for environment debugging and analysis\n */\nexport interface EnvironmentCriteria {\n /** True if hostname is localhost, 127.0.0.1, ::1, or .localhost/.local domain */\n isLocalhost: boolean\n /** True if hostname is within private IP ranges (10.x, 192.168.x, 172.16-31.x) */\n isPrivateIP: boolean\n /** True if port is in development range (3000-9999) */\n isDevelopmentPort: boolean\n /** True if development tools are detected (Vue DevTools, __DEV__ global) */\n hasDevtools: boolean\n /** Current NODE_ENV value (Node.js only) */\n nodeEnv?: string\n}\n\n/**\n * Complete environment information with platform detection and debugging criteria\n */\nexport interface EnvironmentInfo {\n /** Runtime platform: 'browser', 'node', or 'unknown' */\n platform: 'browser' | 'node' | 'unknown'\n /** Detected environment: 'development', 'production', or 'test' */\n environment: 'development' | 'production' | 'test'\n /** Protocol used (http/https) - from location or request headers */\n protocol?: string\n /** Hostname - from location or request headers */\n hostname?: string\n /** Browser user agent string (browser only) */\n userAgent?: string\n /** Detailed debugging criteria */\n criteria?: EnvironmentCriteria\n}\n\n/**\n * Detects if the current runtime is Node.js environment\n *\n * Checks for the presence of Node.js-specific globals (process, process.versions.node)\n * and absence of browser globals (window).\n *\n * @returns True if running in Node.js, false otherwise\n *\n * @example\n * ```typescript\n * if (isNodeEnvironment()) {\n * // Node.js specific code\n * const fs = require('fs')\n * }\n * ```\n */\nexport function isNodeEnvironment(): boolean {\n return typeof window === 'undefined' && typeof process !== 'undefined' && !!process.versions?.node\n}\n\n/**\n * Detects if the current runtime is a browser environment\n *\n * Checks for the presence of browser-specific globals (window, document).\n * Also handles testing environments that may mock these globals.\n *\n * @returns True if running in browser, false otherwise\n *\n * @example\n * ```typescript\n * if (isBrowserEnvironment()) {\n * // Browser specific code\n * const url = window.location.href\n * }\n * ```\n */\nexport function isBrowserEnvironment(): boolean {\n // Check both global.window (for tests) and window (for actual browser)\n const win = typeof window !== 'undefined' ? window : (global as any).window\n const doc = typeof document !== 'undefined' ? document : (global as any).document\n return typeof win !== 'undefined' && win !== null && typeof doc !== 'undefined' && doc !== null\n}\n\n/**\n * Detects if the application is running in development mode\n *\n * Uses sophisticated detection logic based on multiple criteria:\n * - Node.js: NODE_ENV variable, request headers (for Express), hostname/protocol analysis\n * - Browser: __DEV__ global, development ports (3000-9999), localhost/private IPs, dev tools\n *\n * Handles reverse proxies correctly by checking forwarded headers.\n *\n * @param req - Optional Express request object for server-side detection\n * @returns True if in development mode, false if production\n *\n * @example\n * ```typescript\n * // Node.js - simple check\n * if (isDevelopment()) {\n * console.log('Development mode')\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Express - with request context\n * app.get('/api/status', (req, res) => {\n * const isDev = isDevelopment(req)\n * res.json({ environment: isDev ? 'dev' : 'prod' })\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Browser - automatic detection\n * if (isDevelopment()) {\n * // Enable debug logging, dev tools, etc.\n * enableDebugMode()\n * }\n * ```\n */\nexport function isDevelopment(req?: any): boolean {\n if (isNodeEnvironment()) {\n // En Node.js, verificar NODE_ENV primero\n if (process.env.NODE_ENV === 'production') {\n return false\n }\n if (process.env.NODE_ENV === 'development') {\n return true\n }\n\n // Si hay request object (Express), usar lógica de servidor\n if (req) {\n const protocol = detectProtocol(req)\n const hostname = detectHostname(req)\n\n return protocol === 'http' || isLocalhost(hostname) || isPrivateIP(hostname)\n }\n\n // Fallback para Node.js sin request\n // NODE_ENV undefined se asume como development\n return !process.env.NODE_ENV || process.env.NODE_ENV === 'development'\n }\n\n if (isBrowserEnvironment()) {\n // Verificar variable global de desarrollo (webpack/vite)\n if (\n typeof (globalThis as any).__DEV__ !== 'undefined' &&\n (globalThis as any).__DEV__ === true\n ) {\n return true\n }\n\n // Verificar si hay herramientas de desarrollo activas\n if (\n typeof window !== 'undefined' &&\n (window as any).__VUE_DEVTOOLS_GLOBAL_HOOK__ &&\n typeof location !== 'undefined' &&\n isLocalhost(location.hostname)\n ) {\n return true\n }\n\n // Si location no está disponible, considerar como producción\n if (typeof location === 'undefined') {\n return false\n }\n\n const hostname = location.hostname || ''\n const port = parseInt(location.port || '80')\n\n // Criterios basados en hostname y puerto\n const isDevelopmentPort = port >= 3000 && port <= 9999\n\n // Solo considerar HTTP como desarrollo si el hostname también lo indica\n const isHttpDevelopment =\n location.protocol === 'http:' && (isLocalhost(hostname) || isPrivateIP(hostname))\n\n return isLocalhost(hostname) || isPrivateIP(hostname) || isDevelopmentPort || isHttpDevelopment\n }\n\n return false\n}\n\n/**\n * Detects if the application is running in production mode\n *\n * Simple inverse of isDevelopment() with additional NODE_ENV validation for Node.js.\n *\n * @returns True if in production mode, false if development\n *\n * @example\n * ```typescript\n * if (isProduction()) {\n * // Enable performance optimizations\n * enableProductionMode()\n * }\n * ```\n */\nexport function isProduction(): boolean {\n if (isNodeEnvironment()) {\n return process.env.NODE_ENV === 'production'\n }\n\n // Browser: inverso de isDevelopment (que ya excluye test)\n return !isDevelopment()\n}\n\n/**\n * Detects if the application is running in test mode\n *\n * Checks explicitly for NODE_ENV === 'test' in Node.js environments.\n * Vitest and Jest automatically set this value when running tests.\n *\n * @returns True if NODE_ENV is 'test', false otherwise\n *\n * @example\n * ```typescript\n * if (isTest()) {\n * // Test-specific behavior (mocks, fixtures, etc.)\n * enableTestMode()\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Conditional imports for testing\n * if (isTest()) {\n * const { mockAPI } = await import('./test/mocks')\n * mockAPI.setup()\n * }\n * ```\n */\nexport function isTest(): boolean {\n if (isNodeEnvironment()) {\n return process.env.NODE_ENV === 'test'\n }\n\n return false\n}\n\n/**\n * Detects if the application is NOT running in production mode\n *\n * Returns true for development, test, undefined, or any non-production environment.\n * Useful for enabling debugging, logging, or development-only features\n * across all non-production environments.\n *\n * @returns True if not in production (development, test, or undefined)\n *\n * @example\n * ```typescript\n * if (isNonProduction()) {\n * // Enable debug logging for dev and test\n * console.log('Debug mode enabled')\n * enableVerboseLogging()\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Security: Disable strict checks in non-production\n * if (isNonProduction()) {\n * allowSelfSignedCertificates()\n * disableCSRFProtection()\n * }\n * ```\n */\nexport function isNonProduction(): boolean {\n if (isNodeEnvironment()) {\n return process.env.NODE_ENV !== 'production'\n }\n\n return !isProduction()\n}\n\n/**\n * Detects the protocol (HTTP/HTTPS) from browser location or request headers\n *\n * Handles reverse proxies correctly by checking forwarded headers in priority order:\n * 1. X-Forwarded-Proto (most common)\n * 2. X-Forwarded-Protocol\n * 3. X-Url-Scheme\n * 4. Front-End-Https\n * 5. CF-Visitor (Cloudflare specific)\n *\n * @param req - Optional Express request object with headers\n * @returns Protocol as 'http' or 'https'\n *\n * @example\n * ```typescript\n * // Browser usage\n * const protocol = detectProtocol() // 'https' from window.location\n * ```\n *\n * @example\n * ```typescript\n * // Express server with proxy\n * app.get('/api', (req, res) => {\n * const protocol = detectProtocol(req) // Detects from X-Forwarded-Proto\n * const fullUrl = `${protocol}://${req.get('host')}${req.path}`\n * })\n * ```\n */\nexport function detectProtocol(req?: any): 'http' | 'https' {\n if (isBrowserEnvironment()) {\n if (typeof location !== 'undefined') {\n return location.protocol === 'https:' ? 'https' : 'http'\n }\n return 'https' // Default seguro para browser\n }\n\n if (req) {\n // Headers comunes de proxies reversos en orden de prioridad\n const forwardedProto = req.get?.('X-Forwarded-Proto') || req.headers?.['x-forwarded-proto']\n const forwardedProtocol =\n req.get?.('X-Forwarded-Protocol') || req.headers?.['x-forwarded-protocol']\n const urlScheme = req.get?.('X-Url-Scheme') || req.headers?.['x-url-scheme']\n const frontEndHttps = req.get?.('Front-End-Https') || req.headers?.['front-end-https']\n const cloudflareVisitor = req.get?.('CF-Visitor') || req.headers?.['cf-visitor']\n\n // Verificar headers de proxy\n if (forwardedProto) {\n return forwardedProto.split(',')[0].trim().toLowerCase() as 'http' | 'https'\n }\n if (forwardedProtocol) {\n return forwardedProtocol.toLowerCase() as 'http' | 'https'\n }\n if (urlScheme) {\n return urlScheme.toLowerCase() as 'http' | 'https'\n }\n if (frontEndHttps === 'on') {\n return 'https'\n }\n if (cloudflareVisitor) {\n try {\n const visitor = JSON.parse(cloudflareVisitor)\n if (visitor.scheme) {\n return visitor.scheme.toLowerCase() as 'http' | 'https'\n }\n } catch (_e) {\n // Ignorar errores de parsing JSON\n }\n }\n\n // Fallback al protocolo directo\n if (req.protocol) return req.protocol\n if (req.secure) return 'https'\n }\n\n // Default para Node.js sin request context\n return 'http'\n}\n\n/**\n * Extracts hostname from browser location or request headers\n *\n * Handles reverse proxies by checking forwarded headers in priority order:\n * 1. X-Forwarded-Host (most common, supports multiple hosts)\n * 2. X-Original-Host\n * 3. Host header\n *\n * Automatically strips port numbers and handles multiple comma-separated hosts.\n *\n * @param req - Optional Express request object with headers\n * @returns Hostname without port number\n *\n * @example\n * ```typescript\n * // Browser usage\n * const hostname = detectHostname() // 'example.com' from window.location\n * ```\n *\n * @example\n * ```typescript\n * // Express server behind proxy\n * app.get('/api', (req, res) => {\n * const hostname = detectHostname(req) // 'api.example.com' from X-Forwarded-Host\n * const isLocal = isLocalhost(hostname)\n * })\n * ```\n */\nexport function detectHostname(req?: any): string {\n if (isBrowserEnvironment()) {\n if (typeof location !== 'undefined') {\n return location.hostname\n }\n return 'localhost'\n }\n\n if (req) {\n // Headers comunes de proxies reversos en orden de prioridad\n const forwardedHost = req.get?.('X-Forwarded-Host') || req.headers?.['x-forwarded-host']\n const originalHost = req.get?.('X-Original-Host') || req.headers?.['x-original-host']\n const host = req.get?.('Host') || req.headers?.['host']\n\n // Verificar headers de proxy\n if (forwardedHost) {\n return forwardedHost.split(',')[0].trim().split(':')[0]\n }\n if (originalHost) {\n return originalHost.split(':')[0]\n }\n if (host) {\n return host.split(':')[0]\n }\n\n // Fallback al hostname directo\n if (req.hostname) return req.hostname\n }\n\n // Default para Node.js sin request context\n return 'localhost'\n}\n\n/**\n * Checks if a hostname represents localhost or local development\n *\n * Recognizes various localhost representations:\n * - 'localhost' (standard)\n * - '127.0.0.1' (IPv4 loopback)\n * - '::1' (IPv6 loopback)\n * - '*.localhost' (local development domains)\n * - '*.local' (mDNS local domains)\n *\n * @param hostname - Hostname to check\n * @returns True if hostname represents localhost\n *\n * @example\n * ```typescript\n * isLocalhost('localhost') // true\n * isLocalhost('127.0.0.1') // true\n * isLocalhost('app.localhost') // true\n * isLocalhost('macbook.local') // true\n * isLocalhost('example.com') // false\n * ```\n */\nexport function isLocalhost(hostname: string): boolean {\n return (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '::1' ||\n hostname.endsWith('.localhost') ||\n hostname.endsWith('.local')\n )\n}\n\n/**\n * Checks if a hostname is within private IP address ranges\n *\n * Recognizes private/internal IP ranges according to RFC 1918:\n * - IPv4: 10.x.x.x, 192.168.x.x, 172.16-31.x.x, 127.x.x.x (loopback)\n * - IPv6: ::1 (loopback), fc00::/7 (unique local), fe80::/10 (link-local)\n * - Also includes localhost detection\n *\n * @param hostname - Hostname or IP address to check\n * @returns True if hostname is private/local, false if public\n *\n * @example\n * ```typescript\n * isPrivateIP('192.168.1.1') // true\n * isPrivateIP('10.0.0.1') // true\n * isPrivateIP('172.16.0.1') // true\n * isPrivateIP('localhost') // true\n * isPrivateIP('8.8.8.8') // false\n * isPrivateIP('example.com') // false\n * ```\n */\nexport function isPrivateIP(hostname: string): boolean {\n // IPv4 private ranges\n const ipv4Patterns = [\n /^10\\./, // 10.0.0.0/8\n /^172\\.(1[6-9]|2[0-9]|3[0-1])\\./, // 172.16.0.0/12\n /^192\\.168\\./, // 192.168.0.0/16\n /^127\\./, // 127.0.0.0/8 (loopback)\n ]\n\n // IPv6 private/local ranges\n const ipv6Patterns = [\n /^::1$/, // IPv6 loopback\n /^fc[0-9a-f]{2}:/i, // Unique local addresses\n /^fd[0-9a-f]{2}:/i, // Unique local addresses\n /^fe80:/i, // Link-local addresses\n ]\n\n return (\n ipv4Patterns.some(pattern => pattern.test(hostname)) ||\n ipv6Patterns.some(pattern => pattern.test(hostname)) ||\n isLocalhost(hostname)\n )\n}\n\n/**\n * Gathers complete environment information for debugging and analysis\n *\n * Returns comprehensive environment data including platform detection,\n * development/production status, protocol/hostname information, and\n * detailed debugging criteria.\n *\n * @param req - Optional Express request object for server-side context\n * @returns Complete environment information object\n *\n * @example\n * ```typescript\n * // Basic usage\n * const env = getEnvironmentInfo()\n * console.log(env.platform) // 'node' | 'browser' | 'unknown'\n * console.log(env.environment) // 'development' | 'production' | 'test'\n * ```\n *\n * @example\n * ```typescript\n * // Express server usage\n * app.get('/api/debug', (req, res) => {\n * const envInfo = getEnvironmentInfo(req)\n * res.json({\n * platform: envInfo.platform,\n * isDev: envInfo.environment === 'development',\n * protocol: envInfo.protocol,\n * host: envInfo.hostname,\n * criteria: envInfo.criteria\n * })\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Conditional features based on environment\n * const env = getEnvironmentInfo()\n * if (env.criteria?.hasDevtools) {\n * enableVueDevtools()\n * }\n * if (env.criteria?.isDevelopmentPort) {\n * enableHotReload()\n * }\n * ```\n */\nexport function getEnvironmentInfo(req?: any): EnvironmentInfo {\n const platform = isNodeEnvironment() ? 'node' : isBrowserEnvironment() ? 'browser' : 'unknown'\n\n let environment: EnvironmentInfo['environment'] = 'production'\n if (isNodeEnvironment()) {\n const nodeEnv = process.env.NODE_ENV\n if (nodeEnv === 'development' || nodeEnv === 'test') {\n environment = nodeEnv\n }\n } else if (isDevelopment()) {\n environment = 'development'\n }\n\n const protocol = detectProtocol(req)\n const hostname = detectHostname(req)\n\n const info: EnvironmentInfo = {\n platform,\n environment,\n protocol,\n hostname,\n }\n\n if (isBrowserEnvironment() && typeof navigator !== 'undefined') {\n info.userAgent = navigator.userAgent\n }\n\n // Criterios detallados para debugging\n const criteria: EnvironmentCriteria = {\n isLocalhost: isLocalhost(hostname),\n isPrivateIP: isPrivateIP(hostname),\n isDevelopmentPort: false,\n hasDevtools: false,\n }\n\n if (isBrowserEnvironment() && typeof location !== 'undefined') {\n const port = parseInt(location.port || '80')\n criteria.isDevelopmentPort = port >= 3000 && port <= 9999\n criteria.hasDevtools = !!(\n typeof window !== 'undefined' && (window as any).__VUE_DEVTOOLS_GLOBAL_HOOK__\n )\n }\n\n if (isNodeEnvironment()) {\n criteria.nodeEnv = process.env.NODE_ENV || 'undefined'\n }\n\n info.criteria = criteria\n\n return info\n}\n\n/**\n * Alias for isNodeEnvironment() - detects Node.js runtime\n * @see {@link isNodeEnvironment}\n */\nexport const isNode = isNodeEnvironment\n\n/**\n * Alias for isBrowserEnvironment() - detects browser runtime\n * @see {@link isBrowserEnvironment}\n */\nexport const isBrowser = isBrowserEnvironment\n\n/**\n * Parses environment variable string values to their native JavaScript types\n *\n * Automatically detects and converts:\n * - Booleans: 'true', 'false', 'yes', 'no', '1', '0'\n * - Numbers: '42', '3.14', '-100'\n * - JSON arrays: '[1,2,3]', '[\"a\",\"b\"]'\n * - JSON objects: '{\"key\":\"value\"}'\n * - Comma-separated arrays: 'item1,item2,item3' (when not valid JSON)\n * - null/undefined: 'null', 'undefined', empty string\n *\n * Falls back to original string if no conversion applies.\n *\n * @param value - Environment variable string value to parse\n * @returns Parsed value with appropriate native type\n *\n * @example\n * ```typescript\n * // Boolean conversion\n * parseEnvValue('true') // true\n * parseEnvValue('false') // false\n * parseEnvValue('yes') // true\n * parseEnvValue('1') // true (as boolean)\n *\n * // Number conversion\n * parseEnvValue('42') // 42\n * parseEnvValue('3.14') // 3.14\n * parseEnvValue('-100') // -100\n *\n * // JSON arrays\n * parseEnvValue('[1,2,3]') // [1, 2, 3]\n * parseEnvValue('[\"a\",\"b\"]') // ['a', 'b']\n *\n * // JSON objects\n * parseEnvValue('{\"port\":3000,\"host\":\"localhost\"}')\n * // { port: 3000, host: 'localhost' }\n *\n * // Comma-separated arrays (fallback when not JSON)\n * parseEnvValue('red,green,blue') // ['red', 'green', 'blue']\n *\n * // Null/undefined\n * parseEnvValue('null') // null\n * parseEnvValue('undefined') // undefined\n * parseEnvValue('') // undefined\n *\n * // Strings (no conversion)\n * parseEnvValue('hello') // 'hello'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world usage with process.env\n * const config = {\n * debug: parseEnvValue(process.env.DEBUG), // 'true' → true\n * port: parseEnvValue(process.env.PORT), // '3000' → 3000\n * features: parseEnvValue(process.env.FEATURES), // 'auth,api' → ['auth', 'api']\n * database: parseEnvValue(process.env.DB_CONFIG), // '{\"host\":\"localhost\"}' → object\n * }\n * ```\n */\nexport function parseEnvValue(value: string | undefined): any {\n // Handle null/undefined/empty\n if (value === undefined || value === null || value === '') {\n return undefined\n }\n\n const trimmed = value.trim()\n\n // Handle explicit null/undefined strings\n if (trimmed === 'null') return null\n if (trimmed === 'undefined') return undefined\n\n // Handle booleans\n const lowerValue = trimmed.toLowerCase()\n if (lowerValue === 'true' || lowerValue === 'yes') return true\n if (lowerValue === 'false' || lowerValue === 'no') return false\n\n // Handle boolean-like numbers\n if (trimmed === '1') return true\n if (trimmed === '0') return false\n\n // Try parsing as number (but not if it starts with 0 and has more digits - could be octal/string)\n if (/^-?\\d+\\.?\\d*$/.test(trimmed)) {\n // Avoid treating leading-zero strings as numbers (e.g., postal codes '01234')\n if (trimmed.length > 1 && trimmed[0] === '0' && trimmed[1] !== '.') {\n return trimmed // Keep as string (e.g., '01234')\n }\n const num = Number(trimmed)\n if (!isNaN(num)) return num\n }\n\n // Try parsing as JSON (arrays, objects)\n if (\n (trimmed.startsWith('[') && trimmed.endsWith(']')) ||\n (trimmed.startsWith('{') && trimmed.endsWith('}'))\n ) {\n try {\n return JSON.parse(trimmed)\n } catch {\n // Not valid JSON, continue to next check\n }\n }\n\n // Try parsing comma-separated values as array\n if (trimmed.includes(',')) {\n const parts = trimmed.split(',').map(s => s.trim())\n // Only convert to array if all parts are non-empty\n if (parts.every(p => p.length > 0)) {\n return parts\n }\n }\n\n // Return as-is string\n return trimmed\n}\n","/**\n * Tree Structure Rendering Utilities\n *\n * Provides ASCII/Unicode tree visualization for hierarchical data structures.\n * Features:\n * - Customizable box-drawing characters (Unicode or ASCII)\n * - Flexible label extraction (field name or custom function)\n * - Recursive rendering with proper indentation\n * - Multiple root support\n * - Zero dependencies\n *\n * @module tree\n */\n\n/**\n * Configuration options for tree rendering\n *\n * Controls the visual appearance and label extraction strategy for ASCII/Unicode tree output.\n * All options have sensible defaults for immediate use with Unicode box-drawing characters.\n *\n * @property labelField - Object property name to use as node label (default: 'name')\n * @property verticalLine - Character sequence for vertical continuation (default: '│ ')\n * @property middleBranch - Character sequence for non-last child branch (default: '├── ')\n * @property lastBranch - Character sequence for last child branch (default: '└── ')\n * @property emptySpace - Character sequence for empty space after last branch (default: ' ')\n * @property labelFunction - Custom function to extract/format node labels (overrides labelField)\n *\n * @example\n * ```typescript\n * // Unicode box-drawing (default)\n * const unicodeOptions: TreeExportOptions = {\n * labelField: 'name',\n * verticalLine: '│ ',\n * middleBranch: '├── ',\n * lastBranch: '└── ',\n * emptySpace: ' '\n * }\n *\n * // ASCII alternative (better compatibility)\n * const asciiOptions: TreeExportOptions = {\n * verticalLine: '| ',\n * middleBranch: '+-- ',\n * lastBranch: '`-- ',\n * emptySpace: ' '\n * }\n *\n * // Custom label function\n * const customOptions: TreeExportOptions = {\n * labelFunction: (node) => `${node.type}: ${node.name} (${node.size})`\n * }\n * ```\n */\nexport interface TreeExportOptions {\n labelField?: string\n verticalLine?: string\n middleBranch?: string\n lastBranch?: string\n emptySpace?: string\n labelFunction?: (node: any) => string\n}\n\n/**\n * Renders hierarchical data as ASCII/Unicode tree visualization\n *\n * Converts nested object structures (with `children` arrays) into human-readable tree diagrams.\n * Supports multiple root nodes and customizable rendering styles.\n *\n * Features:\n * - **Recursive rendering**: Handles arbitrary depth\n * - **Unicode box-drawing**: Beautiful output by default (│ ├ └)\n * - **ASCII fallback**: Use simpler characters for compatibility\n * - **Flexible labels**: Extract from field name or custom function\n * - **Multiple roots**: Render forests (arrays of trees)\n *\n * Node Structure Requirements:\n * - Each node must have a label (via `labelField` property or `labelFunction`)\n * - Child nodes stored in `children` array property\n * - No `children` property = leaf node\n *\n * @param data - Array of root nodes (each with optional `children` property)\n * @param options - Rendering options (characters, label extraction)\n * @returns String containing the complete tree visualization with newlines\n *\n * @example\n * ```typescript\n * // Basic usage - Simple file tree\n * const fileTree = [\n * {\n * name: 'src',\n * children: [\n * { name: 'index.ts' },\n * { name: 'utils.ts' },\n * {\n * name: 'components',\n * children: [\n * { name: 'Button.tsx' },\n * { name: 'Input.tsx' }\n * ]\n * }\n * ]\n * }\n * ]\n *\n * console.log(renderTreeAsText(fileTree))\n * // Output:\n * // └── src\n * // ├── index.ts\n * // ├── utils.ts\n * // └── components\n * // ├── Button.tsx\n * // └── Input.tsx\n * ```\n *\n * @example\n * ```typescript\n * // Multiple roots - Project structure\n * const projectTree = [\n * {\n * name: 'packages',\n * children: [\n * { name: 'core' },\n * { name: 'ui' }\n * ]\n * },\n * {\n * name: 'apps',\n * children: [\n * { name: 'web' },\n * { name: 'mobile' }\n * ]\n * }\n * ]\n *\n * console.log(renderTreeAsText(projectTree))\n * // Output:\n * // ├── packages\n * // │ ├── core\n * // │ └── ui\n * // └── apps\n * // ├── web\n * // └── mobile\n * ```\n *\n * @example\n * ```typescript\n * // Custom label field - Organization chart\n * const orgChart = [\n * {\n * title: 'CEO',\n * children: [\n * {\n * title: 'CTO',\n * children: [\n * { title: 'Dev Lead' },\n * { title: 'QA Lead' }\n * ]\n * },\n * { title: 'CFO' }\n * ]\n * }\n * ]\n *\n * console.log(renderTreeAsText(orgChart, { labelField: 'title' }))\n * // Output:\n * // └── CEO\n * // ├── CTO\n * // │ ├── Dev Lead\n * // │ └── QA Lead\n * // └── CFO\n * ```\n *\n * @example\n * ```typescript\n * // Custom label function - Rich formatting\n * const tasks = [\n * {\n * name: 'Backend',\n * status: 'in-progress',\n * assignee: 'Alice',\n * children: [\n * { name: 'API', status: 'done', assignee: 'Bob' },\n * { name: 'Database', status: 'pending', assignee: 'Charlie' }\n * ]\n * }\n * ]\n *\n * const rendered = renderTreeAsText(tasks, {\n * labelFunction: (node) => `[${node.status}] ${node.name} (@${node.assignee})`\n * })\n *\n * console.log(rendered)\n * // Output:\n * // └── [in-progress] Backend (@Alice)\n * // ├── [done] API (@Bob)\n * // └── [pending] Database (@Charlie)\n * ```\n *\n * @example\n * ```typescript\n * // ASCII characters - Better terminal compatibility\n * const tree = [\n * {\n * name: 'root',\n * children: [\n * { name: 'child1' },\n * { name: 'child2' }\n * ]\n * }\n * ]\n *\n * console.log(renderTreeAsText(tree, {\n * verticalLine: '| ',\n * middleBranch: '+-- ',\n * lastBranch: '`-- ',\n * emptySpace: ' '\n * }))\n * // Output:\n * // `-- root\n * // +-- child1\n * // `-- child2\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: File system visualization\n * async function visualizeDirectory(dirPath: string) {\n * const tree = await buildDirectoryTree(dirPath)\n *\n * console.log(`Directory structure of ${dirPath}:`)\n * console.log(renderTreeAsText(tree, {\n * labelFunction: (node) => {\n * const icon = node.type === 'dir' ? '📁' : '📄'\n * return `${icon} ${node.name}`\n * }\n * }))\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Dependency tree analysis\n * function visualizeDependencies(packageName: string) {\n * const deps = analyzeDependencies(packageName)\n *\n * console.log(`Dependencies of ${packageName}:`)\n * console.log(renderTreeAsText(deps, {\n * labelFunction: (node) => `${node.name}@${node.version} (${node.size})`\n * }))\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Edge case: Empty tree\n * console.log(renderTreeAsText([])) // Returns empty string\n *\n * // Edge case: Leaf nodes only\n * const leaves = [{ name: 'item1' }, { name: 'item2' }]\n * console.log(renderTreeAsText(leaves))\n * // Output:\n * // ├── item1\n * // └── item2\n *\n * // Edge case: Deep nesting\n * const deep = [\n * {\n * name: 'level1',\n * children: [\n * {\n * name: 'level2',\n * children: [\n * { name: 'level3' }\n * ]\n * }\n * ]\n * }\n * ]\n * console.log(renderTreeAsText(deep))\n * // Handles arbitrary depth correctly\n * ```\n *\n * @see {@link TreeExportOptions} for configuration details\n */\nexport function renderTreeAsText(data: any[], options?: TreeExportOptions): string {\n // ✅ Input validation\n if (!data || !Array.isArray(data)) {\n return ''\n }\n\n if (data.length === 0) {\n return ''\n }\n\n const {\n labelField = 'name',\n verticalLine = '│ ',\n middleBranch = '├── ',\n lastBranch = '└── ',\n emptySpace = ' ',\n labelFunction,\n } = options || {}\n\n // ✅ Circular reference detection\n const visited = new WeakSet<any>()\n\n const getLabel = (node: any): string => {\n try {\n if (labelFunction) return String(labelFunction(node))\n if (node == null) return '[null]'\n if (typeof node !== 'object') return String(node)\n return node[labelField] != null ? String(node[labelField]) : String(node)\n } catch (_error) {\n // ✅ Error handling for labelFunction\n return '[error]'\n }\n }\n\n const renderNode = (\n node: any,\n prefix: string = '',\n isLast: boolean = true,\n depth: number = 0\n ): string => {\n // ✅ Null/undefined node handling\n if (node == null) {\n return ''\n }\n\n // ✅ Circular reference detection\n if (typeof node === 'object' && visited.has(node)) {\n return `${prefix + (isLast ? lastBranch : middleBranch)}[circular reference]`\n }\n\n // ✅ Max depth protection (prevent stack overflow)\n if (depth > 100) {\n return `${prefix + (isLast ? lastBranch : middleBranch)}[max depth exceeded]`\n }\n\n if (typeof node === 'object') {\n visited.add(node)\n }\n\n const lines: string[] = []\n const currentPrefix = isLast ? lastBranch : middleBranch\n lines.push(prefix + currentPrefix + getLabel(node))\n\n // ✅ Children validation\n const children = Array.isArray(node.children)\n ? node.children.filter((child: any) => child != null) // Filter null/undefined\n : []\n\n const nextPrefix = prefix + (isLast ? emptySpace : verticalLine)\n\n children.forEach((child: any, index: number) => {\n const isLastChild = index === children.length - 1\n const childOutput = renderNode(child, nextPrefix, isLastChild, depth + 1)\n if (childOutput) {\n lines.push(childOutput)\n }\n })\n\n return lines.join('\\n')\n }\n\n // ✅ Filter null/undefined roots\n const validRoots = data.filter(root => root != null)\n\n if (validRoots.length === 0) {\n return ''\n }\n\n return validRoots\n .map((root, index) => renderNode(root, '', index === validRoots.length - 1))\n .join('\\n')\n}\n","/**\n * File and security validators\n *\n * This module provides validation functions for file operations and content security.\n * Functions implement basic security checks to prevent common attack vectors:\n * - Path traversal attacks (../, ..\\)\n * - XSS injection (script tags, javascript: URLs)\n * - File size limits\n *\n * ⚠️ WARNING: These are basic validations. For production systems, consider:\n * - Additional OS-specific path validation\n * - Symbolic link resolution checking\n * - Comprehensive XSS prevention libraries (e.g., DOMPurify)\n * - Content-Type validation\n * - Virus scanning for uploaded files\n */\n\n/**\n * Validates file path for security vulnerabilities\n *\n * Protects against common path traversal attacks by checking for:\n * - Parent directory references (../, ..\\)\n * - Mixed path separators (//\\, \\\\)\n * - Excessive path length (>1000 chars)\n * - Empty or null paths\n *\n * ⚠️ SECURITY NOTE: This is basic validation. For production use, consider:\n * - Validating against an allowed directory whitelist\n * - Resolving symbolic links\n * - OS-specific path rules (Windows vs Unix)\n * - Canonicalization before validation\n *\n * @param filePath - File path string to validate\n * @returns True if path appears safe, false if potentially dangerous\n *\n * @example\n * ```typescript\n * // Safe paths - Allowed\n * isValidFilePath('./data/users.json') // true\n * isValidFilePath('data/users.json') // true\n * isValidFilePath('/absolute/path/file.txt') // true\n * isValidFilePath('C:\\\\Users\\\\data\\\\file.txt') // true\n *\n * // Dangerous paths - Path traversal attacks\n * isValidFilePath('../../../etc/passwd') // false\n * isValidFilePath('data/../../../etc/passwd') // false\n * isValidFilePath('..\\\\..\\\\..\\\\windows\\\\system32\\\\config') // false\n * isValidFilePath('data//\\\\..//config') // false\n *\n * // Invalid paths - Malformed\n * isValidFilePath('') // false\n * isValidFilePath(null as any) // false\n * isValidFilePath(undefined as any) // false\n * isValidFilePath('x'.repeat(1001)) // false (too long)\n *\n * // Real-world usage in file operations\n * async function readUserFile(userPath: string) {\n * if (!isValidFilePath(userPath)) {\n * throw new Error('Invalid or unsafe file path detected')\n * }\n *\n * // Additional check: ensure path is within allowed directory\n * const allowedDir = '/var/app/uploads'\n * const fullPath = path.join(allowedDir, userPath)\n *\n * return fs.readFile(fullPath, 'utf-8')\n * }\n * ```\n *\n * @see {@link isValidTextContent} for content security validation\n * @see {@link isValidFileSize} for file size validation\n */\nexport function isValidFilePath(filePath: string): boolean {\n // ✅ Type and null checks\n if (!filePath || typeof filePath !== 'string') return false\n\n // ✅ Handle type coercion attempts\n if (typeof filePath === 'object') return false\n\n // ✅ Length limits (DoS prevention)\n if (filePath.length === 0 || filePath.length > 1000) return false\n\n // ✅ Path traversal patterns (Unix and Windows)\n // Reject parent directory traversal (..)\n if (filePath.includes('../') || filePath.includes('..\\\\')) return false\n\n // Reject mixed/double slashes (but allow ./ and .\\ at start for relative paths)\n const suspiciousPatterns = ['//', '\\\\\\\\', '//\\\\', '\\\\//', '/./', '\\\\.\\\\']\n if (suspiciousPatterns.some(pattern => filePath.includes(pattern))) return false\n\n // ✅ URL-encoded path traversal\n const encodedPatterns = [\n '%2e%2e%2f', // ../\n '%2e%2e/', // ../\n '%2e%2e%5c', // ..\\\n '%252e', // double-encoded\n ]\n if (encodedPatterns.some(pattern => filePath.toLowerCase().includes(pattern))) return false\n\n // ✅ Null byte injection\n if (filePath.includes('\\x00')) return false\n\n // ✅ Command injection characters\n const commandChars = [';', '&', '|', '`', '$', '(', ')', '{', '}']\n if (commandChars.some(char => filePath.includes(char))) return false\n\n // ✅ SQL injection attempts\n const sqlPatterns = [\"'\", '\"', '--', '/*', '*/', 'DROP', 'DELETE', 'UNION', 'SELECT']\n const lowerPath = filePath.toLowerCase()\n if (\n sqlPatterns.some(\n pattern =>\n lowerPath.includes(pattern.toLowerCase()) &&\n (pattern === \"'\" || pattern === '\"' || lowerPath.includes(` ${pattern.toLowerCase()}`))\n )\n )\n return false\n\n // ✅ Unicode attacks (RTL override, zero-width)\n const dangerousUnicode = [\n '\\u202E', // Right-to-left override\n '\\u200B', // Zero-width space\n '\\u200C', // Zero-width non-joiner\n '\\u200D', // Zero-width joiner\n '\\uFEFF', // Zero-width no-break space\n ]\n if (dangerousUnicode.some(char => filePath.includes(char))) return false\n\n // ✅ Absolute Windows paths trying to escape\n if (/^[A-Za-z]:\\\\.*\\\\\\.\\./.test(filePath)) return false\n\n return true\n}\n\n/**\n * Validates file size against a maximum limit\n *\n * Checks if a file size is valid (non-negative) and within acceptable limits.\n * Used to prevent:\n * - Denial of Service (DoS) attacks via large file uploads\n * - Disk space exhaustion\n * - Memory overflow during file processing\n *\n * @param size - File size in bytes to validate\n * @param maxSize - Maximum allowed size in bytes\n * @returns True if size is valid and within limit, false otherwise\n *\n * @example\n * ```typescript\n * // Common size limits\n * const KB = 1024\n * const MB = 1024 * KB\n * const GB = 1024 * MB\n *\n * // Image upload validation (5 MB limit)\n * const imageSize = 4 * MB\n * isValidFileSize(imageSize, 5 * MB) // true\n *\n * // Document upload validation (10 MB limit)\n * const docSize = 12 * MB\n * isValidFileSize(docSize, 10 * MB) // false (exceeds limit)\n *\n * // Video upload validation (1 GB limit)\n * const videoSize = 500 * MB\n * isValidFileSize(videoSize, 1 * GB) // true\n *\n * // Invalid sizes\n * isValidFileSize(-100, 1 * MB) // false (negative size)\n * isValidFileSize(NaN, 1 * MB) // false (invalid number)\n * isValidFileSize('1000' as any, 1 * MB) // false (not a number)\n *\n * // Real-world usage in file upload handler\n * app.post('/upload', (req, res) => {\n * const file = req.files.document\n * const maxSize = 10 * 1024 * 1024 // 10 MB\n *\n * if (!isValidFileSize(file.size, maxSize)) {\n * return res.status(413).json({\n * error: 'File too large',\n * maxSize: '10 MB',\n * received: `${(file.size / (1024 * 1024)).toFixed(2)} MB`\n * })\n * }\n *\n * // Process file...\n * })\n * ```\n *\n * @see {@link isValidFilePath} for path security validation\n */\nexport function isValidFileSize(size: number, maxSize: number): boolean {\n return typeof size === 'number' && size >= 0 && size <= maxSize\n}\n\n/**\n * Validates text content for security vulnerabilities and size limits\n *\n * Performs basic security checks on text content to detect common XSS attack vectors:\n * - Script tags (<script>)\n * - JavaScript URLs (javascript:)\n * - Data URLs with HTML (data:text/html)\n * - VBScript URLs (vbscript:)\n *\n * Also enforces maximum content length to prevent DoS attacks.\n *\n * ⚠️ WARNING: This is basic XSS detection. For production systems:\n * - Use dedicated sanitization libraries (DOMPurify, sanitize-html)\n * - Implement Content Security Policy (CSP)\n * - Apply output encoding based on context (HTML, JS, URL)\n * - Validate against allowlists, not just blocklists\n *\n * @param content - Text content to validate\n * @param options - Validation options\n * @param options.maxLength - Maximum content length in characters (default: 1,000,000)\n * @returns True if content appears safe, false if dangerous patterns detected\n *\n * @example\n * ```typescript\n * // Safe content - Allowed\n * isValidTextContent('Hello, world!') // true\n * isValidTextContent('User input: <b>bold</b>') // true\n * isValidTextContent('Email: user@example.com') // true\n *\n * // Dangerous content - XSS vectors detected\n * isValidTextContent('<script>alert(\"XSS\")</script>') // false\n * isValidTextContent('<img src=x onerror=\"alert(1)\">') // false (contains 'script' in onerror)\n * isValidTextContent('<a href=\"javascript:void(0)\">') // false\n * isValidTextContent('<iframe src=\"data:text/html,...\"') // false\n * isValidTextContent('vbscript:msgbox(\"XSS\")') // false\n *\n * // Size limit validation\n * isValidTextContent('x'.repeat(999_999)) // true (under default 1M limit)\n * isValidTextContent('x'.repeat(1_000_001)) // false (exceeds default limit)\n * isValidTextContent('x'.repeat(5000), { maxLength: 1000 }) // false (custom limit)\n *\n * // Edge cases\n * isValidTextContent('') // true (empty is valid)\n * isValidTextContent(null as any) // false (not a string)\n * isValidTextContent(undefined as any) // false (not a string)\n * isValidTextContent(123 as any) // false (not a string)\n *\n * // Real-world usage in comment system\n * app.post('/api/comments', (req, res) => {\n * const { content } = req.body\n *\n * if (!isValidTextContent(content, { maxLength: 5000 })) {\n * return res.status(400).json({\n * error: 'Invalid comment content',\n * details: 'Content contains dangerous patterns or exceeds 5000 characters'\n * })\n * }\n *\n * // Additional sanitization recommended\n * const sanitized = DOMPurify.sanitize(content)\n *\n * // Save to database...\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Form validation with custom limits\n * function validateUserBio(bio: string): { valid: boolean; error?: string } {\n * if (!isValidTextContent(bio, { maxLength: 500 })) {\n * return {\n * valid: false,\n * error: 'Bio must be less than 500 characters and cannot contain scripts'\n * }\n * }\n * return { valid: true }\n * }\n * ```\n *\n * @see {@link isValidFilePath} for file path validation\n * @see sanitizeHtml from validation module for HTML sanitization\n */\nexport function isValidTextContent(content: string, options: { maxLength?: number } = {}): boolean {\n const { maxLength = 1_000_000 } = options\n\n // ✅ Type validation\n if (typeof content !== 'string') return false\n\n // ✅ Length validation (DoS prevention)\n if (content.length > maxLength) return false\n\n const lowerContent = content.toLowerCase()\n\n // ✅ Script tag detection (case-insensitive, with variants)\n const scriptPatterns = ['<script', '</script', '<scr<script>ipt', '<scr\\x00ipt']\n if (scriptPatterns.some(pattern => lowerContent.includes(pattern))) return false\n\n // ✅ Event handler attributes (XSS vectors)\n const eventHandlers = [\n 'onerror',\n 'onload',\n 'onclick',\n 'onmouseover',\n 'onfocus',\n 'onblur',\n 'oninput',\n ]\n if (eventHandlers.some(handler => lowerContent.includes(handler))) return false\n\n // ✅ Protocol handlers (javascript:, data:, vbscript:, file:)\n const protocolPatterns = [\n 'javascript:',\n 'data:text/html',\n 'data:text/javascript',\n 'data:application',\n 'vbscript:',\n 'file:///',\n 'steam://',\n 'slack://',\n ]\n if (protocolPatterns.some(pattern => lowerContent.includes(pattern))) return false\n\n // ✅ Encoded script attempts\n const encodedPatterns = [\n '&#60;script', // &#60; = <\n '%3cscript', // %3c = <\n '\\\\x3cscript', // \\x3c = <\n '\\\\u003cscript', // \\u003c = <\n ]\n if (encodedPatterns.some(pattern => lowerContent.includes(pattern))) return false\n\n // ✅ SVG-based XSS\n if (lowerContent.includes('<svg') && lowerContent.includes('onload')) return false\n\n // ✅ iframe injection\n if (lowerContent.includes('<iframe')) return false\n\n return true\n}\n","/**\n * CSV Import/Export utilities\n *\n * Provides reliable CSV operations for both Node.js and Browser environments using PapaParse.\n * Features:\n * - UTF-8 BOM encoding for Excel compatibility\n * - Semicolon delimiter by default (European standard)\n * - Automatic header detection\n * - Dual-mode: filesystem (Node.js) vs download trigger (Browser)\n *\n * @module csv\n */\n\nimport Papa from 'papaparse'\nimport { isNode } from './environment'\n\n/**\n * Exports data as CSV file with UTF-8 BOM encoding\n *\n * Works in both Node.js (writes to filesystem) and Browser (triggers download).\n * Uses PapaParse library for reliable CSV generation with proper escaping and formatting.\n *\n * Features:\n * - UTF-8 BOM prepended (\\\\ufeff) for Excel compatibility\n * - Semicolon delimiter by default (European/Spanish standard)\n * - Automatic header row from object keys\n * - Custom delimiters and options via PapaParse config\n *\n * Environment behavior:\n * - **Node.js**: Writes file to filesystem at specified path\n * - **Browser**: Triggers automatic download with filename extracted from path\n *\n * @param data - Array of objects or array of arrays to export as CSV\n * @param filePath - File path (Node.js) or download filename (Browser)\n * @param options - PapaParse unparse options (optional)\n * @param options.delimiter - Column separator (default: ';' semicolon)\n * @param options.header - Include header row (default: true)\n * @param options.quotes - Quote all fields (default: false)\n * @param options.newline - Line ending (default: '\\\\r\\\\n')\n * @returns Promise that resolves when export completes\n *\n * @example\n * ```typescript\n * // Basic export - Array of objects\n * const users = [\n * { name: 'Alice', age: 30, city: 'Madrid' },\n * { name: 'Bob', age: 25, city: 'Barcelona' },\n * { name: 'Carlos', age: 35, city: 'Valencia' }\n * ]\n *\n * // Node.js - Write to file\n * await exportCSV(users, './reports/users.csv')\n * // Creates: ./reports/users.csv with BOM and semicolon delimiter\n *\n * // Browser - Trigger download\n * await exportCSV(users, 'users.csv')\n * // Downloads: users.csv to browser's download folder\n *\n * // Custom delimiter (comma for international)\n * await exportCSV(users, 'users_intl.csv', { delimiter: ',' })\n * ```\n *\n * @example\n * ```typescript\n * // Array of arrays (no header auto-detection)\n * const matrix = [\n * ['Name', 'Age', 'City'], // Header row\n * ['Alice', 30, 'Madrid'],\n * ['Bob', 25, 'Barcelona']\n * ]\n * await exportCSV(matrix, 'matrix.csv')\n *\n * // Without header row\n * const data = [['A1', 'B1'], ['A2', 'B2']]\n * await exportCSV(data, 'no_header.csv', { header: false })\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Export sales report\n * async function exportSalesReport(sales: Sale[]) {\n * const reportData = sales.map(sale => ({\n * Fecha: formatDate(sale.date),\n * Cliente: sale.customerName,\n * Producto: sale.productName,\n * Cantidad: sale.quantity,\n * Total: `${sale.total.toFixed(2)}€`\n * }))\n *\n * const filename = `ventas_${new Date().toISOString().split('T')[0]}.csv`\n * await exportCSV(reportData, filename)\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Custom PapaParse options\n * await exportCSV(data, 'quotes.csv', {\n * delimiter: ',',\n * quotes: true, // Quote all fields\n * quoteChar: '\"',\n * escapeChar: '\"',\n * newline: '\\\\n' // Unix line endings\n * })\n * ```\n *\n * @throws {Error} If file write fails in Node.js\n * @see {@link importCSV} for importing CSV files\n * @see {@link https://www.papaparse.com/docs#unparse PapaParse Unparse Documentation}\n */\nexport async function exportCSV(data: any[], filePath: string, options?: any): Promise<void> {\n // ✅ Input validation\n if (data == null) {\n throw new TypeError('Data cannot be null or undefined')\n }\n\n if (!Array.isArray(data)) {\n throw new TypeError('Data must be an array')\n }\n\n if (data.length === 0) {\n throw new Error('Data array cannot be empty')\n }\n\n if (!filePath || typeof filePath !== 'string' || filePath.trim() === '') {\n throw new TypeError('File path must be a non-empty string')\n }\n\n const csvText = Papa.unparse(data, {\n delimiter: ';',\n header: true,\n ...options,\n })\n\n if (isNode()) {\n const fs = await import('fs/promises')\n await fs.writeFile(filePath, `\\ufeff${csvText}`, { encoding: 'utf-8' })\n } else {\n const blob = new Blob([`\\ufeff${csvText}`], { type: 'text/csv;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = filePath.split('/').pop() || 'data.csv'\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n URL.revokeObjectURL(url)\n }\n}\n\n/**\n * Imports CSV file and parses it to array of objects\n *\n * Reads CSV file from filesystem (Node.js only) and parses using PapaParse.\n * Automatically detects headers and converts rows to objects.\n *\n * Features:\n * - Automatic header detection from first row\n * - Semicolon delimiter by default (European standard)\n * - Empty line skipping\n * - Type inference for numbers and booleans\n * - UTF-8 BOM handling\n *\n * ⚠️ BROWSER LIMITATION: File reading from filesystem is not supported in browsers\n * for security reasons. For browser file uploads, use `readFileAsText()` with a File object\n * obtained from an input[type=\"file\"] element.\n *\n * @param filePath - Path to CSV file (Node.js only)\n * @param options - PapaParse parse options (optional)\n * @param options.delimiter - Column separator (default: ';' semicolon)\n * @param options.header - Parse first row as headers (default: true)\n * @param options.skipEmptyLines - Skip empty rows (default: true)\n * @param options.dynamicTyping - Convert numeric/boolean values (default: false)\n * @returns Promise<Array<Object>> Array of objects with keys from header row\n *\n * @example\n * ```typescript\n * // Basic import - Node.js only\n * const users = await importCSV('./data/users.csv')\n * // Returns: [\n * // { name: 'Alice', age: '30', city: 'Madrid' },\n * // { name: 'Bob', age: '25', city: 'Barcelona' }\n * // ]\n *\n * // Custom delimiter (comma)\n * const intlData = await importCSV('./data/users_intl.csv', {\n * delimiter: ','\n * })\n *\n * // With type conversion\n * const typed = await importCSV('./data/sales.csv', {\n * dynamicTyping: true // Converts '123' → 123, 'true' → true\n * })\n * // Returns: { quantity: 10, total: 299.99, active: true }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Import and process sales data\n * async function importSalesReport(filePath: string) {\n * try {\n * const sales = await importCSV(filePath, {\n * delimiter: ';',\n * dynamicTyping: true,\n * skipEmptyLines: true\n * })\n *\n * // Process imported data\n * const totalSales = sales.reduce((sum, sale) =>\n * sum + (sale.total || 0), 0\n * )\n *\n * console.log(`Imported ${sales.length} sales, total: €${totalSales}`)\n * return sales\n * } catch (error) {\n * console.error('Failed to import CSV:', error)\n * throw error\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Browser alternative using File API\n * // HTML: <input type=\"file\" id=\"csvFile\" accept=\".csv\">\n *\n * document.getElementById('csvFile').addEventListener('change', async (e) => {\n * const file = e.target.files[0]\n *\n * // Read file content\n * const content = await readFileAsText(file)\n *\n * // Parse with PapaParse\n * const parsed = Papa.parse(content, {\n * header: true,\n * delimiter: ';',\n * skipEmptyLines: true\n * })\n *\n * const data = parsed.data\n * console.log('Imported data:', data)\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Handle parsing errors\n * const result = await importCSV('./malformed.csv')\n * .catch(error => {\n * if (error.message.includes('not supported in browser')) {\n * console.error('Use File API for browser uploads')\n * } else if (error.code === 'ENOENT') {\n * console.error('File not found')\n * } else {\n * console.error('CSV parsing failed:', error)\n * }\n * return [] // Return empty array as fallback\n * })\n * ```\n *\n * @throws {Error} 'CSV import not supported in browser' when called in browser environment\n * @throws {Error} File system errors (ENOENT, EACCES, etc.) in Node.js\n * @see {@link exportCSV} for exporting CSV files\n * @see {@link readFileAsText} for browser file reading\n * @see {@link https://www.papaparse.com/docs#parse PapaParse Parse Documentation}\n */\nexport async function importCSV(filePath: string, options?: any): Promise<any[]> {\n // ✅ Input validation\n if (!filePath || typeof filePath !== 'string' || filePath.trim() === '') {\n throw new TypeError('File path must be a non-empty string')\n }\n\n if (isNode()) {\n const fs = await import('fs/promises')\n const content = await fs.readFile(filePath, { encoding: 'utf-8' })\n const parseResult = Papa.parse(content, {\n header: true,\n delimiter: ';',\n skipEmptyLines: true,\n ...options,\n }) as any\n return parseResult.data || []\n } else {\n throw new Error('CSV import not supported in browser. Use readFileAsText() with a File object.')\n }\n}\n","/**\n * JSON Import/Export utilities\n *\n * Provides reliable JSON file operations for both Node.js and Browser environments.\n * Features:\n * - UTF-8 encoding for international character support\n * - Pretty-printing with configurable indentation\n * - Dual-mode: filesystem (Node.js) vs download trigger (Browser)\n * - Native JSON.stringify/parse for maximum compatibility\n *\n * @module json\n */\n\nimport { isNode } from './environment'\n\n/**\n * Exports data as JSON file with UTF-8 encoding\n *\n * Works in both Node.js (writes to filesystem) and Browser (triggers download).\n * Uses native JSON.stringify for serialization with optional pretty-printing.\n *\n * Features:\n * - UTF-8 encoding for international characters\n * - Configurable indentation (default: 2 spaces)\n * - Automatic filename extraction in browser\n * - Pretty-printed by default for readability\n *\n * Environment behavior:\n * - **Node.js**: Writes file to filesystem at specified path\n * - **Browser**: Triggers automatic download with filename extracted from path\n *\n * ⚠️ WARNING: Cannot serialize:\n * - Functions (removed silently)\n * - Symbols (removed silently)\n * - Circular references (throws TypeError)\n * - undefined values (removed from objects, converted to null in arrays)\n * - BigInt values (throws TypeError unless custom replacer provided)\n *\n * @param data - Any serializable JavaScript value (object, array, primitive)\n * @param filePath - File path (Node.js) or download filename (Browser)\n * @param options - Export options (optional)\n * @param options.indent - Number of spaces for indentation (default: 2, use 0 for compact)\n * @returns Promise that resolves when export completes\n *\n * @example\n * ```typescript\n * // Basic export - Object\n * const config = {\n * database: { host: 'localhost', port: 5432 },\n * features: { auth: true, payments: false }\n * }\n *\n * // Node.js - Write to file\n * await exportJSON(config, './config/app.json')\n * // Creates: ./config/app.json with 2-space indentation\n *\n * // Browser - Trigger download\n * await exportJSON(config, 'app-config.json')\n * // Downloads: app-config.json to browser's download folder\n *\n * // Compact output (no pretty-printing)\n * await exportJSON(config, 'config.json', { indent: 0 })\n * // Single-line JSON: {\"database\":{\"host\":\"localhost\",\"port\":5432}}\n * ```\n *\n * @example\n * ```typescript\n * // Array export\n * const users = [\n * { id: 1, name: 'Alice', email: 'alice@example.com' },\n * { id: 2, name: 'Bob', email: 'bob@example.com' }\n * ]\n * await exportJSON(users, 'users.json')\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Configuration backup system\n * async function backupConfiguration(config: AppConfig) {\n * const timestamp = new Date().toISOString().split('T')[0]\n * const filename = `config-backup-${timestamp}.json`\n *\n * try {\n * await exportJSON(config, `./backups/${filename}`, { indent: 2 })\n * console.log(`✅ Configuration backed up to ${filename}`)\n * } catch (error) {\n * console.error('❌ Backup failed:', error)\n * throw error\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Export API response for debugging\n * async function debugApiResponse(endpoint: string, data: any) {\n * const filename = `api-${endpoint.replace(/\\//g, '-')}.json`\n * await exportJSON({\n * endpoint,\n * timestamp: new Date().toISOString(),\n * data\n * }, filename)\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Handle circular references\n * const obj = { name: 'test' }\n * obj.self = obj // Circular reference\n *\n * try {\n * await exportJSON(obj, 'circular.json')\n * } catch (error) {\n * console.error('Cannot serialize circular structure')\n * // Use custom replacer to handle circular refs\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Different indentation levels\n * await exportJSON(data, 'compact.json', { indent: 0 }) // Single line\n * await exportJSON(data, 'readable.json', { indent: 2 }) // 2 spaces (default)\n * await exportJSON(data, 'spacious.json', { indent: 4 }) // 4 spaces\n * ```\n *\n * @throws {TypeError} If data contains circular references or BigInt values\n * @throws {Error} If file write fails in Node.js (permissions, disk space, etc.)\n * @see {@link importJSON} for importing JSON files\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify JSON.stringify Documentation}\n */\nexport async function exportJSON(\n data: any,\n filePath: string,\n options?: { indent?: number }\n): Promise<void> {\n // ✅ Input validation\n if (data === null || data === undefined) {\n throw new TypeError('Data cannot be null or undefined')\n }\n\n if (!filePath || typeof filePath !== 'string' || filePath.trim() === '') {\n throw new TypeError('File path must be a non-empty string')\n }\n\n const { indent = 2 } = options || {}\n\n // ✅ Detect circular references before stringify\n let jsonText: string\n try {\n jsonText = JSON.stringify(data, null, indent)\n } catch (error) {\n if (error instanceof TypeError && error.message.includes('circular')) {\n throw new TypeError('Cannot serialize data with circular references')\n }\n throw error\n }\n\n if (isNode()) {\n const fs = await import('fs/promises')\n await fs.writeFile(filePath, jsonText, { encoding: 'utf-8' })\n } else {\n const blob = new Blob([jsonText], { type: 'application/json;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = filePath.split('/').pop() || 'data.json'\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n URL.revokeObjectURL(url)\n }\n}\n\n/**\n * Imports JSON file and parses it to JavaScript value\n *\n * Reads JSON file from filesystem (Node.js only) and parses using native JSON.parse.\n * Returns the parsed JavaScript value (object, array, primitive).\n *\n * Features:\n * - UTF-8 encoding support for international characters\n * - Native JSON.parse for standard-compliant parsing\n * - Automatic type inference\n * - Whitespace and comment tolerance (standard JSON only)\n *\n * ⚠️ BROWSER LIMITATION: File reading from filesystem is not supported in browsers\n * for security reasons. For browser file uploads, use `readFileAsText()` with a File object\n * obtained from an input[type=\"file\"] element.\n *\n * @param filePath - Path to JSON file (Node.js only)\n * @param _options - Reserved for future options (currently unused)\n * @returns Promise<any> Parsed JavaScript value (object, array, primitive, null)\n *\n * @example\n * ```typescript\n * // Basic import - Node.js only\n * const config = await importJSON('./config/app.json')\n * // Returns: { database: { host: 'localhost', port: 5432 }, ... }\n *\n * console.log(config.database.host) // 'localhost'\n *\n * // Array import\n * const users = await importJSON('./data/users.json')\n * // Returns: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]\n *\n * // Primitive values (valid JSON)\n * const version = await importJSON('./version.json') // \"1.2.3\"\n * const enabled = await importJSON('./feature.json') // true\n * const count = await importJSON('./count.json') // 42\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Load configuration with fallback\n * async function loadConfiguration(configPath: string): Promise<AppConfig> {\n * try {\n * const config = await importJSON(configPath)\n *\n * // Validate loaded config\n * if (!config.database || !config.database.host) {\n * throw new Error('Invalid configuration: missing database.host')\n * }\n *\n * return config\n * } catch (error) {\n * console.warn(`Failed to load config from ${configPath}:`, error)\n * console.log('Using default configuration')\n * return getDefaultConfig()\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Restore configuration backup\n * async function restoreBackup(backupFile: string) {\n * console.log(`Restoring configuration from ${backupFile}...`)\n *\n * const backup = await importJSON(`./backups/${backupFile}`)\n *\n * // Verify backup integrity\n * if (!backup.timestamp || !backup.config) {\n * throw new Error('Invalid backup file format')\n * }\n *\n * // Restore configuration\n * await saveConfiguration(backup.config)\n *\n * console.log(`✅ Configuration restored from ${backup.timestamp}`)\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Browser alternative using File API\n * // HTML: <input type=\"file\" id=\"jsonFile\" accept=\".json\">\n *\n * document.getElementById('jsonFile').addEventListener('change', async (e) => {\n * const file = e.target.files[0]\n *\n * // Read file content\n * const content = await readFileAsText(file)\n *\n * // Parse JSON\n * try {\n * const data = JSON.parse(content)\n * console.log('Imported JSON:', data)\n * } catch (error) {\n * console.error('Invalid JSON file:', error)\n * }\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Handle parsing errors\n * async function safeImportJSON(filePath: string): Promise<any | null> {\n * try {\n * return await importJSON(filePath)\n * } catch (error) {\n * if (error.message.includes('not supported in browser')) {\n * console.error('Use File API for browser uploads')\n * } else if (error.code === 'ENOENT') {\n * console.error(`File not found: ${filePath}`)\n * } else if (error instanceof SyntaxError) {\n * console.error(`Invalid JSON syntax in ${filePath}:`, error.message)\n * } else {\n * console.error('JSON import failed:', error)\n * }\n * return null\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Type-safe import with validation\n * interface DatabaseConfig {\n * host: string\n * port: number\n * database: string\n * }\n *\n * async function loadDatabaseConfig(path: string): Promise<DatabaseConfig> {\n * const raw = await importJSON(path)\n *\n * // Runtime validation\n * if (typeof raw.host !== 'string' || typeof raw.port !== 'number') {\n * throw new Error('Invalid database configuration structure')\n * }\n *\n * return raw as DatabaseConfig\n * }\n * ```\n *\n * @throws {Error} 'JSON import not supported in browser' when called in browser environment\n * @throws {SyntaxError} If file contains invalid JSON syntax\n * @throws {Error} File system errors (ENOENT, EACCES, etc.) in Node.js\n * @see {@link exportJSON} for exporting JSON files\n * @see {@link readFileAsText} for browser file reading\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse JSON.parse Documentation}\n */\nexport async function importJSON(filePath: string, _options?: any): Promise<any> {\n // ✅ Input validation\n if (!filePath || typeof filePath !== 'string' || filePath.trim() === '') {\n throw new TypeError('File path must be a non-empty string')\n }\n\n if (isNode()) {\n const fs = await import('fs/promises')\n const content = await fs.readFile(filePath, { encoding: 'utf-8' })\n\n // ✅ Validate content is not empty\n const trimmed = content.trim()\n if (trimmed === '') {\n throw new SyntaxError('JSON file is empty')\n }\n\n // ✅ Better error message for invalid JSON\n try {\n return JSON.parse(content)\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new SyntaxError(`Invalid JSON in file ${filePath}: ${error.message}`)\n }\n throw error\n }\n } else {\n throw new Error(\n 'JSON import not supported in browser. Use readFileAsText() with a File object.'\n )\n }\n}\n","/**\n * Data manipulation utilities for multiple formats with automatic detection\n * Consolidated from data/index module\n */\n\n/* eslint-disable max-lines-per-function */\n\nimport { DataError, TsHelpersErrorCode, createValidationError } from './errors'\nimport { isNode, isBrowser } from './environment'\n\n// =============================================================================\n// SHARED TYPES\n// =============================================================================\n\n/**\n * Type for data - array of objects or array of arrays (for CSV, etc.)\n */\nexport type ExportData = Record<string, any>[] | string[][]\n\n/**\n * Type for imported data - can include string for .tree files\n */\nexport type ImportData = ExportData | string | any\n\n/**\n * Type for CSV data (backward compatibility)\n */\nexport type CSVData = ExportData\n\n/**\n * Supported format types\n */\nexport type ExportFormat = 'csv' | 'json' | 'tree' | 'txt'\n\n/**\n * Universal file format detection - detects any file extension\n */\nexport type FileFormat = string\n\n// =============================================================================\n// ENVIRONMENT UTILITIES\n// =============================================================================\n\n/**\n * Reads a file as text in any environment (Node.js or Browser)\n *\n * Universal file reading utility that adapts to the runtime environment.\n * In Node.js, reads from filesystem. In Browser, reads from File object.\n *\n * Environment behavior:\n * - **Node.js**: Pass file path as string, reads from filesystem\n * - **Browser**: Pass File object (from input[type=\"file\"]), reads with FileReader API\n *\n * @param fileOrPath - File path string (Node.js) or File object (Browser)\n * @param encoding - Text encoding for Node.js filesystem read (default: 'utf8')\n * @returns Promise<string> File contents as text\n *\n * @example\n * ```typescript\n * // Node.js - Read from filesystem\n * const config = await readFileAsText('./config.json')\n * const data = JSON.parse(config)\n *\n * // Browser - Read from File input\n * // HTML: <input type=\"file\" id=\"fileInput\">\n * const input = document.getElementById('fileInput') as HTMLInputElement\n * input.addEventListener('change', async (e) => {\n * const file = e.target.files[0]\n * const content = await readFileAsText(file)\n * console.log('File content:', content)\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Load and parse CSV from user upload (Browser)\n * async function handleCSVUpload(file: File) {\n * const csvText = await readFileAsText(file)\n * const rows = csvText.split('\\n').map(row => row.split(';'))\n * console.log(`Loaded ${rows.length} rows`)\n * return rows\n * }\n * ```\n *\n * @throws {DataError} If called with invalid parameter for current environment\n * @see {@link importData} for format-aware file importing\n */\nexport const readFileAsText = async (\n fileOrPath: string | File,\n encoding: BufferEncoding = 'utf8'\n): Promise<string> => {\n if (isNode() && typeof fileOrPath === 'string') {\n // Node.js environment - read from filesystem\n const fs = await import('fs/promises')\n return fs.readFile(fileOrPath, encoding)\n } else if (isBrowser() && fileOrPath instanceof File) {\n // Browser environment - read File object\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = e => resolve(e.target?.result as string)\n reader.onerror = () => reject(new Error('Error reading file'))\n reader.readAsText(fileOrPath)\n })\n } else {\n throw new DataError(\n 'Invalid parameter for current environment',\n TsHelpersErrorCode.ENVIRONMENT_NOT_SUPPORTED,\n {\n data: {\n isNodeEnv: isNode(),\n isBrowserEnv: isBrowser(),\n parameterType: typeof fileOrPath,\n },\n }\n )\n }\n}\n\n// =============================================================================\n// DATA VALIDATION\n// =============================================================================\n\n/**\n * Validates that data is exportable (array of objects or array of arrays)\n *\n * Ensures data structure is compatible with export formats (CSV, JSON).\n * Validates consistency: all elements must be same type, arrays must have equal length.\n *\n * Validation rules:\n * - Must be non-empty array\n * - If first element is object → all must be objects (no arrays/primitives)\n * - If first element is array → all must be arrays with equal length\n * - Null/undefined not allowed\n *\n * @param data - Data to validate for export\n * @returns `true` if data is valid (type guard for ExportData)\n *\n * @example\n * ```typescript\n * // Valid: Array of objects (typical use case)\n * const users = [\n * { id: 1, name: 'Alice', role: 'Admin' },\n * { id: 2, name: 'Bob', role: 'User' }\n * ]\n * validateExportData(users) // ✅ true\n * await exportData(users, 'users.csv')\n * ```\n *\n * @example\n * ```typescript\n * // Valid: Array of arrays (matrix/tabular data)\n * const matrix = [\n * ['Name', 'Age', 'City'],\n * ['Alice', 25, 'Madrid'],\n * ['Bob', 30, 'Barcelona']\n * ]\n * validateExportData(matrix) // ✅ true\n * await exportData(matrix, 'data.csv')\n * ```\n *\n * @example\n * ```typescript\n * // Invalid: Mixed types - throws ValidationError\n * const mixed = [\n * { id: 1, name: 'Alice' },\n * ['Bob', 30] // ❌ Array mixed with object\n * ]\n * try {\n * validateExportData(mixed)\n * } catch (error) {\n * console.error(error.message)\n * // 'All elements must be objects if first one is an object'\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Invalid: Inconsistent array lengths - throws ValidationError\n * const uneven = [\n * ['Alice', 25, 'Madrid'],\n * ['Bob', 30] // ❌ Missing city\n * ]\n * try {\n * validateExportData(uneven)\n * } catch (error) {\n * console.error(error.message)\n * // 'Row 1 has 2 columns, but first has 3'\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Pre-export validation with error handling\n * async function safeExportUsers(users: any[], filename: string) {\n * try {\n * // Validate before export\n * validateExportData(users)\n * await exportData(users, filename)\n * console.log(`✅ Exported ${users.length} users to ${filename}`)\n * } catch (error) {\n * console.error('Export failed:', error.message)\n * // Log validation errors for debugging\n * if (error.code === 'VALIDATION_ERROR') {\n * console.error('Invalid data structure:', error.details)\n * }\n * }\n * }\n * ```\n *\n * @throws {ValidationError} If data is not an array\n * @throws {ValidationError} If array is empty\n * @throws {ValidationError} If elements have inconsistent types\n * @throws {ValidationError} If array rows have different lengths\n *\n * @see {@link exportData} for universal export using validated data\n * @see {@link ExportData} for type definition\n */\nexport function validateExportData(data: any): data is ExportData {\n if (!Array.isArray(data)) {\n throw createValidationError('Data must be an array', 'data', typeof data)\n }\n\n if (data.length === 0) {\n throw createValidationError('Data array cannot be empty', 'data.length', data.length)\n }\n\n const firstItem = data[0]\n const isObjectArray =\n typeof firstItem === 'object' && firstItem !== null && !Array.isArray(firstItem)\n const isArrayOfArrays = Array.isArray(firstItem)\n\n if (!isObjectArray && !isArrayOfArrays) {\n throw createValidationError(\n 'Data must be an array of objects or an array of arrays',\n 'data[0]',\n typeof firstItem\n )\n }\n\n // Validate consistency according to first element type\n if (isObjectArray) {\n // If first element is object, all must be objects\n for (let i = 1; i < data.length; i++) {\n const item = data[i]\n if (typeof item !== 'object' || item === null || Array.isArray(item)) {\n throw createValidationError(\n 'All elements must be objects if first one is an object',\n `data[${i}]`,\n typeof item\n )\n }\n }\n } else if (isArrayOfArrays) {\n // If first element is array, all must be arrays with equal length\n const firstRowLength = firstItem.length\n for (let i = 1; i < data.length; i++) {\n if (!Array.isArray(data[i])) {\n throw createValidationError(`Row ${i} is not an array`, `data[${i}]`, typeof data[i])\n }\n if (data[i].length !== firstRowLength) {\n throw createValidationError(\n `Row ${i} has ${data[i].length} columns, but first has ${firstRowLength}`,\n `data[${i}].length`,\n { expected: firstRowLength, actual: data[i].length }\n )\n }\n }\n }\n\n return true\n}\n\n/**\n * Alias for validateExportData for CSV backward compatibility\n */\nexport const validateCSVData = validateExportData\n\n// =============================================================================\n// FORMAT DETECTION\n// =============================================================================\n\n/**\n * Detects file format based on filename extension\n *\n * Analyzes filename to determine export format for automatic format selection.\n * Supports CSV, JSON, Tree, and TXT formats. Case-insensitive.\n *\n * @param filename - Filename with extension\n * @returns Detected format ('csv' | 'json' | 'tree' | 'txt')\n *\n * @example\n * ```typescript\n * // CSV detection\n * detectFormatFromFilename('users.csv') // 'csv'\n * detectFormatFromFilename('DATA.CSV') // 'csv' (case-insensitive)\n * ```\n *\n * @example\n * ```typescript\n * // JSON detection\n * detectFormatFromFilename('config.json') // 'json'\n * detectFormatFromFilename('data.JSON') // 'json'\n * ```\n *\n * @example\n * ```typescript\n * // Tree structure detection\n * detectFormatFromFilename('structure.tree') // 'tree'\n * ```\n *\n * @example\n * ```typescript\n * // Plain text detection\n * detectFormatFromFilename('notes.txt') // 'txt'\n * detectFormatFromFilename('log.TXT') // 'txt'\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Dynamic export routing\n * async function smartExport(data: any[], filename: string) {\n * const format = detectFormatFromFilename(filename)\n * console.log(`Exporting as ${format.toUpperCase()}...`)\n *\n * await exportData(data, filename)\n * console.log(`✅ Export completed: ${filename}`)\n * }\n *\n * smartExport(users, 'users.csv') // Exports as CSV\n * smartExport(config, 'config.json') // Exports as JSON\n * ```\n *\n * @throws {DataError} If filename has no extension\n * @throws {DataError} If extension is not supported (.csv, .json, .tree, .txt)\n *\n * @see {@link detectFileExtension} for universal extension detection (any file type)\n * @see {@link detectUniversalFormat} for detailed format metadata\n * @see {@link exportData} for universal export using detected format\n *\n * @deprecated Use detectFileExtension for universal detection, or use exportData directly (auto-detects)\n */\nexport function detectFormatFromFilename(filename: string): ExportFormat {\n const parts = filename.toLowerCase().split('.')\n const extension = parts.length > 1 ? parts.pop() : undefined\n\n switch (extension) {\n case 'csv':\n return 'csv'\n case 'json':\n return 'json'\n case 'tree':\n return 'tree'\n case 'txt':\n return 'txt'\n default:\n if (!extension) {\n throw new DataError(\n 'Could not detect file format. Must have a valid extension (.csv, .json, .tree, .txt)',\n TsHelpersErrorCode.UNSUPPORTED_FORMAT,\n { data: { filename } }\n )\n }\n throw new DataError(\n `Unsupported file format: .${extension}. Use .csv, .json, .tree or .txt`,\n TsHelpersErrorCode.UNSUPPORTED_FORMAT,\n { data: { filename, extension } }\n )\n }\n}\n\n/**\n * Universal file extension detector - detects any file extension\n *\n * Extracts file extension from filename (without dot) for any file type.\n * Returns last extension for compound extensions (e.g., '.tar.gz' → 'gz').\n * Case-insensitive, trims whitespace, returns null if no extension found.\n *\n * @param filename - Filename with extension\n * @returns File extension (lowercase, without dot) or `null` if no extension\n *\n * @example\n * ```typescript\n * // Common file types\n * detectFileExtension('document.pdf') // 'pdf'\n * detectFileExtension('image.jpg') // 'jpg'\n * detectFileExtension('data.json') // 'json'\n * detectFileExtension('styles.css') // 'css'\n * ```\n *\n * @example\n * ```typescript\n * // Compound extensions (returns last extension)\n * detectFileExtension('archive.tar.gz') // 'gz'\n * detectFileExtension('backup.sql.bz2') // 'bz2'\n * detectFileExtension('file.test.ts') // 'ts'\n * ```\n *\n * @example\n * ```typescript\n * // Edge cases\n * detectFileExtension('noextension') // null\n * detectFileExtension('.hidden') // 'hidden' (Unix hidden files)\n * detectFileExtension('file.') // '' (empty string)\n * detectFileExtension(' file.txt ') // 'txt' (trims whitespace)\n * detectFileExtension('FILE.PDF') // 'pdf' (case-insensitive)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Conditional file processing\n * async function processUploadedFile(filename: string, data: any) {\n * const ext = detectFileExtension(filename)\n *\n * switch (ext) {\n * case 'csv':\n * return await importCSV(filename)\n * case 'json':\n * return await importJSON(filename)\n * case 'pdf':\n * return await extractPdfText(filename)\n * case null:\n * throw new Error('File must have an extension')\n * default:\n * throw new Error(`Unsupported file type: .${ext}`)\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: File type validation\n * function validateUpload(file: File) {\n * const ext = detectFileExtension(file.name)\n * const allowedExtensions = ['jpg', 'jpeg', 'png', 'gif']\n *\n * if (!ext) {\n * return { valid: false, error: 'File must have an extension' }\n * }\n *\n * if (!allowedExtensions.includes(ext)) {\n * return {\n * valid: false,\n * error: `Invalid file type .${ext}. Allowed: ${allowedExtensions.join(', ')}`\n * }\n * }\n *\n * return { valid: true }\n * }\n * ```\n *\n * @see {@link detectUniversalFormat} for detailed format metadata (category, MIME type)\n * @see {@link detectFormatFromFilename} for export format detection (csv/json/tree/txt only)\n */\nexport function detectFileExtension(filename: string): FileFormat | null {\n if (!filename || typeof filename !== 'string') {\n return null\n }\n\n const parts = filename.trim().split('.')\n\n if (parts.length < 2) {\n return null\n }\n\n const extension = parts.pop()?.toLowerCase()\n return extension || null\n}\n\n/**\n * Universal filename format detection with comprehensive extension support\n *\n * Analyzes filename extension and returns detailed format metadata including:\n * category, MIME type, text/binary classification. Supports 80+ file formats\n * across data, code, documents, images, audio, video, archives, and fonts.\n *\n * @param filename - Filename with extension\n * @returns Object with extension, category, isText, isBinary, mimeType properties\n *\n * @example\n * ```typescript\n * // Data formats\n * detectUniversalFormat('config.json')\n * // { extension: 'json', category: 'data', isText: true, isBinary: false, mimeType: 'application/json' }\n *\n * detectUniversalFormat('users.csv')\n * // { extension: 'csv', category: 'data', isText: true, isBinary: false, mimeType: 'text/csv' }\n * ```\n *\n * @example\n * ```typescript\n * // Programming languages\n * detectUniversalFormat('app.ts')\n * // { extension: 'ts', category: 'code', isText: true, isBinary: false, mimeType: 'application/typescript' }\n *\n * detectUniversalFormat('main.py')\n * // { extension: 'py', category: 'code', isText: true, isBinary: false, mimeType: 'text/x-python' }\n * ```\n *\n * @example\n * ```typescript\n * // Documents\n * detectUniversalFormat('report.pdf')\n * // { extension: 'pdf', category: 'document', isText: false, isBinary: true, mimeType: 'application/pdf' }\n *\n * detectUniversalFormat('doc.docx')\n * // { extension: 'docx', category: 'document', isText: false, isBinary: true, mimeType: '...' }\n * ```\n *\n * @example\n * ```typescript\n * // Images\n * detectUniversalFormat('photo.jpg')\n * // { extension: 'jpg', category: 'image', isText: false, isBinary: true, mimeType: 'image/jpeg' }\n *\n * detectUniversalFormat('icon.svg')\n * // { extension: 'svg', category: 'image', isText: true, isBinary: false, mimeType: 'image/svg+xml' }\n * ```\n *\n * @example\n * ```typescript\n * // Archives\n * detectUniversalFormat('backup.zip')\n * // { extension: 'zip', category: 'archive', isText: false, isBinary: true, mimeType: 'application/zip' }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Smart file processing routing\n * async function processFile(filename: string, content: any) {\n * const format = detectUniversalFormat(filename)\n *\n * console.log(`Processing ${format.extension} file (${format.category})`)\n *\n * if (format.isText) {\n * // Can read as text\n * const text = await readFileAsText(filename)\n * return parseTextFormat(text, format.extension)\n * } else if (format.isBinary) {\n * // Needs binary processing\n * return processBinaryFile(filename, format.mimeType)\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Content-Type header generation\n * function getResponseHeaders(filename: string) {\n * const format = detectUniversalFormat(filename)\n *\n * return {\n * 'Content-Type': format.mimeType || 'application/octet-stream',\n * 'Content-Disposition': `attachment; filename=\"${filename}\"`,\n * 'X-File-Category': format.category\n * }\n * }\n * ```\n *\n * @see {@link detectFileExtension} for simple extension extraction\n * @see {@link detectFormatFromFilename} for export format detection\n */\nexport function detectUniversalFormat(filename: string) {\n const extension = detectFileExtension(filename)\n\n if (!extension) {\n return {\n extension: null,\n category: 'unknown',\n isText: false,\n isBinary: false,\n mimeType: null,\n }\n }\n\n // Comprehensive file format database with 80+ common formats\n const formatInfo = {\n // Data formats\n json: { category: 'data', isText: true, isBinary: false, mimeType: 'application/json' },\n csv: { category: 'data', isText: true, isBinary: false, mimeType: 'text/csv' },\n xml: { category: 'data', isText: true, isBinary: false, mimeType: 'application/xml' },\n yaml: { category: 'data', isText: true, isBinary: false, mimeType: 'application/yaml' },\n yml: { category: 'data', isText: true, isBinary: false, mimeType: 'application/yaml' },\n tree: { category: 'data', isText: true, isBinary: false, mimeType: 'text/plain' },\n tsv: { category: 'data', isText: true, isBinary: false, mimeType: 'text/tab-separated-values' },\n sql: { category: 'data', isText: true, isBinary: false, mimeType: 'application/sql' },\n db: { category: 'data', isText: false, isBinary: true, mimeType: 'application/x-sqlite3' },\n sqlite: { category: 'data', isText: false, isBinary: true, mimeType: 'application/x-sqlite3' },\n\n // Text and code formats\n txt: { category: 'text', isText: true, isBinary: false, mimeType: 'text/plain' },\n md: { category: 'text', isText: true, isBinary: false, mimeType: 'text/markdown' },\n rst: { category: 'text', isText: true, isBinary: false, mimeType: 'text/x-rst' },\n rtf: { category: 'text', isText: true, isBinary: false, mimeType: 'application/rtf' },\n log: { category: 'text', isText: true, isBinary: false, mimeType: 'text/plain' },\n\n // Web formats\n html: { category: 'web', isText: true, isBinary: false, mimeType: 'text/html' },\n htm: { category: 'web', isText: true, isBinary: false, mimeType: 'text/html' },\n css: { category: 'web', isText: true, isBinary: false, mimeType: 'text/css' },\n scss: { category: 'web', isText: true, isBinary: false, mimeType: 'text/x-scss' },\n sass: { category: 'web', isText: true, isBinary: false, mimeType: 'text/x-sass' },\n less: { category: 'web', isText: true, isBinary: false, mimeType: 'text/x-less' },\n\n // Programming languages\n js: { category: 'code', isText: true, isBinary: false, mimeType: 'application/javascript' },\n mjs: { category: 'code', isText: true, isBinary: false, mimeType: 'application/javascript' },\n jsx: { category: 'code', isText: true, isBinary: false, mimeType: 'text/jsx' },\n ts: { category: 'code', isText: true, isBinary: false, mimeType: 'application/typescript' },\n tsx: { category: 'code', isText: true, isBinary: false, mimeType: 'text/tsx' },\n py: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-python' },\n java: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-java-source' },\n php: { category: 'code', isText: true, isBinary: false, mimeType: 'application/x-httpd-php' },\n rb: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-ruby' },\n go: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-go' },\n rs: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-rust' },\n c: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-c' },\n cpp: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-c++' },\n cc: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-c++' },\n h: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-c' },\n cs: { category: 'code', isText: true, isBinary: false, mimeType: 'text/x-csharp' },\n sh: { category: 'code', isText: true, isBinary: false, mimeType: 'application/x-sh' },\n bash: { category: 'code', isText: true, isBinary: false, mimeType: 'application/x-sh' },\n ps1: { category: 'code', isText: true, isBinary: false, mimeType: 'application/x-powershell' },\n\n // Microsoft Office documents\n doc: { category: 'document', isText: false, isBinary: true, mimeType: 'application/msword' },\n docx: {\n category: 'document',\n isText: false,\n isBinary: true,\n mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n },\n xls: {\n category: 'spreadsheet',\n isText: false,\n isBinary: true,\n mimeType: 'application/vnd.ms-excel',\n },\n xlsx: {\n category: 'spreadsheet',\n isText: false,\n isBinary: true,\n mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n },\n ppt: {\n category: 'presentation',\n isText: false,\n isBinary: true,\n mimeType: 'application/vnd.ms-powerpoint',\n },\n pptx: {\n category: 'presentation',\n isText: false,\n isBinary: true,\n mimeType: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n },\n\n // LibreOffice/OpenOffice\n odt: {\n category: 'document',\n isText: false,\n isBinary: true,\n mimeType: 'application/vnd.oasis.opendocument.text',\n },\n ods: {\n category: 'spreadsheet',\n isText: false,\n isBinary: true,\n mimeType: 'application/vnd.oasis.opendocument.spreadsheet',\n },\n odp: {\n category: 'presentation',\n isText: false,\n isBinary: true,\n mimeType: 'application/vnd.oasis.opendocument.presentation',\n },\n\n // Other document formats\n pdf: { category: 'document', isText: false, isBinary: true, mimeType: 'application/pdf' },\n epub: { category: 'document', isText: false, isBinary: true, mimeType: 'application/epub+zip' },\n mobi: {\n category: 'document',\n isText: false,\n isBinary: true,\n mimeType: 'application/x-mobipocket-ebook',\n },\n\n // Image formats\n jpg: { category: 'image', isText: false, isBinary: true, mimeType: 'image/jpeg' },\n jpeg: { category: 'image', isText: false, isBinary: true, mimeType: 'image/jpeg' },\n png: { category: 'image', isText: false, isBinary: true, mimeType: 'image/png' },\n gif: { category: 'image', isText: false, isBinary: true, mimeType: 'image/gif' },\n svg: { category: 'image', isText: true, isBinary: false, mimeType: 'image/svg+xml' },\n webp: { category: 'image', isText: false, isBinary: true, mimeType: 'image/webp' },\n avif: { category: 'image', isText: false, isBinary: true, mimeType: 'image/avif' },\n bmp: { category: 'image', isText: false, isBinary: true, mimeType: 'image/bmp' },\n tiff: { category: 'image', isText: false, isBinary: true, mimeType: 'image/tiff' },\n tif: { category: 'image', isText: false, isBinary: true, mimeType: 'image/tiff' },\n ico: { category: 'image', isText: false, isBinary: true, mimeType: 'image/x-icon' },\n psd: {\n category: 'image',\n isText: false,\n isBinary: true,\n mimeType: 'image/vnd.adobe.photoshop',\n },\n\n // Audio formats\n mp3: { category: 'audio', isText: false, isBinary: true, mimeType: 'audio/mpeg' },\n wav: { category: 'audio', isText: false, isBinary: true, mimeType: 'audio/wav' },\n flac: { category: 'audio', isText: false, isBinary: true, mimeType: 'audio/flac' },\n ogg: { category: 'audio', isText: false, isBinary: true, mimeType: 'audio/ogg' },\n aac: { category: 'audio', isText: false, isBinary: true, mimeType: 'audio/aac' },\n m4a: { category: 'audio', isText: false, isBinary: true, mimeType: 'audio/m4a' },\n wma: { category: 'audio', isText: false, isBinary: true, mimeType: 'audio/x-ms-wma' },\n\n // Video formats\n mp4: { category: 'video', isText: false, isBinary: true, mimeType: 'video/mp4' },\n avi: { category: 'video', isText: false, isBinary: true, mimeType: 'video/x-msvideo' },\n mov: { category: 'video', isText: false, isBinary: true, mimeType: 'video/quicktime' },\n wmv: { category: 'video', isText: false, isBinary: true, mimeType: 'video/x-ms-wmv' },\n flv: { category: 'video', isText: false, isBinary: true, mimeType: 'video/x-flv' },\n webm: { category: 'video', isText: false, isBinary: true, mimeType: 'video/webm' },\n mkv: { category: 'video', isText: false, isBinary: true, mimeType: 'video/x-matroska' },\n '3gp': { category: 'video', isText: false, isBinary: true, mimeType: 'video/3gpp' },\n\n // Archive formats\n zip: { category: 'archive', isText: false, isBinary: true, mimeType: 'application/zip' },\n rar: {\n category: 'archive',\n isText: false,\n isBinary: true,\n mimeType: 'application/x-rar-compressed',\n },\n '7z': {\n category: 'archive',\n isText: false,\n isBinary: true,\n mimeType: 'application/x-7z-compressed',\n },\n tar: { category: 'archive', isText: false, isBinary: true, mimeType: 'application/x-tar' },\n gz: { category: 'archive', isText: false, isBinary: true, mimeType: 'application/gzip' },\n bz2: { category: 'archive', isText: false, isBinary: true, mimeType: 'application/x-bzip2' },\n xz: { category: 'archive', isText: false, isBinary: true, mimeType: 'application/x-xz' },\n\n // Configuration files\n ini: { category: 'config', isText: true, isBinary: false, mimeType: 'text/plain' },\n cfg: { category: 'config', isText: true, isBinary: false, mimeType: 'text/plain' },\n conf: { category: 'config', isText: true, isBinary: false, mimeType: 'text/plain' },\n toml: { category: 'config', isText: true, isBinary: false, mimeType: 'application/toml' },\n env: { category: 'config', isText: true, isBinary: false, mimeType: 'text/plain' },\n\n // Font formats\n ttf: { category: 'font', isText: false, isBinary: true, mimeType: 'font/ttf' },\n otf: { category: 'font', isText: false, isBinary: true, mimeType: 'font/otf' },\n woff: { category: 'font', isText: false, isBinary: true, mimeType: 'font/woff' },\n woff2: { category: 'font', isText: false, isBinary: true, mimeType: 'font/woff2' },\n eot: {\n category: 'font',\n isText: false,\n isBinary: true,\n mimeType: 'application/vnd.ms-fontobject',\n },\n } as const\n\n const info = formatInfo[extension as keyof typeof formatInfo] || {\n category: 'unknown',\n isText: false,\n isBinary: false,\n mimeType: null,\n }\n\n return {\n extension,\n ...info,\n }\n}\n\n// =============================================================================\n// TREE EXPORT OPTIONS\n// =============================================================================\n\nexport interface TreeExportOptions {\n /** Property to display as node name (default: 'name') */\n labelField?: string\n /** Characters for vertical line (default: '│ ') */\n verticalLine?: string\n /** Characters for middle branch (default: '├── ') */\n middleBranch?: string\n /** Characters for last branch (default: '└── ') */\n lastBranch?: string\n /** Spacing for nodes without vertical line (default: ' ') */\n emptySpace?: string\n /** Custom function to get node label */\n labelFunction?: (node: any) => string\n}\n\n/**\n * Exports a tree structure as a text file with visual format\n *\n * Converts hierarchical data (nodes with `children` arrays) into ASCII/Unicode tree\n * visualization and saves as .tree file. Supports Node.js (filesystem) and Browser (download).\n *\n * @param data - Array of root nodes (each with optional `children` property)\n * @param filePath - Output file path (Node.js) or filename (Browser)\n * @param options - Tree rendering options (labelField, box-drawing characters, labelFunction)\n *\n * @example\n * ```typescript\n * // Basic tree export - File structure\n * const fileTree = [\n * {\n * name: 'src',\n * children: [\n * { name: 'index.ts' },\n * { name: 'utils.ts' },\n * {\n * name: 'components',\n * children: [\n * { name: 'Button.tsx' },\n * { name: 'Input.tsx' }\n * ]\n * }\n * ]\n * }\n * ]\n *\n * await exportTree(fileTree, 'structure.tree')\n * // Creates file:\n * // └── src\n * // ├── index.ts\n * // ├── utils.ts\n * // └── components\n * // ├── Button.tsx\n * // └── Input.tsx\n * ```\n *\n * @example\n * ```typescript\n * // Custom label field - Organization chart\n * const orgChart = [\n * {\n * title: 'CEO',\n * children: [\n * {\n * title: 'CTO',\n * children: [\n * { title: 'Dev Lead' },\n * { title: 'QA Lead' }\n * ]\n * },\n * { title: 'CFO' }\n * ]\n * }\n * ]\n *\n * await exportTree(orgChart, 'org-chart.tree', { labelField: 'title' })\n * ```\n *\n * @example\n * ```typescript\n * // ASCII characters - Better terminal compatibility\n * await exportTree(data, 'structure.tree', {\n * verticalLine: '| ',\n * middleBranch: '+-- ',\n * lastBranch: '`-- ',\n * emptySpace: ' '\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Custom label function - Rich formatting\n * const tasks = [\n * {\n * name: 'Backend',\n * status: 'in-progress',\n * assignee: 'Alice',\n * children: [\n * { name: 'API', status: 'done', assignee: 'Bob' },\n * { name: 'Database', status: 'pending', assignee: 'Charlie' }\n * ]\n * }\n * ]\n *\n * await exportTree(tasks, 'tasks.tree', {\n * labelFunction: (node) => `[${node.status}] ${node.name} (@${node.assignee})`\n * })\n * // Output:\n * // └── [in-progress] Backend (@Alice)\n * // ├── [done] API (@Bob)\n * // └── [pending] Database (@Charlie)\n * ```\n *\n * @throws {ValidationError} If data is not an array of nodes\n *\n * @see {@link importTree} for importing .tree files as text\n * @see {@link renderTreeAsText} for tree rendering without file export\n * @see {@link TreeExportOptions} for configuration options\n */\nexport async function exportTree(\n data: any[],\n filePath: string,\n options?: TreeExportOptions\n): Promise<void> {\n if (!Array.isArray(data)) {\n throw createValidationError(\n 'Data for tree format must be an array of nodes',\n 'data',\n typeof data\n )\n }\n\n // Import render function from specialized/tree\n const { renderTreeAsText } = await import('./tree')\n\n // Render tree as text\n const treeText = renderTreeAsText(data, options)\n\n // Write file according to environment\n if (isNode()) {\n // Node.js - write file to system\n const fs = await import('fs/promises')\n await fs.writeFile(filePath, treeText, { encoding: 'utf-8' })\n } else {\n // Browser - download as file\n const blob = new Blob([treeText], { type: 'text/plain;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n\n const link = document.createElement('a')\n link.href = url\n link.download = filePath.split('/').pop() || 'tree.tree'\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n\n URL.revokeObjectURL(url)\n }\n}\n\n/**\n * Imports a .tree file as plain text (Node.js only)\n *\n * Reads tree structure visualization files created with exportTree() as plain text.\n * Returns the ASCII/Unicode tree diagram as string. Only supported in Node.js environment.\n *\n * @param filePath - Path to .tree file (Node.js only)\n * @param _options - Reserved for future use\n * @returns Promise<string> Tree structure as text\n *\n * @example\n * ```typescript\n * // Import tree structure\n * const treeText = await importTree('./structure.tree')\n * console.log(treeText)\n * // Output:\n * // └── src\n * // ├── index.ts\n * // ├── utils.ts\n * // └── components\n * // ├── Button.tsx\n * // └── Input.tsx\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Display tree structure in terminal\n * async function showProjectStructure(filePath: string) {\n * try {\n * const structure = await importTree(filePath)\n * console.log('📁 Project Structure:')\n * console.log(structure)\n * } catch (error) {\n * console.error('Failed to load structure:', error.message)\n * }\n * }\n * ```\n *\n * @throws {DataError} If called in Browser environment (use readFileAsText with File object instead)\n *\n * @see {@link exportTree} for creating .tree files\n * @see {@link readFileAsText} for browser-compatible file reading\n */\nexport async function importTree(filePath: string, _options?: any): Promise<string> {\n if (isNode()) {\n // Node.js - read file from system\n const fs = await import('fs/promises')\n return fs.readFile(filePath, { encoding: 'utf-8' })\n } else {\n // Browser - cannot read files from system directly\n throw new DataError(\n '.tree file import is not supported in browser. Use readFileAsText() with a File object.',\n TsHelpersErrorCode.ENVIRONMENT_NOT_SUPPORTED,\n { data: { environment: 'browser', operation: 'importTree' } }\n )\n }\n}\n\n// =============================================================================\n// TEXT EXPORT OPTIONS\n// =============================================================================\n\nexport interface TxtExportOptions {\n /** Separator between array elements (default: '\\n') */\n separator?: string\n /** Custom function to convert objects to string */\n stringify?: (obj: any) => string\n /** Indentation for JSON objects (default: 2) */\n indent?: number\n}\n\n/**\n * Exports any type of data as plain text file\n *\n * Universal text file exporter supporting strings, arrays, objects, and primitives.\n * Automatically formats data based on type. Supports Node.js (filesystem) and Browser (download).\n *\n * Features:\n * - **Strings**: Direct output\n * - **Arrays**: One element per line (or custom separator)\n * - **Objects**: Formatted JSON with indentation\n * - **Primitives**: String conversion\n * - **Custom stringify**: Optional transform function\n *\n * @param data - Data to export (string, array, object, or primitive)\n * @param filePath - Output file path (Node.js) or filename (Browser)\n * @param options - Export options (separator, stringify function, indent)\n *\n * @example\n * ```typescript\n * // Export string - Direct output\n * await exportTxt('Hello, World!', 'message.txt')\n * // Creates: Hello, World!\n * ```\n *\n * @example\n * ```typescript\n * // Export array - One per line\n * const logs = [\n * '[INFO] Server started',\n * '[WARN] High memory usage',\n * '[ERROR] Connection failed'\n * ]\n * await exportTxt(logs, 'server.log')\n * // Creates:\n * // [INFO] Server started\n * // [WARN] High memory usage\n * // [ERROR] Connection failed\n * ```\n *\n * @example\n * ```typescript\n * // Export object - Formatted JSON\n * const config = {\n * server: { host: 'localhost', port: 3000 },\n * database: { url: 'mongodb://localhost' }\n * }\n * await exportTxt(config, 'config.txt', { indent: 2 })\n * // Creates formatted JSON\n * ```\n *\n * @example\n * ```typescript\n * // Custom separator - Comma-separated list\n * const tags = ['typescript', 'nodejs', 'express', 'mongodb']\n * await exportTxt(tags, 'tags.txt', { separator: ', ' })\n * // Creates: typescript, nodejs, express, mongodb\n * ```\n *\n * @example\n * ```typescript\n * // Custom stringify function - Markdown list\n * const tasks = [\n * { id: 1, title: 'Setup project', done: true },\n * { id: 2, title: 'Write tests', done: false }\n * ]\n * await exportTxt(tasks, 'tasks.txt', {\n * stringify: (tasks) =>\n * tasks.map(t => `- [${t.done ? 'x' : ' '}] ${t.title}`).join('\\n')\n * })\n * // Creates:\n * // - [x] Setup project\n * // - [ ] Write tests\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Export error log with timestamps\n * async function exportErrorLog(errors: Error[]) {\n * const timestamp = new Date().toISOString()\n * const filename = `errors-${timestamp}.txt`\n *\n * await exportTxt(errors, filename, {\n * stringify: (errors) =>\n * errors.map(e => `[${new Date().toISOString()}] ${e.message}\\n${e.stack}`).join('\\n\\n')\n * })\n *\n * console.log(`✅ Exported ${errors.length} errors to ${filename}`)\n * }\n * ```\n *\n * @see {@link importTxt} for importing text files with security validations\n * @see {@link TxtExportOptions} for configuration options\n */\nexport async function exportTxt(\n data: any,\n filePath: string,\n options?: TxtExportOptions\n): Promise<void> {\n const { separator = '\\n', stringify, indent = 2 } = options || {}\n\n let textContent: string\n\n if (stringify) {\n // Use custom function\n textContent = stringify(data)\n } else if (typeof data === 'string') {\n // Already string\n textContent = data\n } else if (Array.isArray(data)) {\n // Array: each element on a line (or custom separator)\n textContent = data\n .map(item => (typeof item === 'string' ? item : JSON.stringify(item, null, indent)))\n .join(separator)\n } else if (typeof data === 'object' && data !== null) {\n // Object: formatted JSON\n textContent = JSON.stringify(data, null, indent)\n } else {\n // Primitives: convert to string\n textContent = String(data)\n }\n\n // Write file according to environment\n if (isNode()) {\n // Node.js - write file to system\n const fs = await import('fs/promises')\n await fs.writeFile(filePath, textContent, { encoding: 'utf-8' })\n } else {\n // Browser - download as file\n const blob = new Blob([textContent], { type: 'text/plain;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n\n const link = document.createElement('a')\n link.href = url\n link.download = filePath.split('/').pop() || 'file.txt'\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n\n URL.revokeObjectURL(url)\n }\n}\n\nexport interface TxtImportOptions {\n /** Maximum file size in bytes (default: 10MB) */\n maxFileSize?: number\n /** Maximum content length in characters (default: 1M characters) */\n maxLength?: number\n /** Whether to validate content for security (default: true) */\n validateSecurity?: boolean\n /** Whether to sanitize content (default: true) */\n sanitize?: boolean\n}\n\n/**\n * Sanitizes text content removing problematic characters\n */\nfunction sanitizeTextContent(content: string): string {\n return (\n content\n // Normalize different types of line breaks\n .replace(/\\r\\n|\\r/g, '\\n')\n // Remove dangerous control characters (keep \\n, \\t)\n // eslint-disable-next-line no-control-regex\n .replace(/[\\x00-\\x08\\x0E-\\x1F\\x7F]/g, '')\n // Limit multiple consecutive line breaks\n .replace(/\\n{4,}/g, '\\n\\n\\n')\n // Remove trailing spaces from lines\n .replace(/[ \\t]+$/gm, '')\n )\n}\n\n/**\n * Imports a .txt file as plain text with security validations (Node.js only)\n *\n * Reads text files with comprehensive security checks: file size limits, content length validation,\n * path traversal prevention, and dangerous character sanitization. Only supported in Node.js.\n *\n * Security features:\n * - **File size limit**: Default 10MB (configurable)\n * - **Content length limit**: Default 1M characters (configurable)\n * - **Path validation**: Prevents directory traversal attacks\n * - **Content sanitization**: Removes dangerous control characters\n * - **Line break normalization**: Standardizes to \\n\n *\n * @param filePath - Path to .txt file (Node.js only)\n * @param options - Security and sanitization options\n *\n * @example\n * ```typescript\n * // Basic import - Default security settings\n * const content = await importTxt('./notes.txt')\n * console.log(content)\n * ```\n *\n * @example\n * ```typescript\n * // Custom size limits - Large file support\n * const largeFile = await importTxt('./large-log.txt', {\n * maxFileSize: 50 * 1024 * 1024, // 50MB\n * maxLength: 10_000_000 // 10M characters\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Disable sanitization - Raw content\n * const rawContent = await importTxt('./data.txt', {\n * sanitize: false,\n * validateSecurity: false\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Safe log file reader with error handling\n * async function readServerLog(logPath: string) {\n * try {\n * const log = await importTxt(logPath, {\n * maxFileSize: 100 * 1024 * 1024, // 100MB logs\n * maxLength: 50_000_000, // 50M chars\n * sanitize: true,\n * validateSecurity: true\n * })\n *\n * const lines = log.split('\\n')\n * const errors = lines.filter(line => line.includes('[ERROR]'))\n *\n * console.log(`📊 Log stats:`)\n * console.log(` Total lines: ${lines.length}`)\n * console.log(` Errors: ${errors.length}`)\n *\n * return { lines, errors }\n * } catch (error) {\n * if (error.message.includes('too large')) {\n * console.error('Log file exceeds size limit')\n * } else if (error.message.includes('not found')) {\n * console.error('Log file does not exist')\n * } else {\n * console.error('Failed to read log:', error.message)\n * }\n * throw error\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Secure user file upload processing\n * async function processUserUpload(uploadedFilePath: string) {\n * // Enforce strict limits for user-uploaded files\n * const content = await importTxt(uploadedFilePath, {\n * maxFileSize: 5 * 1024 * 1024, // 5MB max\n * maxLength: 1_000_000, // 1M chars max\n * validateSecurity: true, // Check for dangerous content\n * sanitize: true // Remove control chars\n * })\n *\n * // Process sanitized content safely\n * return analyzeText(content)\n * }\n * ```\n *\n * @throws {Error} If file exceeds maxFileSize\n * @throws {Error} If content exceeds maxLength\n * @throws {Error} If file path is invalid or unsafe\n * @throws {Error} If file not found\n * @throws {Error} If called in Browser environment\n *\n * @see {@link exportTxt} for creating text files\n * @see {@link TxtImportOptions} for configuration options\n * @see {@link readFileAsText} for browser-compatible file reading\n */\nexport async function importTxt(filePath: string, options: TxtImportOptions = {}): Promise<string> {\n const {\n maxFileSize = 10 * 1024 * 1024, // 10MB default\n maxLength = 1_000_000, // 1M characters default\n validateSecurity = true,\n sanitize = true,\n } = options\n\n if (isNode()) {\n // Node.js - read file from system with validations\n const fs = await import('fs/promises')\n const path = await import('path')\n\n // Validate file path for security using our validator\n const { isValidFilePath, isValidFileSize, isValidTextContent } = await import('./validators')\n\n if (!isValidFilePath(filePath)) {\n throw new Error('Invalid or unsafe file path')\n }\n\n const resolvedPath = path.resolve(filePath)\n\n // Check file size before reading\n try {\n const stats = await fs.stat(resolvedPath)\n\n if (!isValidFileSize(stats.size, maxFileSize)) {\n throw new Error(`File too large: ${stats.size} bytes (max: ${maxFileSize})`)\n }\n } catch (error) {\n if ((error as any).code === 'ENOENT') {\n throw new Error(`File not found: ${filePath}`)\n }\n throw error\n }\n\n // Read and validate content\n const content = await fs.readFile(resolvedPath, { encoding: 'utf-8' })\n\n // Validate content security using our validator\n if (validateSecurity && !isValidTextContent(content, { maxLength })) {\n throw new Error('File contains potentially dangerous content or exceeds security limits')\n }\n\n return sanitize ? sanitizeTextContent(content) : content\n } else {\n // Browser - cannot read files from system directly\n throw new Error(\n '.txt file import is not supported in browser. Use readFileAsText() with a File object.'\n )\n }\n}\n\n// =============================================================================\n// UNIVERSAL FUNCTIONS\n// =============================================================================\n\n/**\n * Exports data in specified format based on file extension (Universal dispatcher)\n *\n * Universal export function that automatically detects format from filename extension\n * and delegates to specialized exporters (CSV, JSON, Tree, TXT). Simplifies data export\n * with a single unified API for all formats.\n *\n * Automatic format detection:\n * - **.csv** → exportCSV (PapaParse, UTF-8 BOM, semicolon delimiter)\n * - **.json** → exportJSON (pretty-printed JSON)\n * - **.tree** → exportTree (ASCII/Unicode tree visualization)\n * - **.txt** → exportTxt (plain text with auto-formatting)\n *\n * Environment support:\n * - **Node.js**: Writes to filesystem\n * - **Browser**: Triggers download\n *\n * @param data - Data to export (structure depends on format)\n * @param filePath - Output file path with extension (determines format)\n * @param options - Format-specific options (passed to specialized exporter)\n *\n * @example\n * ```typescript\n * // CSV export - Array of objects\n * const users = [\n * { id: 1, name: 'Alice', email: 'alice@example.com' },\n * { id: 2, name: 'Bob', email: 'bob@example.com' }\n * ]\n * await exportData(users, 'users.csv')\n * // Auto-detects CSV format, exports with semicolon delimiter\n * ```\n *\n * @example\n * ```typescript\n * // JSON export - Any data structure\n * const config = {\n * server: { host: 'localhost', port: 3000 },\n * database: { url: 'mongodb://localhost' },\n * features: { auth: true, cache: false }\n * }\n * await exportData(config, 'config.json', { indent: 2 })\n * // Auto-detects JSON format, pretty-prints with 2-space indent\n * ```\n *\n * @example\n * ```typescript\n * // Tree export - Hierarchical structure\n * const fileTree = [\n * {\n * name: 'src',\n * children: [\n * { name: 'index.ts' },\n * { name: 'utils.ts' },\n * {\n * name: 'components',\n * children: [\n * { name: 'Button.tsx' },\n * { name: 'Input.tsx' }\n * ]\n * }\n * ]\n * }\n * ]\n * await exportData(fileTree, 'structure.tree')\n * // Auto-detects tree format, renders as ASCII tree\n * ```\n *\n * @example\n * ```typescript\n * // Text export - Logs/strings\n * const logs = [\n * '[2024-01-15 10:30:00] Server started',\n * '[2024-01-15 10:30:15] Connected to database',\n * '[2024-01-15 10:31:00] Ready to accept connections'\n * ]\n * await exportData(logs, 'server.log')\n * // Auto-detects txt format, one line per array element\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Multi-format export function\n * async function exportReport(data: any[], format: 'csv' | 'json' | 'txt') {\n * const timestamp = new Date().toISOString().split('T')[0]\n * const filename = `report-${timestamp}.${format}`\n *\n * try {\n * await exportData(data, filename)\n * console.log(`✅ Report exported: ${filename}`)\n * return { success: true, filename }\n * } catch (error) {\n * console.error(`❌ Export failed:`, error.message)\n * return { success: false, error: error.message }\n * }\n * }\n *\n * // Usage\n * exportReport(users, 'csv') // users-2024-01-15.csv\n * exportReport(stats, 'json') // report-2024-01-15.json\n * exportReport(logs, 'txt') // report-2024-01-15.txt\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: User-selected format export\n * async function exportWithUserChoice(data: any[], filename: string) {\n * // User provides filename with desired extension\n * // exportData automatically routes to correct exporter\n *\n * await exportData(data, filename)\n *\n * const format = detectFormatFromFilename(filename)\n * console.log(`Exported as ${format.toUpperCase()}`)\n * }\n *\n * // Works with any supported extension\n * exportWithUserChoice(data, 'data.csv')\n * exportWithUserChoice(data, 'data.json')\n * exportWithUserChoice(data, 'data.txt')\n * ```\n *\n * @throws {DataError} If file extension is not supported\n * @throws {ValidationError} If data structure is invalid for format (e.g., CSV requires array)\n *\n * @see {@link importData} for universal import\n * @see {@link exportCSV} for CSV-specific export\n * @see {@link exportJSON} for JSON-specific export\n * @see {@link exportTree} for tree-specific export\n * @see {@link exportTxt} for text-specific export\n */\nexport async function exportData(data: any, filePath: string, options?: any): Promise<void> {\n const format = detectFormatFromFilename(filePath)\n\n // Only validate as ExportData for formats that require it (CSV)\n if (format === 'csv') {\n validateExportData(data)\n }\n\n switch (format) {\n case 'csv': {\n const { exportCSV } = await import('./csv')\n return exportCSV(data, filePath, options)\n }\n case 'json': {\n const { exportJSON } = await import('./json')\n return exportJSON(data, filePath, options)\n }\n case 'tree': {\n return exportTree(data, filePath, options)\n }\n case 'txt': {\n return exportTxt(data, filePath, options)\n }\n default:\n throw new Error(`Unsupported format: ${format}`)\n }\n}\n\n/**\n * Imports data from specified file based on extension (Universal dispatcher)\n *\n * Universal import function that automatically detects format from filename extension\n * and delegates to specialized importers (CSV, JSON, Tree, TXT). Simplifies data import\n * with a single unified API for all formats. Node.js only.\n *\n * Automatic format detection:\n * - **.csv** → importCSV (PapaParse with header detection)\n * - **.json** → importJSON (native JSON.parse)\n * - **.tree** → importTree (plain text tree visualization)\n * - **.txt** → importTxt (plain text with security validations)\n *\n * @param filePath - Input file path with extension (determines format)\n * @param options - Format-specific options (passed to specialized importer)\n * @returns Promise<ImportData> Imported data (type depends on format)\n *\n * @example\n * ```typescript\n * // CSV import - Returns array of objects\n * const users = await importData('./users.csv')\n * // [\n * // { id: '1', name: 'Alice', email: 'alice@example.com' },\n * // { id: '2', name: 'Bob', email: 'bob@example.com' }\n * // ]\n * ```\n *\n * @example\n * ```typescript\n * // JSON import - Returns original data structure\n * const config = await importData('./config.json')\n * console.log(config.server.host) // 'localhost'\n * console.log(config.server.port) // 3000\n * ```\n *\n * @example\n * ```typescript\n * // Tree import - Returns plain text visualization\n * const treeText = await importData('./structure.tree')\n * console.log(treeText)\n * // └── src\n * // ├── index.ts\n * // ├── utils.ts\n * // └── components\n * ```\n *\n * @example\n * ```typescript\n * // Text import - Returns file content as string\n * const log = await importData('./server.log')\n * const lines = log.split('\\n')\n * const errors = lines.filter(line => line.includes('[ERROR]'))\n * console.log(`Found ${errors.length} errors`)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Dynamic file processor\n * async function processFile(filePath: string) {\n * const ext = detectFileExtension(filePath)\n * console.log(`Processing ${ext} file...`)\n *\n * const data = await importData(filePath)\n *\n * switch (ext) {\n * case 'csv':\n * return processCSVData(data as any[])\n * case 'json':\n * return processJSONData(data)\n * case 'txt':\n * return processTextData(data as string)\n * default:\n * throw new Error(`Unsupported format: ${ext}`)\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Batch file import with error handling\n * async function importAllFiles(directory: string) {\n * const files = await fs.readdir(directory)\n * const results = []\n *\n * for (const file of files) {\n * const filePath = path.join(directory, file)\n * const ext = detectFileExtension(file)\n *\n * // Skip unsupported formats\n * if (!['csv', 'json', 'txt', 'tree'].includes(ext || '')) {\n * console.log(`⏭️ Skipping unsupported file: ${file}`)\n * continue\n * }\n *\n * try {\n * const data = await importData(filePath)\n * results.push({ file, data, success: true })\n * console.log(`✅ Imported: ${file}`)\n * } catch (error) {\n * results.push({ file, error: error.message, success: false })\n * console.error(`❌ Failed: ${file} - ${error.message}`)\n * }\n * }\n *\n * return results\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Import with custom options per format\n * async function smartImport(filePath: string) {\n * const format = detectFormatFromFilename(filePath)\n *\n * let options: any = {}\n *\n * if (format === 'csv') {\n * options = { delimiter: ';', header: true }\n * } else if (format === 'json') {\n * options = { reviver: (key, value) => key === 'date' ? new Date(value) : value }\n * } else if (format === 'txt') {\n * options = { maxFileSize: 50 * 1024 * 1024, sanitize: true }\n * }\n *\n * return await importData(filePath, options)\n * }\n * ```\n *\n * @throws {DataError} If file extension is not supported\n * @throws {Error} If file not found or cannot be read\n * @throws {Error} If file format is invalid\n * @throws {DataError} If called in Browser environment (use readFileAsText with File object instead)\n *\n * @see {@link exportData} for universal export\n * @see {@link importCSV} for CSV-specific import\n * @see {@link importJSON} for JSON-specific import\n * @see {@link importTree} for tree-specific import\n * @see {@link importTxt} for text-specific import\n */\nexport async function importData(filePath: string, options?: any): Promise<ImportData> {\n const format = detectFormatFromFilename(filePath)\n\n switch (format) {\n case 'csv': {\n const { importCSV } = await import('./csv')\n return importCSV(filePath, options)\n }\n case 'json': {\n const { importJSON } = await import('./json')\n return importJSON(filePath, options)\n }\n case 'tree': {\n return importTree(filePath, options)\n }\n case 'txt': {\n return importTxt(filePath, options)\n }\n default:\n throw new Error(`Unsupported format: ${format}`)\n }\n}\n","/**\n * Error handling utilities with specific error types and codes\n * Provides structured error handling for validation, data processing, and environment issues\n */\n\n/**\n * Enumeration of specific error codes for different failure scenarios\n * @example\n * ```ts\n * // Using error codes for type-safe error handling\n * try {\n * validateNIF('invalid')\n * } catch (error) {\n * if (error.code === TsHelpersErrorCode.VALIDATION_FAILED) {\n * console.log('Validation error:', error.data)\n * }\n * }\n * ```\n */\nexport enum TsHelpersErrorCode {\n /** Validation of input data failed (NIF, email, etc.) */\n VALIDATION_FAILED = 'VALIDATION_FAILED',\n /** Error processing data (CSV, JSON, aggregations, etc.) */\n DATA_PROCESSING_FAILED = 'DATA_PROCESSING_FAILED',\n /** Current environment doesn't support the operation */\n ENVIRONMENT_NOT_SUPPORTED = 'ENVIRONMENT_NOT_SUPPORTED',\n /** File format not supported for import/export */\n UNSUPPORTED_FORMAT = 'UNSUPPORTED_FORMAT',\n /** File system operation failed */\n FILE_ERROR = 'FILE_ERROR',\n /** Network or fetch operation failed */\n NETWORK_ERROR = 'NETWORK_ERROR',\n /** Invalid operation or function call */\n INVALID_OPERATION = 'INVALID_OPERATION',\n /** Crypto operation failed (hash, token generation) */\n CRYPTO_ERROR = 'CRYPTO_ERROR',\n /** Math calculation failed (invalid input, division by zero) */\n MATH_ERROR = 'MATH_ERROR',\n /** Generic unknown error */\n UNKNOWN_ERROR = 'UNKNOWN_ERROR',\n}\n\n/**\n * Options for creating TsHelpersError instances\n */\nexport interface TsHelpersErrorOptions {\n /** Specific error code for categorization */\n code?: TsHelpersErrorCode\n /** Additional data about the error context */\n data?: any\n /** Original error that caused this error */\n cause?: Error\n}\n\n/**\n * Main error class for @g10/ts-helpers with structured error information\n * @example\n * ```ts\n * // Creating structured errors\n * throw new TsHelpersError('Invalid NIF format', {\n * code: TsHelpersErrorCode.VALIDATION_FAILED,\n * data: { input: '12345', expected: '8 digits + letter' }\n * })\n *\n * // Error handling with type checking\n * try {\n * someOperation()\n * } catch (error) {\n * if (error instanceof TsHelpersError) {\n * console.log('Error code:', error.code)\n * console.log('Error data:', error.data)\n * }\n * }\n * ```\n */\nexport class TsHelpersError extends Error {\n public readonly code?: TsHelpersErrorCode\n public readonly data?: any\n public readonly cause?: Error\n\n constructor(message: string, options: TsHelpersErrorOptions = {}) {\n super(message)\n this.name = 'TsHelpersError'\n this.code = options.code\n this.data = options.data\n this.cause = options.cause\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TsHelpersError)\n }\n }\n}\n\n/**\n * Specialized error class for data processing operations\n * @example\n * ```ts\n * // Data processing errors\n * throw new DataError(\n * 'Failed to parse CSV',\n * TsHelpersErrorCode.DATA_PROCESSING_FAILED,\n * { line: 5, column: 'amount' }\n * )\n * ```\n */\nexport class DataError extends TsHelpersError {\n constructor(message: string, code?: TsHelpersErrorCode, data?: any) {\n super(message, { code, data })\n this.name = 'DataError'\n }\n}\n\n/**\n * Creates a validation error with structured information\n * @param message - Descriptive error message\n * @param field - Field name that failed validation\n * @param value - Value that failed validation\n * @returns TsHelpersError with validation context\n * @example\n * ```ts\n * // Using in validation functions\n * if (!isValidEmail(email)) {\n * throw createValidationError('Invalid email format', 'email', email)\n * }\n *\n * // Error contains structured data\n * // error.code === TsHelpersErrorCode.VALIDATION_FAILED\n * // error.data === { field: 'email', value: 'invalid-email' }\n * ```\n */\nexport function createValidationError(message: string, field: string, value: any): TsHelpersError {\n return new TsHelpersError(`Validation failed: ${message}`, {\n code: TsHelpersErrorCode.VALIDATION_FAILED,\n data: { field, value },\n })\n}\n\n/**\n * Creates a crypto operation error\n * @param message - Descriptive error message\n * @param operation - Crypto operation that failed\n * @param details - Additional error details\n * @returns TsHelpersError with crypto context\n * @example\n * ```ts\n * // Using in crypto functions\n * if (!crypto.getRandomValues) {\n * throw createCryptoError('Web Crypto API not available', 'generateSecureToken', {\n * environment: 'browser',\n * fallback: 'Math.random'\n * })\n * }\n * ```\n */\nexport function createCryptoError(\n message: string,\n operation: string,\n details?: any\n): TsHelpersError {\n return new TsHelpersError(`Crypto error: ${message}`, {\n code: TsHelpersErrorCode.CRYPTO_ERROR,\n data: { operation, details },\n })\n}\n\n/**\n * Creates a math calculation error\n * @param message - Descriptive error message\n * @param operation - Math operation that failed\n * @param input - Input data that caused the error\n * @returns TsHelpersError with math context\n * @example\n * ```ts\n * // Using in math functions\n * if (values.length === 0) {\n * throw createMathError('Cannot calculate median of empty array', 'calculateMedian', values)\n * }\n * ```\n */\nexport function createMathError(message: string, operation: string, input?: any): TsHelpersError {\n return new TsHelpersError(`Math error: ${message}`, {\n code: TsHelpersErrorCode.MATH_ERROR,\n data: { operation, input },\n })\n}\n"],"mappings":"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","names":["Papa","renderTreeAsText","isValidFilePath","isValidFileSize","isValidTextContent","exportCSV","exportJSON","importCSV","importJSON"]}