@salespark/toolkit 2.1.17 → 2.1.19
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/README.md +55 -3
- package/dist/index.cjs +197 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -1
- package/dist/index.d.ts +47 -1
- package/dist/index.js +192 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -32,7 +32,8 @@ npm i @salespark/toolkit
|
|
|
32
32
|
- **Defer utilities**: post-return microtask scheduling, non-critical timers, after-response hooks.
|
|
33
33
|
- **Boolean utilities**: safe boolean conversion with common representations
|
|
34
34
|
- **Validation utilities**: IBAN validator (ISO 13616), Portuguese tax ID validator
|
|
35
|
-
- **Security utilities**: Markdown XSS protection, content sanitization, risk assessment, obfuscation helpers
|
|
35
|
+
- **Security utilities**: Markdown XSS protection, content sanitization, risk assessment, obfuscation helpers, reversible base36 code encoding/decoding
|
|
36
|
+
- **UUID utilities**: uuidv4 generator (RFC 4122)
|
|
36
37
|
- **Environment detection**: `isBrowser`, `isNode` runtime checks
|
|
37
38
|
|
|
38
39
|
---
|
|
@@ -52,6 +53,7 @@ import {
|
|
|
52
53
|
clamp,
|
|
53
54
|
isBrowser,
|
|
54
55
|
toBool,
|
|
56
|
+
uuidv4,
|
|
55
57
|
} from "@salespark/toolkit";
|
|
56
58
|
|
|
57
59
|
// Debounce a function
|
|
@@ -74,6 +76,9 @@ const safe = clamp(15, 0, 10); // 10
|
|
|
74
76
|
// Convert to boolean
|
|
75
77
|
const bool = toBool("yes"); // true
|
|
76
78
|
|
|
79
|
+
// Generate UUID v4
|
|
80
|
+
const id = uuidv4();
|
|
81
|
+
|
|
77
82
|
// Check environment
|
|
78
83
|
if (isBrowser) {
|
|
79
84
|
/* browser-specific code */
|
|
@@ -102,6 +107,9 @@ const safe = _.clamp(15, 0, 10); // 10
|
|
|
102
107
|
// Convert to boolean
|
|
103
108
|
const bool = _.toBool("yes"); // true
|
|
104
109
|
|
|
110
|
+
// Generate UUID v4
|
|
111
|
+
const id = _.uuidv4();
|
|
112
|
+
|
|
105
113
|
// Check environment
|
|
106
114
|
if (_.isBrowser) {
|
|
107
115
|
/* browser-specific code */
|
|
@@ -876,6 +884,50 @@ const decoded = decodeObject(encoded.data, "secret");
|
|
|
876
884
|
|
|
877
885
|
**`decodeObject(encoded: string, secret: string): SalesParkContract<object>`** — Reverses `encodeObject` using the same secret.
|
|
878
886
|
|
|
887
|
+
**`encodeBase36Code(identifier: string, config: EncodeDecodeConfig): SalesParkContract<{ code: string }>`** — Encodes a base36 identifier into a reversible lower-case base36 code using secret-based XOR + rotation.
|
|
888
|
+
|
|
889
|
+
```typescript
|
|
890
|
+
import { encodeBase36Code, decodeBase36Code } from "@salespark/toolkit";
|
|
891
|
+
|
|
892
|
+
const config = {
|
|
893
|
+
secret: "my-super-secret-key",
|
|
894
|
+
bitSize: 80,
|
|
895
|
+
rotateBits: 17,
|
|
896
|
+
addConstant: "0x1fd0a5b7c3",
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
const encoded = encodeBase36Code("AB12CD34", config);
|
|
900
|
+
// Result: { status: true, data: { code: "..." } }
|
|
901
|
+
|
|
902
|
+
const decoded = decodeBase36Code(encoded.data.code, config);
|
|
903
|
+
// Result: { status: true, data: { identifier: "AB12CD34" } }
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
**`decodeBase36Code(code: string, config: EncodeDecodeConfig): SalesParkContract<{ identifier: string }>`** — Decodes a previously encoded base36 code back to the original identifier (upper-case).
|
|
907
|
+
|
|
908
|
+
```typescript
|
|
909
|
+
const bad = encodeBase36Code("AB-12", {
|
|
910
|
+
secret: "my-super-secret-key",
|
|
911
|
+
});
|
|
912
|
+
// Result: { status: false, data: { message: "Identifier must be base36 (0-9, A-Z)" } }
|
|
913
|
+
|
|
914
|
+
const weakSecret = decodeBase36Code("abc123", {
|
|
915
|
+
secret: "short",
|
|
916
|
+
});
|
|
917
|
+
// Result: { status: false, data: { message: "Missing or weak secret" } }
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
**`EncodeDecodeConfig`** — Configuration object used by `encodeBase36Code` and `decodeBase36Code`.
|
|
921
|
+
|
|
922
|
+
```typescript
|
|
923
|
+
type EncodeDecodeConfig = {
|
|
924
|
+
secret: string; // required, minimum 12 chars
|
|
925
|
+
bitSize?: number; // default: 80
|
|
926
|
+
rotateBits?: number; // default: 17
|
|
927
|
+
addConstant?: string; // default: "0x1fd0a5b7c3"
|
|
928
|
+
};
|
|
929
|
+
```
|
|
930
|
+
|
|
879
931
|
### ✅ Validation Utilities
|
|
880
932
|
|
|
881
933
|
**`isPTTaxId(value: string | number): boolean`** — Validates Portuguese Tax ID (NIF) with MOD-11 algorithm and format checking.
|
|
@@ -1009,5 +1061,5 @@ MIT © [SalesPark](https://salespark.io)
|
|
|
1009
1061
|
|
|
1010
1062
|
---
|
|
1011
1063
|
|
|
1012
|
-
_Document version:
|
|
1013
|
-
_Last update:
|
|
1064
|
+
_Document version: 16_
|
|
1065
|
+
_Last update: 14-03-2026_
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var uuid = require('uuid');
|
|
4
|
+
|
|
3
5
|
// src/utils/array.ts
|
|
4
6
|
function uniqBy(arr, key) {
|
|
5
7
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2031,10 +2033,203 @@ var deferAfterResponseNonCritical = (res, fn) => {
|
|
|
2031
2033
|
}
|
|
2032
2034
|
};
|
|
2033
2035
|
|
|
2036
|
+
// src/utils/base36.ts
|
|
2037
|
+
var DEFAULTS = {
|
|
2038
|
+
bitSize: 80,
|
|
2039
|
+
rotateBits: 17,
|
|
2040
|
+
addConstant: "0x1fd0a5b7c3"
|
|
2041
|
+
};
|
|
2042
|
+
var isValidBase36 = (value) => /^[a-z0-9]+$/i.test(value);
|
|
2043
|
+
var normalizeInput = (value) => (value || "").trim();
|
|
2044
|
+
var assertSecret = (secret) => {
|
|
2045
|
+
if (typeof secret !== "string" || secret.trim().length < 12) {
|
|
2046
|
+
return { status: false, data: { message: "Missing or weak secret" } };
|
|
2047
|
+
}
|
|
2048
|
+
return { status: true, data: true };
|
|
2049
|
+
};
|
|
2050
|
+
var parseBase36BigInt = (value) => {
|
|
2051
|
+
try {
|
|
2052
|
+
const normalizedValue = normalizeInput(value);
|
|
2053
|
+
if (!normalizedValue) {
|
|
2054
|
+
return { status: false, data: { message: "Empty input" } };
|
|
2055
|
+
}
|
|
2056
|
+
if (!isValidBase36(normalizedValue)) {
|
|
2057
|
+
return { status: false, data: { message: "Invalid base36 input" } };
|
|
2058
|
+
}
|
|
2059
|
+
const safeValue = normalizedValue.toLowerCase();
|
|
2060
|
+
let output = 0n;
|
|
2061
|
+
for (let i = 0; i < safeValue.length; i++) {
|
|
2062
|
+
const char = safeValue[i];
|
|
2063
|
+
const digit = char >= "0" && char <= "9" ? BigInt(char.charCodeAt(0) - 48) : BigInt(char.charCodeAt(0) - 87);
|
|
2064
|
+
output = output * 36n + digit;
|
|
2065
|
+
}
|
|
2066
|
+
return { status: true, data: output };
|
|
2067
|
+
} catch (error) {
|
|
2068
|
+
return {
|
|
2069
|
+
status: false,
|
|
2070
|
+
data: { message: "Failed to parse base36 input", error }
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
};
|
|
2074
|
+
var toBase36Lower = (value) => {
|
|
2075
|
+
try {
|
|
2076
|
+
if (value < 0n) {
|
|
2077
|
+
return {
|
|
2078
|
+
status: false,
|
|
2079
|
+
data: { message: "Negative values are not supported" }
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
return { status: true, data: value.toString(36) };
|
|
2083
|
+
} catch (error) {
|
|
2084
|
+
return {
|
|
2085
|
+
status: false,
|
|
2086
|
+
data: { message: "Failed to convert to base36", error }
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
};
|
|
2090
|
+
var getParams = (config) => {
|
|
2091
|
+
try {
|
|
2092
|
+
const bitSizeRaw = BigInt(config.bitSize ?? DEFAULTS.bitSize);
|
|
2093
|
+
if (bitSizeRaw <= 0n) {
|
|
2094
|
+
return {
|
|
2095
|
+
status: false,
|
|
2096
|
+
data: { message: "bitSize must be greater than 0" }
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
const rotateRaw = BigInt(config.rotateBits ?? DEFAULTS.rotateBits);
|
|
2100
|
+
const rotateBits = (rotateRaw % bitSizeRaw + bitSizeRaw) % bitSizeRaw;
|
|
2101
|
+
const addConstant = BigInt(config.addConstant ?? DEFAULTS.addConstant);
|
|
2102
|
+
const mask = (1n << bitSizeRaw) - 1n;
|
|
2103
|
+
return {
|
|
2104
|
+
status: true,
|
|
2105
|
+
data: { bitSize: bitSizeRaw, rotateBits, addConstant, mask }
|
|
2106
|
+
};
|
|
2107
|
+
} catch (error) {
|
|
2108
|
+
return { status: false, data: { message: "Invalid configuration", error } };
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
var secretToKey = (secret, mask) => {
|
|
2112
|
+
let key = 0n;
|
|
2113
|
+
const safeSecret = secret.trim();
|
|
2114
|
+
for (let i = 0; i < safeSecret.length; i++) {
|
|
2115
|
+
key = key * 131n + BigInt(safeSecret.charCodeAt(i)) & mask;
|
|
2116
|
+
}
|
|
2117
|
+
return key;
|
|
2118
|
+
};
|
|
2119
|
+
var rotl = (x, r, bitSize, mask) => {
|
|
2120
|
+
if (r === 0n) return x & mask;
|
|
2121
|
+
return (x << r | x >> bitSize - r) & mask;
|
|
2122
|
+
};
|
|
2123
|
+
var rotr = (x, r, bitSize, mask) => {
|
|
2124
|
+
if (r === 0n) return x & mask;
|
|
2125
|
+
return (x >> r | x << bitSize - r) & mask;
|
|
2126
|
+
};
|
|
2127
|
+
var encodeBase36Code = (identifier, config) => {
|
|
2128
|
+
try {
|
|
2129
|
+
const secretCheck = assertSecret(config?.secret);
|
|
2130
|
+
if (!secretCheck.status) {
|
|
2131
|
+
return { status: false, data: secretCheck.data };
|
|
2132
|
+
}
|
|
2133
|
+
const input = normalizeInput(identifier);
|
|
2134
|
+
if (!input) {
|
|
2135
|
+
return { status: false, data: { message: "Identifier is required" } };
|
|
2136
|
+
}
|
|
2137
|
+
if (!isValidBase36(input)) {
|
|
2138
|
+
return {
|
|
2139
|
+
status: false,
|
|
2140
|
+
data: { message: "Identifier must be base36 (0-9, A-Z)" }
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
const parsed = parseBase36BigInt(input);
|
|
2144
|
+
if (!parsed.status) {
|
|
2145
|
+
return { status: false, data: parsed.data };
|
|
2146
|
+
}
|
|
2147
|
+
const params = getParams(config);
|
|
2148
|
+
if (!params.status) {
|
|
2149
|
+
return { status: false, data: params.data };
|
|
2150
|
+
}
|
|
2151
|
+
const {
|
|
2152
|
+
bitSize,
|
|
2153
|
+
rotateBits,
|
|
2154
|
+
addConstant,
|
|
2155
|
+
mask
|
|
2156
|
+
} = params.data;
|
|
2157
|
+
const key = secretToKey(config.secret, mask);
|
|
2158
|
+
let value = parsed.data & mask;
|
|
2159
|
+
value = value ^ key;
|
|
2160
|
+
value = value + addConstant & mask;
|
|
2161
|
+
value = rotl(value, rotateBits, bitSize, mask);
|
|
2162
|
+
const codeResult = toBase36Lower(value);
|
|
2163
|
+
if (!codeResult.status) {
|
|
2164
|
+
return { status: false, data: codeResult.data };
|
|
2165
|
+
}
|
|
2166
|
+
return { status: true, data: { code: codeResult.data } };
|
|
2167
|
+
} catch (error) {
|
|
2168
|
+
return {
|
|
2169
|
+
status: false,
|
|
2170
|
+
data: { message: "Failed to encode code", error }
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
};
|
|
2174
|
+
var decodeBase36Code = (code, config) => {
|
|
2175
|
+
try {
|
|
2176
|
+
const secretCheck = assertSecret(config?.secret);
|
|
2177
|
+
if (!secretCheck.status) {
|
|
2178
|
+
return { status: false, data: secretCheck.data };
|
|
2179
|
+
}
|
|
2180
|
+
const input = normalizeInput(code);
|
|
2181
|
+
if (!input) {
|
|
2182
|
+
return { status: false, data: { message: "Code is required" } };
|
|
2183
|
+
}
|
|
2184
|
+
if (!isValidBase36(input)) {
|
|
2185
|
+
return {
|
|
2186
|
+
status: false,
|
|
2187
|
+
data: { message: "Code must be base36 (0-9, a-z)" }
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
const parsed = parseBase36BigInt(input);
|
|
2191
|
+
if (!parsed.status) {
|
|
2192
|
+
return { status: false, data: parsed.data };
|
|
2193
|
+
}
|
|
2194
|
+
const params = getParams(config);
|
|
2195
|
+
if (!params.status) {
|
|
2196
|
+
return { status: false, data: params.data };
|
|
2197
|
+
}
|
|
2198
|
+
const {
|
|
2199
|
+
bitSize,
|
|
2200
|
+
rotateBits,
|
|
2201
|
+
addConstant,
|
|
2202
|
+
mask
|
|
2203
|
+
} = params.data;
|
|
2204
|
+
const key = secretToKey(config.secret, mask);
|
|
2205
|
+
let value = parsed.data & mask;
|
|
2206
|
+
value = rotr(value, rotateBits, bitSize, mask);
|
|
2207
|
+
value = value - addConstant & mask;
|
|
2208
|
+
value = value ^ key;
|
|
2209
|
+
const identifierResult = toBase36Lower(value);
|
|
2210
|
+
if (!identifierResult.status) {
|
|
2211
|
+
return { status: false, data: identifierResult.data };
|
|
2212
|
+
}
|
|
2213
|
+
return {
|
|
2214
|
+
status: true,
|
|
2215
|
+
data: { identifier: identifierResult.data.toUpperCase() }
|
|
2216
|
+
};
|
|
2217
|
+
} catch (error) {
|
|
2218
|
+
return {
|
|
2219
|
+
status: false,
|
|
2220
|
+
data: { message: "Failed to decode code", error }
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
|
|
2034
2225
|
// src/index.ts
|
|
2035
2226
|
var isBrowser = typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
2036
2227
|
var isNode = typeof process !== "undefined" && !!process.versions?.node;
|
|
2037
2228
|
|
|
2229
|
+
Object.defineProperty(exports, "uuidv4", {
|
|
2230
|
+
enumerable: true,
|
|
2231
|
+
get: function () { return uuid.v4; }
|
|
2232
|
+
});
|
|
2038
2233
|
exports.addSpaceBetweenNumbers = addSpaceBetweenNumbers;
|
|
2039
2234
|
exports.addThousandsSpace = addThousandsSpace;
|
|
2040
2235
|
exports.areArraysDeepEqualUnordered = areArraysDeepEqualUnordered;
|
|
@@ -2051,6 +2246,7 @@ exports.compact = compact;
|
|
|
2051
2246
|
exports.currencyToSymbol = currencyToSymbol;
|
|
2052
2247
|
exports.debounce = debounce;
|
|
2053
2248
|
exports.deburr = deburr;
|
|
2249
|
+
exports.decodeBase36Code = decodeBase36Code;
|
|
2054
2250
|
exports.decodeObject = decodeObject;
|
|
2055
2251
|
exports.decodeString = decodeString;
|
|
2056
2252
|
exports.deferAfterResponse = deferAfterResponse;
|
|
@@ -2059,6 +2255,7 @@ exports.deferNonCritical = deferNonCritical;
|
|
|
2059
2255
|
exports.deferPostReturn = deferPostReturn;
|
|
2060
2256
|
exports.delay = delay;
|
|
2061
2257
|
exports.difference = difference;
|
|
2258
|
+
exports.encodeBase36Code = encodeBase36Code;
|
|
2062
2259
|
exports.encodeObject = encodeObject;
|
|
2063
2260
|
exports.encodeString = encodeString;
|
|
2064
2261
|
exports.fill = fill;
|