@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.
- package/CHANGELOG.md +320 -0
- package/README.md +233 -0
- package/USAGE-GUIDE.md +800 -0
- package/dist/browser/async.js +15 -0
- package/dist/browser/async.js.map +1 -0
- package/dist/browser/chunk-4O7ZPIJN.js +383 -0
- package/dist/browser/chunk-4O7ZPIJN.js.map +1 -0
- package/dist/browser/chunk-75XNTC34.js +60 -0
- package/dist/browser/chunk-75XNTC34.js.map +1 -0
- package/dist/browser/chunk-C3D7YZVE.js +299 -0
- package/dist/browser/chunk-C3D7YZVE.js.map +1 -0
- package/dist/browser/chunk-CZL6C2EI.js +452 -0
- package/dist/browser/chunk-CZL6C2EI.js.map +1 -0
- package/dist/browser/chunk-D4FZFIVA.js +240 -0
- package/dist/browser/chunk-D4FZFIVA.js.map +1 -0
- package/dist/browser/chunk-IL7NG7IC.js +72 -0
- package/dist/browser/chunk-IL7NG7IC.js.map +1 -0
- package/dist/browser/chunk-NSBPE2FW.js +17 -0
- package/dist/browser/chunk-NSBPE2FW.js.map +1 -0
- package/dist/browser/chunk-SLQVNPTH.js +27 -0
- package/dist/browser/chunk-SLQVNPTH.js.map +1 -0
- package/dist/browser/chunk-WG7ILCUB.js +195 -0
- package/dist/browser/chunk-WG7ILCUB.js.map +1 -0
- package/dist/browser/chunk-WJA4JDMZ.js +278 -0
- package/dist/browser/chunk-WJA4JDMZ.js.map +1 -0
- package/dist/browser/chunk-ZFVYLUTT.js +65 -0
- package/dist/browser/chunk-ZFVYLUTT.js.map +1 -0
- package/dist/browser/chunk-ZYTSVMTI.js +263 -0
- package/dist/browser/chunk-ZYTSVMTI.js.map +1 -0
- package/dist/browser/dates.js +78 -0
- package/dist/browser/dates.js.map +1 -0
- package/dist/browser/environment-detection.js +21 -0
- package/dist/browser/environment-detection.js.map +1 -0
- package/dist/browser/environment.js +34 -0
- package/dist/browser/environment.js.map +1 -0
- package/dist/browser/errors.js +18 -0
- package/dist/browser/errors.js.map +1 -0
- package/dist/browser/index.js +412 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/math.js +51 -0
- package/dist/browser/math.js.map +1 -0
- package/dist/browser/number.js +10 -0
- package/dist/browser/number.js.map +1 -0
- package/dist/browser/objects.js +31 -0
- package/dist/browser/objects.js.map +1 -0
- package/dist/browser/strings.js +80 -0
- package/dist/browser/strings.js.map +1 -0
- package/dist/browser/validation-core.js +54 -0
- package/dist/browser/validation-core.js.map +1 -0
- package/dist/browser/validation-crypto.js +28 -0
- package/dist/browser/validation-crypto.js.map +1 -0
- package/dist/browser/validators.js +98 -0
- package/dist/browser/validators.js.map +1 -0
- package/dist/cjs/async.js +86 -0
- package/dist/cjs/async.js.map +1 -0
- package/dist/cjs/dates.js +285 -0
- package/dist/cjs/dates.js.map +1 -0
- package/dist/cjs/environment-detection.js +84 -0
- package/dist/cjs/environment-detection.js.map +1 -0
- package/dist/cjs/environment.js +261 -0
- package/dist/cjs/environment.js.map +1 -0
- package/dist/cjs/errors.js +80 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.js +2035 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/math.js +388 -0
- package/dist/cjs/math.js.map +1 -0
- package/dist/cjs/number.js +37 -0
- package/dist/cjs/number.js.map +1 -0
- package/dist/cjs/objects.js +249 -0
- package/dist/cjs/objects.js.map +1 -0
- package/dist/cjs/strings.js +253 -0
- package/dist/cjs/strings.js.map +1 -0
- package/dist/cjs/validation.js +450 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/async.js +15 -0
- package/dist/esm/async.js.map +1 -0
- package/dist/esm/chunk-4O7ZPIJN.js +383 -0
- package/dist/esm/chunk-4O7ZPIJN.js.map +1 -0
- package/dist/esm/chunk-75XNTC34.js +60 -0
- package/dist/esm/chunk-75XNTC34.js.map +1 -0
- package/dist/esm/chunk-BDOBKBKA.js +72 -0
- package/dist/esm/chunk-BDOBKBKA.js.map +1 -0
- package/dist/esm/chunk-C3D7YZVE.js +299 -0
- package/dist/esm/chunk-C3D7YZVE.js.map +1 -0
- package/dist/esm/chunk-CZL6C2EI.js +452 -0
- package/dist/esm/chunk-CZL6C2EI.js.map +1 -0
- package/dist/esm/chunk-EBLSTOEC.js +263 -0
- package/dist/esm/chunk-EBLSTOEC.js.map +1 -0
- package/dist/esm/chunk-NSBPE2FW.js +17 -0
- package/dist/esm/chunk-NSBPE2FW.js.map +1 -0
- package/dist/esm/chunk-SLQVNPTH.js +27 -0
- package/dist/esm/chunk-SLQVNPTH.js.map +1 -0
- package/dist/esm/chunk-WG7ILCUB.js +195 -0
- package/dist/esm/chunk-WG7ILCUB.js.map +1 -0
- package/dist/esm/chunk-WJA4JDMZ.js +278 -0
- package/dist/esm/chunk-WJA4JDMZ.js.map +1 -0
- package/dist/esm/chunk-ZFVYLUTT.js +65 -0
- package/dist/esm/chunk-ZFVYLUTT.js.map +1 -0
- package/dist/esm/dates.js +78 -0
- package/dist/esm/dates.js.map +1 -0
- package/dist/esm/environment-detection.js +21 -0
- package/dist/esm/environment-detection.js.map +1 -0
- package/dist/esm/environment.js +34 -0
- package/dist/esm/environment.js.map +1 -0
- package/dist/esm/errors.js +18 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.js +380 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/math.js +51 -0
- package/dist/esm/math.js.map +1 -0
- package/dist/esm/number.js +10 -0
- package/dist/esm/number.js.map +1 -0
- package/dist/esm/objects.js +31 -0
- package/dist/esm/objects.js.map +1 -0
- package/dist/esm/strings.js +80 -0
- package/dist/esm/strings.js.map +1 -0
- package/dist/esm/validation.js +54 -0
- package/dist/esm/validation.js.map +1 -0
- package/dist/node/async.js +93 -0
- package/dist/node/async.js.map +1 -0
- package/dist/node/csv.js +102 -0
- package/dist/node/csv.js.map +1 -0
- package/dist/node/data.js +880 -0
- package/dist/node/data.js.map +1 -0
- package/dist/node/dates.js +324 -0
- package/dist/node/dates.js.map +1 -0
- package/dist/node/environment.js +278 -0
- package/dist/node/environment.js.map +1 -0
- package/dist/node/errors.js +89 -0
- package/dist/node/errors.js.map +1 -0
- package/dist/node/index.js +3151 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/json.js +107 -0
- package/dist/node/json.js.map +1 -0
- package/dist/node/math.js +413 -0
- package/dist/node/math.js.map +1 -0
- package/dist/node/number.js +42 -0
- package/dist/node/number.js.map +1 -0
- package/dist/node/objects.js +264 -0
- package/dist/node/objects.js.map +1 -0
- package/dist/node/strings.js +293 -0
- package/dist/node/strings.js.map +1 -0
- package/dist/node/tree.js +89 -0
- package/dist/node/tree.js.map +1 -0
- package/dist/node/validation-core.js +477 -0
- package/dist/node/validation-core.js.map +1 -0
- package/dist/node/validation-crypto.js +179 -0
- package/dist/node/validation-crypto.js.map +1 -0
- package/dist/node/validation.js +677 -0
- package/dist/node/validation.js.map +1 -0
- package/dist/node/validators.js +123 -0
- package/dist/node/validators.js.map +1 -0
- package/dist/node-esm/async.js +15 -0
- package/dist/node-esm/async.js.map +1 -0
- package/dist/node-esm/chunk-3YOF7NPT.js +299 -0
- package/dist/node-esm/chunk-3YOF7NPT.js.map +1 -0
- package/dist/node-esm/chunk-64TBXJQS.js +263 -0
- package/dist/node-esm/chunk-64TBXJQS.js.map +1 -0
- package/dist/node-esm/chunk-75XNTC34.js +60 -0
- package/dist/node-esm/chunk-75XNTC34.js.map +1 -0
- package/dist/node-esm/chunk-C4PKXIPB.js +278 -0
- package/dist/node-esm/chunk-C4PKXIPB.js.map +1 -0
- package/dist/node-esm/chunk-CMDFZME3.js +452 -0
- package/dist/node-esm/chunk-CMDFZME3.js.map +1 -0
- package/dist/node-esm/chunk-DZZPUYMP.js +74 -0
- package/dist/node-esm/chunk-DZZPUYMP.js.map +1 -0
- package/dist/node-esm/chunk-HTSEHRHI.js +195 -0
- package/dist/node-esm/chunk-HTSEHRHI.js.map +1 -0
- package/dist/node-esm/chunk-JCAUVOPH.js +27 -0
- package/dist/node-esm/chunk-JCAUVOPH.js.map +1 -0
- package/dist/node-esm/chunk-KBHE3K2F.js +505 -0
- package/dist/node-esm/chunk-KBHE3K2F.js.map +1 -0
- package/dist/node-esm/chunk-LYTET5NX.js +65 -0
- package/dist/node-esm/chunk-LYTET5NX.js.map +1 -0
- package/dist/node-esm/chunk-PZ5AY32C.js +10 -0
- package/dist/node-esm/chunk-PZ5AY32C.js.map +1 -0
- package/dist/node-esm/chunk-UKGXL2QO.js +383 -0
- package/dist/node-esm/chunk-UKGXL2QO.js.map +1 -0
- package/dist/node-esm/chunk-XAEYT23H.js +164 -0
- package/dist/node-esm/chunk-XAEYT23H.js.map +1 -0
- package/dist/node-esm/csv.js +63 -0
- package/dist/node-esm/csv.js.map +1 -0
- package/dist/node-esm/data.js +32 -0
- package/dist/node-esm/data.js.map +1 -0
- package/dist/node-esm/dates.js +78 -0
- package/dist/node-esm/dates.js.map +1 -0
- package/dist/node-esm/environment.js +34 -0
- package/dist/node-esm/environment.js.map +1 -0
- package/dist/node-esm/errors.js +18 -0
- package/dist/node-esm/errors.js.map +1 -0
- package/dist/node-esm/index.js +426 -0
- package/dist/node-esm/index.js.map +1 -0
- package/dist/node-esm/json.js +68 -0
- package/dist/node-esm/json.js.map +1 -0
- package/dist/node-esm/math.js +51 -0
- package/dist/node-esm/math.js.map +1 -0
- package/dist/node-esm/number.js +10 -0
- package/dist/node-esm/number.js.map +1 -0
- package/dist/node-esm/objects.js +31 -0
- package/dist/node-esm/objects.js.map +1 -0
- package/dist/node-esm/strings.js +80 -0
- package/dist/node-esm/strings.js.map +1 -0
- package/dist/node-esm/tree.js +8 -0
- package/dist/node-esm/tree.js.map +1 -0
- package/dist/node-esm/validation-core.js +54 -0
- package/dist/node-esm/validation-core.js.map +1 -0
- package/dist/node-esm/validation-crypto.js +26 -0
- package/dist/node-esm/validation-crypto.js.map +1 -0
- package/dist/node-esm/validation.js +606 -0
- package/dist/node-esm/validation.js.map +1 -0
- package/dist/node-esm/validators.js +98 -0
- package/dist/node-esm/validators.js.map +1 -0
- package/dist/types/async-C8gvbSG-.d.ts +453 -0
- package/dist/types/async.d.ts +1 -0
- package/dist/types/csv.d.ts +226 -0
- package/dist/types/data.d.ts +1561 -0
- package/dist/types/dates-hTiE0Z11.d.ts +298 -0
- package/dist/types/dates.d.ts +1 -0
- package/dist/types/environment-B8eLS7KT.d.ts +420 -0
- package/dist/types/environment-detection.d.ts +102 -0
- package/dist/types/environment.d.ts +1 -0
- package/dist/types/errors.d.ts +147 -0
- package/dist/types/index.d.ts +211 -0
- package/dist/types/json.d.ts +284 -0
- package/dist/types/math-BQ9Lwdp7.d.ts +2060 -0
- package/dist/types/math.d.ts +1 -0
- package/dist/types/number-CYnQfLWj.d.ts +44 -0
- package/dist/types/number.d.ts +1 -0
- package/dist/types/objects-BohS8GCS.d.ts +1185 -0
- package/dist/types/objects.d.ts +1 -0
- package/dist/types/strings-CiqRPYLL.d.ts +1349 -0
- package/dist/types/strings.d.ts +1 -0
- package/dist/types/tree.d.ts +284 -0
- package/dist/types/validation-core-DfHF8rCG.d.ts +238 -0
- package/dist/types/validation-crypto-browser.d.ts +56 -0
- package/dist/types/validation-crypto-node.d.ts +31 -0
- package/dist/types/validation.d.ts +1 -0
- package/dist/types/validators.d.ts +216 -0
- package/package.json +253 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
capitalizeEachWord,
|
|
3
|
+
capitalizeFirst,
|
|
4
|
+
cleanJsonChars,
|
|
5
|
+
contains,
|
|
6
|
+
countOccurrences,
|
|
7
|
+
endsWith,
|
|
8
|
+
ensureEndsWith,
|
|
9
|
+
ensureStartsWith,
|
|
10
|
+
envKeyToPath,
|
|
11
|
+
escapeHtmlChars,
|
|
12
|
+
isEmail,
|
|
13
|
+
isEmpty,
|
|
14
|
+
matchPathPattern,
|
|
15
|
+
padEnd,
|
|
16
|
+
padStart,
|
|
17
|
+
pathToEnvKey,
|
|
18
|
+
removeAccents,
|
|
19
|
+
repeatString,
|
|
20
|
+
replaceAllOccurrences,
|
|
21
|
+
reverseString,
|
|
22
|
+
sanitizeString,
|
|
23
|
+
startsWith,
|
|
24
|
+
stripFromEnd,
|
|
25
|
+
stripFromStart,
|
|
26
|
+
toCamelCase,
|
|
27
|
+
toKebabCase,
|
|
28
|
+
toLowerCase,
|
|
29
|
+
toPascalCase,
|
|
30
|
+
toSnakeCase,
|
|
31
|
+
toUpperCase,
|
|
32
|
+
toUrlSlug,
|
|
33
|
+
trim,
|
|
34
|
+
trimEnd,
|
|
35
|
+
trimStart,
|
|
36
|
+
truncateString,
|
|
37
|
+
unescapeHtmlChars,
|
|
38
|
+
unescapeUnicode
|
|
39
|
+
} from "./chunk-WJA4JDMZ.js";
|
|
40
|
+
import "./chunk-NSBPE2FW.js";
|
|
41
|
+
export {
|
|
42
|
+
capitalizeEachWord,
|
|
43
|
+
capitalizeFirst,
|
|
44
|
+
cleanJsonChars,
|
|
45
|
+
contains,
|
|
46
|
+
countOccurrences,
|
|
47
|
+
endsWith,
|
|
48
|
+
ensureEndsWith,
|
|
49
|
+
ensureStartsWith,
|
|
50
|
+
envKeyToPath,
|
|
51
|
+
escapeHtmlChars,
|
|
52
|
+
isEmail,
|
|
53
|
+
isEmpty,
|
|
54
|
+
matchPathPattern,
|
|
55
|
+
padEnd,
|
|
56
|
+
padStart,
|
|
57
|
+
pathToEnvKey,
|
|
58
|
+
removeAccents,
|
|
59
|
+
repeatString,
|
|
60
|
+
replaceAllOccurrences,
|
|
61
|
+
reverseString,
|
|
62
|
+
sanitizeString,
|
|
63
|
+
startsWith,
|
|
64
|
+
stripFromEnd,
|
|
65
|
+
stripFromStart,
|
|
66
|
+
toCamelCase,
|
|
67
|
+
toKebabCase,
|
|
68
|
+
toLowerCase,
|
|
69
|
+
toPascalCase,
|
|
70
|
+
toSnakeCase,
|
|
71
|
+
toUpperCase,
|
|
72
|
+
toUrlSlug,
|
|
73
|
+
trim,
|
|
74
|
+
trimEnd,
|
|
75
|
+
trimStart,
|
|
76
|
+
truncateString,
|
|
77
|
+
unescapeHtmlChars,
|
|
78
|
+
unescapeUnicode
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=strings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateAlphaNumericString,
|
|
3
|
+
generateAlphaString,
|
|
4
|
+
generateComplexString,
|
|
5
|
+
generateEmail,
|
|
6
|
+
generateHexColor,
|
|
7
|
+
generatePassword,
|
|
8
|
+
generateRandomInteger,
|
|
9
|
+
generateSpanishCIF,
|
|
10
|
+
generateSpanishIBAN,
|
|
11
|
+
generateSpanishNIE,
|
|
12
|
+
generateSpanishNIF,
|
|
13
|
+
generateSpanishPostalCode,
|
|
14
|
+
generateUsername,
|
|
15
|
+
generateUsernameFromEmail,
|
|
16
|
+
isValidCIF,
|
|
17
|
+
isValidEmail,
|
|
18
|
+
isValidJSON,
|
|
19
|
+
isValidNIE,
|
|
20
|
+
isValidNIF,
|
|
21
|
+
isValidSpanishIBAN,
|
|
22
|
+
isValidSpanishPhone,
|
|
23
|
+
isValidSpanishPostalCode,
|
|
24
|
+
isValidURL,
|
|
25
|
+
validateNIF
|
|
26
|
+
} from "./chunk-CZL6C2EI.js";
|
|
27
|
+
import "./chunk-NSBPE2FW.js";
|
|
28
|
+
export {
|
|
29
|
+
generateAlphaNumericString,
|
|
30
|
+
generateAlphaString,
|
|
31
|
+
generateComplexString,
|
|
32
|
+
generateEmail,
|
|
33
|
+
generateHexColor,
|
|
34
|
+
generatePassword,
|
|
35
|
+
generateRandomInteger,
|
|
36
|
+
generateSpanishCIF,
|
|
37
|
+
generateSpanishIBAN,
|
|
38
|
+
generateSpanishNIE,
|
|
39
|
+
generateSpanishNIF,
|
|
40
|
+
generateSpanishPostalCode,
|
|
41
|
+
generateUsername,
|
|
42
|
+
generateUsernameFromEmail,
|
|
43
|
+
isValidCIF,
|
|
44
|
+
isValidEmail,
|
|
45
|
+
isValidJSON,
|
|
46
|
+
isValidNIE,
|
|
47
|
+
isValidNIF,
|
|
48
|
+
isValidSpanishIBAN,
|
|
49
|
+
isValidSpanishPhone,
|
|
50
|
+
isValidSpanishPostalCode,
|
|
51
|
+
isValidURL,
|
|
52
|
+
validateNIF
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/async.ts
|
|
21
|
+
var async_exports = {};
|
|
22
|
+
__export(async_exports, {
|
|
23
|
+
handleOperation: () => handleOperation,
|
|
24
|
+
runBatch: () => runBatch,
|
|
25
|
+
sleep: () => sleep,
|
|
26
|
+
wait: () => wait
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(async_exports);
|
|
29
|
+
|
|
30
|
+
// src/errors.ts
|
|
31
|
+
var TsHelpersError = class _TsHelpersError extends Error {
|
|
32
|
+
constructor(message, options = {}) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "TsHelpersError";
|
|
35
|
+
this.code = options.code;
|
|
36
|
+
this.data = options.data;
|
|
37
|
+
this.cause = options.cause;
|
|
38
|
+
if (Error.captureStackTrace) {
|
|
39
|
+
Error.captureStackTrace(this, _TsHelpersError);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/async.ts
|
|
45
|
+
var sleep = (ms) => {
|
|
46
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
47
|
+
};
|
|
48
|
+
var wait = (ms) => sleep(ms);
|
|
49
|
+
async function runBatch(jobs, batchSize = 50) {
|
|
50
|
+
if (batchSize <= 0) {
|
|
51
|
+
throw new TsHelpersError("Batch size must be greater than 0", {
|
|
52
|
+
code: "INVALID_OPERATION" /* INVALID_OPERATION */,
|
|
53
|
+
data: { batchSize }
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const results = [];
|
|
57
|
+
const batches = Math.ceil(jobs.length / batchSize);
|
|
58
|
+
for (let i = 0; i < batches; i++) {
|
|
59
|
+
const batchStart = i * batchSize;
|
|
60
|
+
const batchEnd = batchStart + batchSize;
|
|
61
|
+
const batch = jobs.slice(batchStart, batchEnd);
|
|
62
|
+
const batchResults = await Promise.all(batch.map((job) => job));
|
|
63
|
+
results.push(...batchResults);
|
|
64
|
+
}
|
|
65
|
+
return results;
|
|
66
|
+
}
|
|
67
|
+
function handleOperation(target, operation, ...args) {
|
|
68
|
+
if (operation.includes("/")) {
|
|
69
|
+
const [parentOp, childOp] = operation.split("/");
|
|
70
|
+
if (!target[parentOp] || !target[parentOp][childOp]) {
|
|
71
|
+
throw new TsHelpersError(`Operation [${operation}] does not exist`, {
|
|
72
|
+
code: "INVALID_OPERATION" /* INVALID_OPERATION */,
|
|
73
|
+
data: { operation, parentOp, childOp }
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return target[parentOp][childOp](...args);
|
|
77
|
+
}
|
|
78
|
+
if (!target[operation]) {
|
|
79
|
+
throw new TsHelpersError(`Operation [${operation}] does not exist`, {
|
|
80
|
+
code: "INVALID_OPERATION" /* INVALID_OPERATION */,
|
|
81
|
+
data: { operation }
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return target[operation](...args);
|
|
85
|
+
}
|
|
86
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
87
|
+
0 && (module.exports = {
|
|
88
|
+
handleOperation,
|
|
89
|
+
runBatch,
|
|
90
|
+
sleep,
|
|
91
|
+
wait
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=async.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/async.ts","../../src/errors.ts"],"sourcesContent":["/**\n * Asynchronous utilities and operations\n * Consolidated from core/async module\n */\n\n/* eslint-disable no-await-in-loop */\n\nimport { TsHelpersError, TsHelpersErrorCode } from './errors'\n\n// =============================================================================\n// DELAY UTILITIES\n// =============================================================================\n\n/**\n * Creates an asynchronous delay for the specified number of milliseconds\n *\n * Pauses async execution without blocking the event loop. Uses Promise with setTimeout\n * internally to schedule resumption after the specified delay.\n *\n * Common use cases:\n * - Rate limiting API calls\n * - Retry delays with exponential backoff\n * - Animation/transition timing\n * - Polling intervals\n * - Debouncing operations\n *\n * @param ms - Milliseconds to sleep (delay duration)\n * @returns Promise that resolves after the specified delay\n *\n * @example\n * ```typescript\n * // Basic delay\n * console.log('Starting...')\n * await sleep(2000) // Wait 2 seconds\n * console.log('Done!')\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Rate-limited API calls\n * async function fetchAllUsers(userIds: string[]): Promise<User[]> {\n * const users: User[] = []\n *\n * for (const id of userIds) {\n * const user = await api.getUser(id)\n * users.push(user)\n *\n * // Rate limit: 100ms between requests (max 10 req/sec)\n * await sleep(100)\n * }\n *\n * return users\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Exponential backoff retry\n * async function fetchWithRetry(url: string, maxRetries = 3): Promise<Response> {\n * let lastError: Error | null = null\n *\n * for (let attempt = 0; attempt < maxRetries; attempt++) {\n * try {\n * return await fetch(url)\n * } catch (error) {\n * lastError = error as Error\n * const delay = Math.pow(2, attempt) * 1000 // 1s, 2s, 4s\n * console.log(`Retry ${attempt + 1}/${maxRetries} in ${delay}ms`)\n * await sleep(delay)\n * }\n * }\n *\n * throw lastError\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Polling with timeout\n * async function pollUntilReady(\n * checkFn: () => Promise<boolean>,\n * timeoutMs = 30000\n * ): Promise<boolean> {\n * const startTime = Date.now()\n *\n * while (Date.now() - startTime < timeoutMs) {\n * if (await checkFn()) {\n * return true\n * }\n * await sleep(1000) // Poll every second\n * }\n *\n * throw new Error('Timeout waiting for ready state')\n * }\n *\n * // Usage: Wait for service to be ready\n * await pollUntilReady(async () => {\n * const response = await fetch('/health')\n * return response.ok\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Animated progress bar\n * async function animateProgress(element: HTMLElement): Promise<void> {\n * for (let i = 0; i <= 100; i += 5) {\n * element.style.width = `${i}%`\n * await sleep(50) // 50ms per step = 1 second total\n * }\n * }\n * ```\n *\n * @see {@link wait} for alias with same functionality\n * @see {@link runBatch} for controlled concurrent execution\n */\nexport const sleep = (ms: number): Promise<void> => {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\n/**\n * Simple alias for sleep - Pauses execution for specified milliseconds\n *\n * Identical to {@link sleep}, provided for semantic clarity in code.\n * Use `wait` when the context emphasizes waiting for a condition or event.\n *\n * @param ms - Milliseconds to wait\n * @returns Promise that resolves after the specified delay\n *\n * @example\n * ```typescript\n * // Semantically clearer in some contexts\n * await wait(1000) // Wait 1 second before continuing\n *\n * // vs sleep (implies intentional delay)\n * await sleep(1000) // Sleep for 1 second\n * ```\n *\n * @see {@link sleep} for full documentation and examples\n */\nexport const wait = (ms: number): Promise<void> => sleep(ms)\n\n// =============================================================================\n// BATCH PROCESSING\n// =============================================================================\n\n/**\n * Executes an array of Promises in batches to control concurrency\n *\n * Processes promises in sequential batches, waiting for each batch to complete before\n * starting the next. Prevents overwhelming systems with too many concurrent requests.\n *\n * Algorithm:\n * 1. Divide promises into batches of size `batchSize`\n * 2. Execute each batch with Promise.all (parallel within batch)\n * 3. Wait for batch completion before starting next batch\n * 4. Collect and return all results in original order\n *\n * Use cases:\n * - Rate-limited API calls (respect API quotas)\n * - Database bulk operations (avoid connection pool exhaustion)\n * - File system operations (prevent file descriptor limits)\n * - Memory-intensive operations (control memory usage)\n *\n * @param jobs - Array of Promises to execute\n * @param batchSize - Number of promises to execute concurrently per batch (default: 50)\n * @returns Promise resolving to array of all results in original order\n *\n * @example\n * ```typescript\n * // Basic batch processing\n * const urls = ['url1', 'url2', ..., 'url100'] // 100 URLs\n * const fetchPromises = urls.map(url => fetch(url))\n *\n * // Execute 10 at a time instead of all 100 simultaneously\n * const responses = await runBatch(fetchPromises, 10)\n * // Batch 1: urls 0-9 (parallel)\n * // Batch 2: urls 10-19 (parallel) - starts after batch 1 completes\n * // ... 10 batches total\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Bulk user data fetch respecting API rate limits\n * async function fetchUsersInBatches(userIds: string[]): Promise<User[]> {\n * console.log(`Fetching ${userIds.length} users in batches of 20...`)\n *\n * const fetchPromises = userIds.map(id =>\n * fetch(`/api/users/${id}`).then(res => res.json())\n * )\n *\n * const users = await runBatch(fetchPromises, 20)\n *\n * console.log(`✅ Fetched ${users.length} users`)\n * return users\n * }\n *\n * // Fetches 1000 users: 50 batches of 20, not 1000 simultaneous requests\n * const allUsers = await fetchUsersInBatches(userIdArray)\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Database bulk insert with connection pool limits\n * async function bulkInsertUsers(users: User[]): Promise<void> {\n * // Database pool has 10 connections, use batch size of 5 for safety\n * const insertPromises = users.map(user =>\n * db.query('INSERT INTO users VALUES ($1, $2)', [user.name, user.email])\n * )\n *\n * await runBatch(insertPromises, 5)\n * console.log(`✅ Inserted ${users.length} users`)\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Image processing pipeline\n * async function processImages(imagePaths: string[]): Promise<Buffer[]> {\n * console.log(`Processing ${imagePaths.length} images...`)\n *\n * const processingPromises = imagePaths.map(async path => {\n * const img = await loadImage(path)\n * const resized = await resize(img, { width: 800, height: 600 })\n * const compressed = await compress(resized, { quality: 80 })\n * return compressed\n * })\n *\n * // Process 3 images at a time to avoid memory exhaustion\n * const processed = await runBatch(processingPromises, 3)\n *\n * console.log(`✅ Processed ${processed.length} images`)\n * return processed\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Parallel file uploads with progress tracking\n * async function uploadFilesWithProgress(\n * files: File[]\n * ): Promise<UploadResult[]> {\n * let completed = 0\n *\n * const uploadPromises = files.map(async file => {\n * const result = await uploadToS3(file)\n * completed++\n * console.log(`Progress: ${completed}/${files.length} (${((completed/files.length)*100).toFixed(1)}%)`)\n * return result\n * })\n *\n * // Upload 5 files at a time\n * return await runBatch(uploadPromises, 5)\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Scraping with politeness delay\n * async function scrapeWebsites(urls: string[]): Promise<ScrapedData[]> {\n * const scrapePromises = urls.map(async url => {\n * const html = await fetch(url).then(r => r.text())\n * const data = parseHTML(html)\n *\n * // Polite delay between requests in same batch\n * await sleep(200)\n *\n * return data\n * })\n *\n * // Only 3 concurrent requests to avoid overwhelming target server\n * return await runBatch(scrapePromises, 3)\n * }\n * ```\n *\n * @see {@link sleep} for adding delays between operations\n * @see {@link handleOperation} for dynamic operation execution\n */\nexport async function runBatch<T>(jobs: Promise<T>[], batchSize: number = 50): Promise<T[]> {\n if (batchSize <= 0) {\n throw new TsHelpersError('Batch size must be greater than 0', {\n code: TsHelpersErrorCode.INVALID_OPERATION,\n data: { batchSize },\n })\n }\n\n const results: T[] = []\n const batches = Math.ceil(jobs.length / batchSize)\n\n for (let i = 0; i < batches; i++) {\n const batchStart = i * batchSize\n const batchEnd = batchStart + batchSize\n const batch = jobs.slice(batchStart, batchEnd)\n\n const batchResults = await Promise.all(batch.map(job => job))\n results.push(...batchResults)\n }\n\n return results\n}\n\n// =============================================================================\n// DYNAMIC OPERATION UTILITIES\n// =============================================================================\n\n/**\n * Dynamically executes an operation on a target object by name\n *\n * Invokes methods on objects using string-based operation names. Supports nested\n * method access using 'parent/child' slash notation for organized operation namespacing.\n *\n * Features:\n * - Dynamic method invocation by string name\n * - Nested method access with '/' separator (e.g., 'api/getUser')\n * - Automatic error handling with descriptive messages\n * - Type-safe return value with generics\n *\n * Use cases:\n * - Dynamic API route handlers\n * - Plugin architectures with string-based commands\n * - RPC (Remote Procedure Call) implementations\n * - Command pattern implementations\n * - Configuration-driven operations\n *\n * @param target - Object containing methods to execute\n * @param operation - Method name or 'parent/child' path to execute\n * @param args - Arguments to pass to the operation\n * @returns Promise resolving to operation result\n * @throws {TsHelpersError} If operation doesn't exist on target\n *\n * @example\n * ```typescript\n * // Basic operation execution\n * const api = {\n * getUser: async (id: string) => ({ id, name: 'John' }),\n * createUser: async (data: any) => ({ id: '123', ...data })\n * }\n *\n * const user = await handleOperation<User>(api, 'getUser', 'user-123')\n * // { id: 'user-123', name: 'John' }\n * ```\n *\n * @example\n * ```typescript\n * // Nested operations with slash notation\n * const service = {\n * users: {\n * list: async () => [{ id: '1' }, { id: '2' }],\n * create: async (data: any) => ({ id: 'new', ...data }),\n * delete: async (id: string) => ({ success: true })\n * },\n * posts: {\n * list: async () => [{ id: 'post-1' }],\n * create: async (data: any) => ({ id: 'new-post', ...data })\n * }\n * }\n *\n * // Execute nested operation\n * const users = await handleOperation(service, 'users/list')\n * const newUser = await handleOperation(service, 'users/create', { name: 'Alice' })\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Dynamic API router\n * interface ApiRequest {\n * operation: string\n * params: any[]\n * }\n *\n * const apiHandlers = {\n * user: {\n * get: async (id: string) => db.users.findById(id),\n * create: async (data: UserInput) => db.users.create(data),\n * update: async (id: string, data: Partial<UserInput>) =>\n * db.users.update(id, data),\n * delete: async (id: string) => db.users.delete(id)\n * },\n * product: {\n * search: async (query: string) => db.products.search(query),\n * list: async (page: number) => db.products.list(page)\n * }\n * }\n *\n * async function handleApiRequest(req: ApiRequest): Promise<any> {\n * try {\n * return await handleOperation(\n * apiHandlers,\n * req.operation,\n * ...req.params\n * )\n * } catch (error) {\n * console.error(`Failed to execute ${req.operation}:`, error)\n * throw error\n * }\n * }\n *\n * // Usage\n * await handleApiRequest({ operation: 'user/get', params: ['user-123'] })\n * await handleApiRequest({ operation: 'product/search', params: ['laptop'] })\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: Plugin system with dynamic commands\n * interface Plugin {\n * name: string\n * commands: Record<string, (...args: any[]) => Promise<any>>\n * }\n *\n * class PluginManager {\n * private plugins: Map<string, Plugin> = new Map()\n *\n * register(plugin: Plugin): void {\n * this.plugins.set(plugin.name, plugin)\n * }\n *\n * async executeCommand(\n * pluginName: string,\n * command: string,\n * ...args: any[]\n * ): Promise<any> {\n * const plugin = this.plugins.get(pluginName)\n * if (!plugin) throw new Error(`Plugin ${pluginName} not found`)\n *\n * return handleOperation(plugin.commands, command, ...args)\n * }\n * }\n *\n * // Register plugins\n * const manager = new PluginManager()\n * manager.register({\n * name: 'fileOps',\n * commands: {\n * read: async (path: string) => fs.readFile(path, 'utf-8'),\n * write: async (path: string, content: string) =>\n * fs.writeFile(path, content)\n * }\n * })\n *\n * // Execute plugin commands dynamically\n * await manager.executeCommand('fileOps', 'read', './config.json')\n * ```\n *\n * @example\n * ```typescript\n * // Real-world: RPC-style service calls\n * const rpcService = {\n * math: {\n * add: async (a: number, b: number) => a + b,\n * multiply: async (a: number, b: number) => a * b\n * },\n * string: {\n * reverse: async (s: string) => s.split('').reverse().join(''),\n * uppercase: async (s: string) => s.toUpperCase()\n * }\n * }\n *\n * async function rpcCall(method: string, ...params: any[]): Promise<any> {\n * console.log(`RPC Call: ${method}(${params.join(', ')})`)\n * const result = await handleOperation(rpcService, method, ...params)\n * console.log(`RPC Result: ${result}`)\n * return result\n * }\n *\n * await rpcCall('math/add', 5, 3) // RPC Result: 8\n * await rpcCall('string/reverse', 'hello') // RPC Result: olleh\n * ```\n *\n * @example\n * ```typescript\n * // Error handling\n * try {\n * await handleOperation(api, 'nonexistent/method')\n * } catch (error) {\n * console.error(error.message)\n * // \"Operation [nonexistent/method] does not exist\"\n * console.error(error.code)\n * // TsHelpersErrorCode.INVALID_OPERATION\n * }\n * ```\n *\n * @see {@link TsHelpersError} for error structure\n * @see {@link TsHelpersErrorCode} for error codes\n */\nexport function handleOperation<T>(\n target: Record<string, any>,\n operation: string,\n ...args: any[]\n): Promise<T> {\n if (operation.includes('/')) {\n const [parentOp, childOp] = operation.split('/')\n\n if (!target[parentOp] || !target[parentOp][childOp]) {\n throw new TsHelpersError(`Operation [${operation}] does not exist`, {\n code: TsHelpersErrorCode.INVALID_OPERATION,\n data: { operation, parentOp, childOp },\n })\n }\n\n return target[parentOp][childOp](...args)\n }\n\n if (!target[operation]) {\n throw new TsHelpersError(`Operation [${operation}] does not exist`, {\n code: TsHelpersErrorCode.INVALID_OPERATION,\n data: { operation },\n })\n }\n\n return target[operation](...args)\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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2EO,IAAM,iBAAN,MAAM,wBAAuB,MAAM;AAAA,EAKxC,YAAY,SAAiB,UAAiC,CAAC,GAAG;AAChE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ;AACpB,SAAK,OAAO,QAAQ;AACpB,SAAK,QAAQ,QAAQ;AAErB,QAAI,MAAM,mBAAmB;AAC3B,YAAM,kBAAkB,MAAM,eAAc;AAAA,IAC9C;AAAA,EACF;AACF;;;ADyBO,IAAM,QAAQ,CAAC,OAA8B;AAClD,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAsBO,IAAM,OAAO,CAAC,OAA8B,MAAM,EAAE;AA0I3D,eAAsB,SAAY,MAAoB,YAAoB,IAAkB;AAC1F,MAAI,aAAa,GAAG;AAClB,UAAM,IAAI,eAAe,qCAAqC;AAAA,MAC5D;AAAA,MACA,MAAM,EAAE,UAAU;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,QAAM,UAAe,CAAC;AACtB,QAAM,UAAU,KAAK,KAAK,KAAK,SAAS,SAAS;AAEjD,WAAS,IAAI,GAAG,IAAI,SAAS,KAAK;AAChC,UAAM,aAAa,IAAI;AACvB,UAAM,WAAW,aAAa;AAC9B,UAAM,QAAQ,KAAK,MAAM,YAAY,QAAQ;AAE7C,UAAM,eAAe,MAAM,QAAQ,IAAI,MAAM,IAAI,SAAO,GAAG,CAAC;AAC5D,YAAQ,KAAK,GAAG,YAAY;AAAA,EAC9B;AAEA,SAAO;AACT;AA0LO,SAAS,gBACd,QACA,cACG,MACS;AACZ,MAAI,UAAU,SAAS,GAAG,GAAG;AAC3B,UAAM,CAAC,UAAU,OAAO,IAAI,UAAU,MAAM,GAAG;AAE/C,QAAI,CAAC,OAAO,QAAQ,KAAK,CAAC,OAAO,QAAQ,EAAE,OAAO,GAAG;AACnD,YAAM,IAAI,eAAe,cAAc,SAAS,oBAAoB;AAAA,QAClE;AAAA,QACA,MAAM,EAAE,WAAW,UAAU,QAAQ;AAAA,MACvC,CAAC;AAAA,IACH;AAEA,WAAO,OAAO,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI;AAAA,EAC1C;AAEA,MAAI,CAAC,OAAO,SAAS,GAAG;AACtB,UAAM,IAAI,eAAe,cAAc,SAAS,oBAAoB;AAAA,MAClE;AAAA,MACA,MAAM,EAAE,UAAU;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,SAAO,OAAO,SAAS,EAAE,GAAG,IAAI;AAClC;","names":[]}
|
package/dist/node/csv.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
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/csv.ts
|
|
31
|
+
var csv_exports = {};
|
|
32
|
+
__export(csv_exports, {
|
|
33
|
+
exportCSV: () => exportCSV,
|
|
34
|
+
importCSV: () => importCSV
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(csv_exports);
|
|
37
|
+
var import_papaparse = __toESM(require("papaparse"));
|
|
38
|
+
|
|
39
|
+
// src/environment.ts
|
|
40
|
+
function isNodeEnvironment() {
|
|
41
|
+
return typeof window === "undefined" && typeof process !== "undefined" && !!process.versions?.node;
|
|
42
|
+
}
|
|
43
|
+
var isNode = isNodeEnvironment;
|
|
44
|
+
|
|
45
|
+
// src/csv.ts
|
|
46
|
+
async function exportCSV(data, filePath, options) {
|
|
47
|
+
if (data == null) {
|
|
48
|
+
throw new TypeError("Data cannot be null or undefined");
|
|
49
|
+
}
|
|
50
|
+
if (!Array.isArray(data)) {
|
|
51
|
+
throw new TypeError("Data must be an array");
|
|
52
|
+
}
|
|
53
|
+
if (data.length === 0) {
|
|
54
|
+
throw new Error("Data array cannot be empty");
|
|
55
|
+
}
|
|
56
|
+
if (!filePath || typeof filePath !== "string" || filePath.trim() === "") {
|
|
57
|
+
throw new TypeError("File path must be a non-empty string");
|
|
58
|
+
}
|
|
59
|
+
const csvText = import_papaparse.default.unparse(data, {
|
|
60
|
+
delimiter: ";",
|
|
61
|
+
header: true,
|
|
62
|
+
...options
|
|
63
|
+
});
|
|
64
|
+
if (isNode()) {
|
|
65
|
+
const fs = await import("fs/promises");
|
|
66
|
+
await fs.writeFile(filePath, `\uFEFF${csvText}`, { encoding: "utf-8" });
|
|
67
|
+
} else {
|
|
68
|
+
const blob = new Blob([`\uFEFF${csvText}`], { type: "text/csv;charset=utf-8" });
|
|
69
|
+
const url = URL.createObjectURL(blob);
|
|
70
|
+
const link = document.createElement("a");
|
|
71
|
+
link.href = url;
|
|
72
|
+
link.download = filePath.split("/").pop() || "data.csv";
|
|
73
|
+
document.body.appendChild(link);
|
|
74
|
+
link.click();
|
|
75
|
+
document.body.removeChild(link);
|
|
76
|
+
URL.revokeObjectURL(url);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function importCSV(filePath, options) {
|
|
80
|
+
if (!filePath || typeof filePath !== "string" || filePath.trim() === "") {
|
|
81
|
+
throw new TypeError("File path must be a non-empty string");
|
|
82
|
+
}
|
|
83
|
+
if (isNode()) {
|
|
84
|
+
const fs = await import("fs/promises");
|
|
85
|
+
const content = await fs.readFile(filePath, { encoding: "utf-8" });
|
|
86
|
+
const parseResult = import_papaparse.default.parse(content, {
|
|
87
|
+
header: true,
|
|
88
|
+
delimiter: ";",
|
|
89
|
+
skipEmptyLines: true,
|
|
90
|
+
...options
|
|
91
|
+
});
|
|
92
|
+
return parseResult.data || [];
|
|
93
|
+
} else {
|
|
94
|
+
throw new Error("CSV import not supported in browser. Use readFileAsText() with a File object.");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
98
|
+
0 && (module.exports = {
|
|
99
|
+
exportCSV,
|
|
100
|
+
importCSV
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=csv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/csv.ts","../../src/environment.ts"],"sourcesContent":["/**\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 * 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;AAaA,uBAAiB;;;ACyCV,SAAS,oBAA6B;AAC3C,SAAO,OAAO,WAAW,eAAe,OAAO,YAAY,eAAe,CAAC,CAAC,QAAQ,UAAU;AAChG;AAuhBO,IAAM,SAAS;;;ADjetB,eAAsB,UAAU,MAAa,UAAkB,SAA8B;AAE3F,MAAI,QAAQ,MAAM;AAChB,UAAM,IAAI,UAAU,kCAAkC;AAAA,EACxD;AAEA,MAAI,CAAC,MAAM,QAAQ,IAAI,GAAG;AACxB,UAAM,IAAI,UAAU,uBAAuB;AAAA,EAC7C;AAEA,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AAEA,MAAI,CAAC,YAAY,OAAO,aAAa,YAAY,SAAS,KAAK,MAAM,IAAI;AACvE,UAAM,IAAI,UAAU,sCAAsC;AAAA,EAC5D;AAEA,QAAM,UAAU,iBAAAA,QAAK,QAAQ,MAAM;AAAA,IACjC,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AAED,MAAI,OAAO,GAAG;AACZ,UAAM,KAAK,MAAM,OAAO,aAAa;AACrC,UAAM,GAAG,UAAU,UAAU,SAAS,OAAO,IAAI,EAAE,UAAU,QAAQ,CAAC;AAAA,EACxE,OAAO;AACL,UAAM,OAAO,IAAI,KAAK,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9E,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;AAsHA,eAAsB,UAAU,UAAkB,SAA+B;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;AACjE,UAAM,cAAc,iBAAAA,QAAK,MAAM,SAAS;AAAA,MACtC,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,GAAG;AAAA,IACL,CAAC;AACD,WAAO,YAAY,QAAQ,CAAC;AAAA,EAC9B,OAAO;AACL,UAAM,IAAI,MAAM,+EAA+E;AAAA,EACjG;AACF;","names":["Papa"]}
|