@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,107 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/json.ts
31
+ var json_exports = {};
32
+ __export(json_exports, {
33
+ exportJSON: () => exportJSON,
34
+ importJSON: () => importJSON
35
+ });
36
+ module.exports = __toCommonJS(json_exports);
37
+
38
+ // src/environment.ts
39
+ function isNodeEnvironment() {
40
+ return typeof window === "undefined" && typeof process !== "undefined" && !!process.versions?.node;
41
+ }
42
+ var isNode = isNodeEnvironment;
43
+
44
+ // src/json.ts
45
+ async function exportJSON(data, filePath, options) {
46
+ if (data === null || data === void 0) {
47
+ throw new TypeError("Data cannot be null or undefined");
48
+ }
49
+ if (!filePath || typeof filePath !== "string" || filePath.trim() === "") {
50
+ throw new TypeError("File path must be a non-empty string");
51
+ }
52
+ const { indent = 2 } = options || {};
53
+ let jsonText;
54
+ try {
55
+ jsonText = JSON.stringify(data, null, indent);
56
+ } catch (error) {
57
+ if (error instanceof TypeError && error.message.includes("circular")) {
58
+ throw new TypeError("Cannot serialize data with circular references");
59
+ }
60
+ throw error;
61
+ }
62
+ if (isNode()) {
63
+ const fs = await import("fs/promises");
64
+ await fs.writeFile(filePath, jsonText, { encoding: "utf-8" });
65
+ } else {
66
+ const blob = new Blob([jsonText], { type: "application/json;charset=utf-8" });
67
+ const url = URL.createObjectURL(blob);
68
+ const link = document.createElement("a");
69
+ link.href = url;
70
+ link.download = filePath.split("/").pop() || "data.json";
71
+ document.body.appendChild(link);
72
+ link.click();
73
+ document.body.removeChild(link);
74
+ URL.revokeObjectURL(url);
75
+ }
76
+ }
77
+ async function importJSON(filePath, _options) {
78
+ if (!filePath || typeof filePath !== "string" || filePath.trim() === "") {
79
+ throw new TypeError("File path must be a non-empty string");
80
+ }
81
+ if (isNode()) {
82
+ const fs = await import("fs/promises");
83
+ const content = await fs.readFile(filePath, { encoding: "utf-8" });
84
+ const trimmed = content.trim();
85
+ if (trimmed === "") {
86
+ throw new SyntaxError("JSON file is empty");
87
+ }
88
+ try {
89
+ return JSON.parse(content);
90
+ } catch (error) {
91
+ if (error instanceof SyntaxError) {
92
+ throw new SyntaxError(`Invalid JSON in file ${filePath}: ${error.message}`);
93
+ }
94
+ throw error;
95
+ }
96
+ } else {
97
+ throw new Error(
98
+ "JSON import not supported in browser. Use readFileAsText() with a File object."
99
+ );
100
+ }
101
+ }
102
+ // Annotate the CommonJS export names for ESM import in node:
103
+ 0 && (module.exports = {
104
+ exportJSON,
105
+ importJSON
106
+ });
107
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/json.ts","../../src/environment.ts"],"sourcesContent":["/**\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 * 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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsDO,SAAS,oBAA6B;AAC3C,SAAO,OAAO,WAAW,eAAe,OAAO,YAAY,eAAe,CAAC,CAAC,QAAQ,UAAU;AAChG;AAuhBO,IAAM,SAAS;;;AD3ctB,eAAsB,WACpB,MACA,UACA,SACe;AAEf,MAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,UAAM,IAAI,UAAU,kCAAkC;AAAA,EACxD;AAEA,MAAI,CAAC,YAAY,OAAO,aAAa,YAAY,SAAS,KAAK,MAAM,IAAI;AACvE,UAAM,IAAI,UAAU,sCAAsC;AAAA,EAC5D;AAEA,QAAM,EAAE,SAAS,EAAE,IAAI,WAAW,CAAC;AAGnC,MAAI;AACJ,MAAI;AACF,eAAW,KAAK,UAAU,MAAM,MAAM,MAAM;AAAA,EAC9C,SAAS,OAAO;AACd,QAAI,iBAAiB,aAAa,MAAM,QAAQ,SAAS,UAAU,GAAG;AACpE,YAAM,IAAI,UAAU,gDAAgD;AAAA,IACtE;AACA,UAAM;AAAA,EACR;AAEA,MAAI,OAAO,GAAG;AACZ,UAAM,KAAK,MAAM,OAAO,aAAa;AACrC,UAAM,GAAG,UAAU,UAAU,UAAU,EAAE,UAAU,QAAQ,CAAC;AAAA,EAC9D,OAAO;AACL,UAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,GAAG,EAAE,MAAM,iCAAiC,CAAC;AAC5E,UAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,UAAM,OAAO,SAAS,cAAc,GAAG;AACvC,SAAK,OAAO;AACZ,SAAK,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK;AAC7C,aAAS,KAAK,YAAY,IAAI;AAC9B,SAAK,MAAM;AACX,aAAS,KAAK,YAAY,IAAI;AAC9B,QAAI,gBAAgB,GAAG;AAAA,EACzB;AACF;AAuJA,eAAsB,WAAW,UAAkB,UAA8B;AAE/E,MAAI,CAAC,YAAY,OAAO,aAAa,YAAY,SAAS,KAAK,MAAM,IAAI;AACvE,UAAM,IAAI,UAAU,sCAAsC;AAAA,EAC5D;AAEA,MAAI,OAAO,GAAG;AACZ,UAAM,KAAK,MAAM,OAAO,aAAa;AACrC,UAAM,UAAU,MAAM,GAAG,SAAS,UAAU,EAAE,UAAU,QAAQ,CAAC;AAGjE,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,YAAY,IAAI;AAClB,YAAM,IAAI,YAAY,oBAAoB;AAAA,IAC5C;AAGA,QAAI;AACF,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,SAAS,OAAO;AACd,UAAI,iBAAiB,aAAa;AAChC,cAAM,IAAI,YAAY,wBAAwB,QAAQ,KAAK,MAAM,OAAO,EAAE;AAAA,MAC5E;AACA,YAAM;AAAA,IACR;AAAA,EACF,OAAO;AACL,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,413 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/math.ts
31
+ var math_exports = {};
32
+ __export(math_exports, {
33
+ calculateAggregations: () => calculateAggregations,
34
+ calculateAnnuityPayment: () => calculateAnnuityPayment,
35
+ calculateCorrelation: () => calculateCorrelation,
36
+ calculateEuclideanDistance: () => calculateEuclideanDistance,
37
+ calculateFutureValue: () => calculateFutureValue,
38
+ calculateHistogram: () => calculateHistogram,
39
+ calculateIQR: () => calculateIQR,
40
+ calculateIRR: () => calculateIRR,
41
+ calculateManhattanDistance: () => calculateManhattanDistance,
42
+ calculateMedian: () => calculateMedian,
43
+ calculateMode: () => calculateMode,
44
+ calculateNPV: () => calculateNPV,
45
+ calculatePercentile: () => calculatePercentile,
46
+ calculatePresentValue: () => calculatePresentValue,
47
+ calculateQuartiles: () => calculateQuartiles,
48
+ calculateStandardDeviation: () => calculateStandardDeviation,
49
+ calculateTrendSlope: () => calculateTrendSlope,
50
+ calculateVariance: () => calculateVariance,
51
+ detectOutliers: () => detectOutliers,
52
+ normalizeToRange: () => normalizeToRange,
53
+ scaleToRange: () => scaleToRange,
54
+ simpleKMeans: () => simpleKMeans
55
+ });
56
+ module.exports = __toCommonJS(math_exports);
57
+ var lodash = __toESM(require("lodash"));
58
+
59
+ // src/number.ts
60
+ function roundToDecimals(num, decimals = 2) {
61
+ const factor = Math.pow(10, decimals);
62
+ return num >= 0 ? Math.round(num * factor) / factor : -Math.round(Math.abs(num) * factor) / factor;
63
+ }
64
+
65
+ // src/math.ts
66
+ var { map, round, sumBy, get, meanBy, maxBy, some, minBy, uniq, size, sum, zipObject, groupBy } = lodash;
67
+ var calculateAggregations = (operations, pgroupBy, inData, appendOperationToField = true) => {
68
+ let data = [];
69
+ if (pgroupBy?.length) {
70
+ const groupedData = groupBy(
71
+ inData,
72
+ (item) => pgroupBy.map((field) => get(item, field)).join("$|marca|@")
73
+ );
74
+ data = map(groupedData, (items, groupKey) => {
75
+ const obj = zipObject(pgroupBy, groupKey.split("$|marca|@"));
76
+ for (const operation in operations) {
77
+ const fields = operations[operation];
78
+ fields.forEach((field) => {
79
+ const resultField = appendOperationToField ? field + operation : field;
80
+ switch (operation) {
81
+ case "$count":
82
+ obj[resultField] = items.length;
83
+ break;
84
+ case "$sum":
85
+ obj[resultField] = sumBy(items, (item) => get(item, field));
86
+ break;
87
+ case "$avg":
88
+ obj[resultField] = round(
89
+ meanBy(items, (item) => get(item, field)),
90
+ 1
91
+ );
92
+ break;
93
+ case "$max": {
94
+ const maxItem = maxBy(items, (item) => get(item, field));
95
+ obj[resultField] = maxItem ? get(maxItem, field) : void 0;
96
+ break;
97
+ }
98
+ case "$min": {
99
+ const minItem = minBy(items, (item) => get(item, field));
100
+ obj[resultField] = minItem ? get(minItem, field) : void 0;
101
+ break;
102
+ }
103
+ case "$exist":
104
+ obj[resultField] = some(items, (item) => get(item, field)) ? 1 : 0;
105
+ break;
106
+ case "$trend":
107
+ obj[resultField] = calculateTrendSlope(
108
+ items.map((item) => get(item, field)),
109
+ items.map((_item, index) => index + 1)
110
+ );
111
+ break;
112
+ case "$countUniq":
113
+ obj[resultField] = uniq(items.map((item) => get(item, field))).length;
114
+ break;
115
+ }
116
+ });
117
+ }
118
+ return obj;
119
+ });
120
+ } else {
121
+ const results = {};
122
+ for (const operation in operations) {
123
+ const fields = operations[operation];
124
+ fields.forEach((field) => {
125
+ const resultField = appendOperationToField ? field + operation : field;
126
+ switch (operation) {
127
+ case "$count":
128
+ results[resultField] = inData.length;
129
+ break;
130
+ case "$sum":
131
+ results[resultField] = sumBy(inData, field);
132
+ break;
133
+ case "$avg":
134
+ results[resultField] = round(meanBy(inData, field), 1);
135
+ break;
136
+ case "$max":
137
+ if (Array.isArray(inData)) {
138
+ const maxItem = maxBy(inData, field);
139
+ results[resultField] = maxItem ? maxItem[field] : void 0;
140
+ }
141
+ break;
142
+ case "$min":
143
+ if (Array.isArray(inData)) {
144
+ const minItem = minBy(inData, field);
145
+ results[resultField] = minItem ? minItem[field] : void 0;
146
+ }
147
+ break;
148
+ case "$exist":
149
+ results[resultField] = some(inData, { [field]: true }) ? 1 : 0;
150
+ break;
151
+ case "$trend":
152
+ results[resultField] = calculateTrendSlope(
153
+ inData.map((item) => get(item, field)),
154
+ inData.map((_item, index) => index + 1)
155
+ );
156
+ break;
157
+ case "$countUniq":
158
+ results[resultField] = uniq(inData.map((item) => get(item, field))).length;
159
+ break;
160
+ }
161
+ });
162
+ }
163
+ data = results;
164
+ }
165
+ return data;
166
+ };
167
+ function calculateTrendSlope(y, x) {
168
+ const n = size(y);
169
+ const sum_x = sum(x);
170
+ const sum_y = sum(y);
171
+ const sum_x2 = sum(map(x, (val) => val * val));
172
+ const sum_xy = sum(map(x, (val, i) => val * y[i]));
173
+ const denominator = n * sum_x2 - sum_x * sum_x;
174
+ if (denominator === 0) {
175
+ return 0;
176
+ }
177
+ const slope = (n * sum_xy - sum_x * sum_y) / denominator;
178
+ return roundToDecimals(slope, 1);
179
+ }
180
+ var calculateMedian = (values) => {
181
+ if (!values.length) return 0;
182
+ const sorted = [...values].sort((a, b) => a - b);
183
+ const middle = Math.floor(sorted.length / 2);
184
+ if (sorted.length % 2 === 0) {
185
+ return (sorted[middle - 1] + sorted[middle]) / 2;
186
+ }
187
+ return sorted[middle];
188
+ };
189
+ var calculateMode = (values) => {
190
+ if (!values.length) return null;
191
+ const frequency = {};
192
+ let maxFreq = 0;
193
+ let mode = null;
194
+ let hasMultipleModes = false;
195
+ for (const value of values) {
196
+ frequency[value] = (frequency[value] || 0) + 1;
197
+ if (frequency[value] > maxFreq) {
198
+ maxFreq = frequency[value];
199
+ mode = value;
200
+ hasMultipleModes = false;
201
+ } else if (frequency[value] === maxFreq && value !== mode) {
202
+ hasMultipleModes = true;
203
+ }
204
+ }
205
+ return hasMultipleModes ? null : mode;
206
+ };
207
+ var calculateStandardDeviation = (values, sample = false) => {
208
+ if (!values.length) return 0;
209
+ const mean = values.reduce((sum2, val) => sum2 + val, 0) / values.length;
210
+ const squaredDiffs = values.map((val) => Math.pow(val - mean, 2));
211
+ const variance = squaredDiffs.reduce((sum2, val) => sum2 + val, 0) / (sample ? values.length - 1 : values.length);
212
+ return Math.sqrt(variance);
213
+ };
214
+ var calculateVariance = (values, sample = false) => {
215
+ if (!values.length) return 0;
216
+ const mean = values.reduce((sum2, val) => sum2 + val, 0) / values.length;
217
+ const squaredDiffs = values.map((val) => Math.pow(val - mean, 2));
218
+ return squaredDiffs.reduce((sum2, val) => sum2 + val, 0) / (sample ? values.length - 1 : values.length);
219
+ };
220
+ var calculatePercentile = (values, percentile) => {
221
+ if (!values.length) return 0;
222
+ if (percentile < 0 || percentile > 100) throw new Error("Percentile must be between 0 and 100");
223
+ const sorted = [...values].sort((a, b) => a - b);
224
+ const index = percentile / 100 * (sorted.length - 1);
225
+ if (Math.floor(index) === index) {
226
+ return sorted[index];
227
+ }
228
+ const lower = sorted[Math.floor(index)];
229
+ const upper = sorted[Math.ceil(index)];
230
+ const weight = index - Math.floor(index);
231
+ return lower * (1 - weight) + upper * weight;
232
+ };
233
+ var calculateQuartiles = (values) => {
234
+ return {
235
+ Q1: calculatePercentile(values, 25),
236
+ Q2: calculatePercentile(values, 50),
237
+ // median
238
+ Q3: calculatePercentile(values, 75)
239
+ };
240
+ };
241
+ var calculateIQR = (values) => {
242
+ const quartiles = calculateQuartiles(values);
243
+ return quartiles.Q3 - quartiles.Q1;
244
+ };
245
+ var detectOutliers = (values, multiplier = 1.5) => {
246
+ const quartiles = calculateQuartiles(values);
247
+ const iqr = quartiles.Q3 - quartiles.Q1;
248
+ const lowerBound = quartiles.Q1 - multiplier * iqr;
249
+ const upperBound = quartiles.Q3 + multiplier * iqr;
250
+ return values.filter((value) => value < lowerBound || value > upperBound);
251
+ };
252
+ var calculateCorrelation = (x, y) => {
253
+ if (x.length !== y.length || x.length === 0) return 0;
254
+ const n = x.length;
255
+ const sumX = sum(x);
256
+ const sumY = sum(y);
257
+ const sumXY = sum(map(x, (val, i) => val * y[i]));
258
+ const sumX2 = sum(map(x, (val) => val * val));
259
+ const sumY2 = sum(map(y, (val) => val * val));
260
+ const numerator = n * sumXY - sumX * sumY;
261
+ const denominator = Math.sqrt((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY));
262
+ if (denominator === 0) return 0;
263
+ return numerator / denominator;
264
+ };
265
+ var calculateNPV = (cashFlows, discountRate) => {
266
+ return cashFlows.reduce((npv, cashFlow, index) => {
267
+ const discountFactor = index === 0 ? 1 : Math.pow(1 + discountRate, index);
268
+ return npv + cashFlow / discountFactor;
269
+ }, 0);
270
+ };
271
+ var calculateIRR = (cashFlows, initialGuess = 0.1, maxIterations = 100, tolerance = 1e-6) => {
272
+ let rate = initialGuess;
273
+ for (let i = 0; i < maxIterations; i++) {
274
+ const npv = calculateNPV(cashFlows, rate);
275
+ const npvDerivative = cashFlows.reduce((sum2, cashFlow, index) => {
276
+ return sum2 - index * cashFlow / Math.pow(1 + rate, index + 1);
277
+ }, 0);
278
+ if (Math.abs(npv) < tolerance) return rate;
279
+ if (Math.abs(npvDerivative) < tolerance) break;
280
+ rate = rate - npv / npvDerivative;
281
+ }
282
+ return rate;
283
+ };
284
+ var calculateFutureValue = (presentValue, interestRate, periods) => {
285
+ return presentValue * Math.pow(1 + interestRate, periods);
286
+ };
287
+ var calculatePresentValue = (futureValue, interestRate, periods) => {
288
+ return futureValue / Math.pow(1 + interestRate, periods);
289
+ };
290
+ var calculateAnnuityPayment = (presentValue, interestRate, periods) => {
291
+ if (interestRate === 0) return presentValue / periods;
292
+ return presentValue * (interestRate * Math.pow(1 + interestRate, periods)) / (Math.pow(1 + interestRate, periods) - 1);
293
+ };
294
+ var normalizeToRange = (values) => {
295
+ if (!values.length) return [];
296
+ const min = Math.min(...values);
297
+ const max = Math.max(...values);
298
+ const range = max - min;
299
+ if (range === 0) return values.map(() => 0);
300
+ return values.map((value) => (value - min) / range);
301
+ };
302
+ var scaleToRange = (values, minRange, maxRange) => {
303
+ const normalized = normalizeToRange(values);
304
+ const scale = maxRange - minRange;
305
+ return normalized.map((value) => minRange + value * scale);
306
+ };
307
+ var calculateHistogram = (values, bins) => {
308
+ if (!values.length || bins <= 0) return [];
309
+ const min = Math.min(...values);
310
+ const max = Math.max(...values);
311
+ const binWidth = (max - min) / bins;
312
+ const histogram = [];
313
+ for (let i = 0; i < bins; i++) {
314
+ const rangeStart = min + i * binWidth;
315
+ const rangeEnd = i === bins - 1 ? max : rangeStart + binWidth;
316
+ const count = values.filter(
317
+ (value) => value >= rangeStart && (i === bins - 1 ? value <= rangeEnd : value < rangeEnd)
318
+ ).length;
319
+ histogram.push({
320
+ range: [roundToDecimals(rangeStart, 2), roundToDecimals(rangeEnd, 2)],
321
+ count
322
+ });
323
+ }
324
+ return histogram;
325
+ };
326
+ var calculateEuclideanDistance = (point1, point2) => {
327
+ if (point1.length !== point2.length) {
328
+ throw new Error("Points must have the same number of dimensions");
329
+ }
330
+ const squaredDiffs = point1.map((val, index) => Math.pow(val - point2[index], 2));
331
+ return Math.sqrt(sum(squaredDiffs));
332
+ };
333
+ var calculateManhattanDistance = (point1, point2) => {
334
+ if (point1.length !== point2.length) {
335
+ throw new Error("Points must have the same number of dimensions");
336
+ }
337
+ return sum(point1.map((val, index) => Math.abs(val - point2[index])));
338
+ };
339
+ var simpleKMeans = (points, k, maxIterations = 100) => {
340
+ if (points.length === 0 || k <= 0) {
341
+ return { centroids: [], clusters: [] };
342
+ }
343
+ const dimensions = points[0].length;
344
+ let centroids = [];
345
+ for (let i = 0; i < k; i++) {
346
+ const randomPoint = points[Math.floor(Math.random() * points.length)];
347
+ centroids.push([...randomPoint]);
348
+ }
349
+ const clusters = new Array(points.length);
350
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
351
+ let hasChanged = false;
352
+ for (let i = 0; i < points.length; i++) {
353
+ let minDistance = Infinity;
354
+ let closestCentroid = 0;
355
+ for (let j = 0; j < centroids.length; j++) {
356
+ const distance = calculateEuclideanDistance(points[i], centroids[j]);
357
+ if (distance < minDistance) {
358
+ minDistance = distance;
359
+ closestCentroid = j;
360
+ }
361
+ }
362
+ if (clusters[i] !== closestCentroid) {
363
+ hasChanged = true;
364
+ clusters[i] = closestCentroid;
365
+ }
366
+ }
367
+ const newCentroids = new Array(k).fill(0).map(() => new Array(dimensions).fill(0));
368
+ const clusterCounts = new Array(k).fill(0);
369
+ for (let i = 0; i < points.length; i++) {
370
+ const cluster = clusters[i];
371
+ clusterCounts[cluster]++;
372
+ for (let dim = 0; dim < dimensions; dim++) {
373
+ newCentroids[cluster][dim] += points[i][dim];
374
+ }
375
+ }
376
+ for (let i = 0; i < k; i++) {
377
+ if (clusterCounts[i] > 0) {
378
+ for (let dim = 0; dim < dimensions; dim++) {
379
+ newCentroids[i][dim] /= clusterCounts[i];
380
+ }
381
+ }
382
+ }
383
+ centroids = newCentroids;
384
+ if (!hasChanged) break;
385
+ }
386
+ return { centroids, clusters };
387
+ };
388
+ // Annotate the CommonJS export names for ESM import in node:
389
+ 0 && (module.exports = {
390
+ calculateAggregations,
391
+ calculateAnnuityPayment,
392
+ calculateCorrelation,
393
+ calculateEuclideanDistance,
394
+ calculateFutureValue,
395
+ calculateHistogram,
396
+ calculateIQR,
397
+ calculateIRR,
398
+ calculateManhattanDistance,
399
+ calculateMedian,
400
+ calculateMode,
401
+ calculateNPV,
402
+ calculatePercentile,
403
+ calculatePresentValue,
404
+ calculateQuartiles,
405
+ calculateStandardDeviation,
406
+ calculateTrendSlope,
407
+ calculateVariance,
408
+ detectOutliers,
409
+ normalizeToRange,
410
+ scaleToRange,
411
+ simpleKMeans
412
+ });
413
+ //# sourceMappingURL=math.js.map