@stacksjs/strings 0.70.88 → 0.70.91

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/dist/utils.js ADDED
@@ -0,0 +1,46 @@
1
+ import { slugify } from "./slug";
2
+ export const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
3
+ export function slash(str) {
4
+ return str.replace(/\\/g, "/");
5
+ }
6
+ export function ensurePrefix(prefix, str) {
7
+ if (!str.startsWith(prefix))
8
+ return prefix + str;
9
+ return str;
10
+ }
11
+ export function ensureSuffix(suffix, str) {
12
+ return str.endsWith(suffix) ? str : str + suffix;
13
+ }
14
+ export function template(str, ...args) {
15
+ return str.replace(/\{(\d+)\}/g, (match, key) => {
16
+ const index = Number(key);
17
+ return Number.isNaN(index) || args[index] === void 0 ? match : args[index];
18
+ });
19
+ }
20
+ export function truncate(str, length, end = "...") {
21
+ if (str.length <= length)
22
+ return str;
23
+ return str.slice(0, length - end.length) + end;
24
+ }
25
+ export function random(size = 16, dict = urlAlphabet) {
26
+ const len = dict.length, g = globalThis;
27
+ if (!g.crypto?.getRandomValues)
28
+ throw Error("[strings.random] crypto.getRandomValues is not available; cannot generate secure random string.");
29
+ const bytes = new Uint8Array(size);
30
+ g.crypto.getRandomValues(bytes);
31
+ let id = "";
32
+ for (let i = 0;i < size; i++)
33
+ id += dict[(bytes[i] ?? 0) % len];
34
+ return id;
35
+ }
36
+ export function slug(str, options) {
37
+ if (options)
38
+ return slugify(str, options);
39
+ return slugify(str, {
40
+ lower: !0,
41
+ strict: !0
42
+ });
43
+ }
44
+
45
+ export * from "./detect-indent";
46
+ export * from "./detect-newline";
@@ -0,0 +1,170 @@
1
+ import type { HashAlgorithm } from '@stacksjs/types';
2
+ /**
3
+ * Email validation
4
+ */
5
+ export declare function isEmail(email: string): boolean;
6
+ /**
7
+ * Strong password validation
8
+ * Must contain: min 8 chars, uppercase, lowercase, number, symbol
9
+ */
10
+ export declare function isStrongPassword(password: string): boolean;
11
+ /**
12
+ * Alphanumeric validation (letters and numbers only)
13
+ */
14
+ export declare function isAlphanumeric(str: string): boolean;
15
+ /**
16
+ * URL validation
17
+ */
18
+ export declare function isURL(url: string): boolean;
19
+ /**
20
+ * Mobile phone validation (international format)
21
+ */
22
+ export declare function isMobilePhone(phoneNumber: string): boolean;
23
+ /**
24
+ * Alphabetic characters only
25
+ */
26
+ export declare function isAlpha(str: string): boolean;
27
+ /**
28
+ * Postal code validation (supports multiple formats)
29
+ */
30
+ export declare function isPostalCode(zipCode: string): boolean;
31
+ /**
32
+ * Numeric string validation
33
+ */
34
+ export declare function isNumeric(str: string): boolean;
35
+ /**
36
+ * Hex color validation
37
+ */
38
+ export declare function isHexColor(color: string): boolean;
39
+ /**
40
+ * Hexadecimal validation
41
+ */
42
+ export declare function isHexadecimal(hex: string): boolean;
43
+ /**
44
+ * Base64 validation
45
+ */
46
+ export declare function isBase64(base64: string): boolean;
47
+ /**
48
+ * UUID validation (v1-v5)
49
+ */
50
+ export declare function isUUID(uuid: string): boolean;
51
+ /**
52
+ * JSON validation
53
+ */
54
+ export declare function isJSON(json: string): boolean;
55
+ /**
56
+ * Credit card validation (Luhn algorithm)
57
+ */
58
+ export declare function isCreditCard(creditCard: string): boolean;
59
+ /**
60
+ * ISBN validation (ISBN-10 and ISBN-13)
61
+ */
62
+ export declare function isISBN(isbn: string): boolean;
63
+ /**
64
+ * IP address validation (IPv4 and IPv6)
65
+ */
66
+ export declare function isIP(ip: string): boolean;
67
+ /**
68
+ * IP range validation (CIDR notation)
69
+ */
70
+ export declare function isIPRange(ip: string): boolean;
71
+ /**
72
+ * MAC address validation
73
+ */
74
+ export declare function isMACAddress(macAddress: string): boolean;
75
+ /**
76
+ * Latitude/Longitude validation
77
+ */
78
+ export declare function isLatLong(latlong: string): boolean;
79
+ /**
80
+ * Currency validation
81
+ */
82
+ export declare function isCurrency(currency: string): boolean;
83
+ /**
84
+ * Data URI validation
85
+ */
86
+ export declare function isDataURI(dataURI: string): boolean;
87
+ /**
88
+ * MIME type validation
89
+ */
90
+ export declare function isMimeType(mimeType: string): boolean;
91
+ /**
92
+ * JWT validation
93
+ */
94
+ export declare function isJWT(jwt: string): boolean;
95
+ /**
96
+ * ASCII validation
97
+ */
98
+ export declare function isAscii(ascii: string): boolean;
99
+ /**
100
+ * Base32 validation
101
+ */
102
+ export declare function isBase32(base32: string): boolean;
103
+ /**
104
+ * Byte length validation
105
+ */
106
+ export declare function isByteLength(str: string, options?: { min?: number, max?: number }): boolean;
107
+ /**
108
+ * FQDN validation (Fully Qualified Domain Name)
109
+ */
110
+ export declare function isFQDN(fqdn: string): boolean;
111
+ /**
112
+ * Full width character validation
113
+ */
114
+ export declare function isFullWidth(fullWidth: string): boolean;
115
+ /**
116
+ * Half width character validation
117
+ */
118
+ export declare function isHalfWidth(halfWidth: string): boolean;
119
+ /**
120
+ * Hash validation for various algorithms
121
+ */
122
+ export declare function isHash(hash: string, algorithm: HashAlgorithm): boolean;
123
+ /**
124
+ * HSL color validation
125
+ */
126
+ export declare function isHSL(hsl: string): boolean;
127
+ /**
128
+ * IBAN validation
129
+ */
130
+ export declare function isIBAN(iban: string): boolean;
131
+ /**
132
+ * Identity card validation
133
+ */
134
+ export declare function isIdentityCard(identityCard: string): boolean;
135
+ /**
136
+ * ISIN validation
137
+ */
138
+ export declare function isISIN(isin: string): boolean;
139
+ /**
140
+ * ISO 8601 date validation
141
+ */
142
+ export declare function isISO8601(iso8601: string): boolean;
143
+ /**
144
+ * ISRC validation
145
+ */
146
+ export declare function isISRC(isrc: string): boolean;
147
+ /**
148
+ * ISSN validation
149
+ */
150
+ export declare function isISSN(issn: string): boolean;
151
+ /**
152
+ * ISO 3166-1 alpha-2 country code validation
153
+ */
154
+ export declare function isISO31661Alpha2(iso31661Alpha2: string): boolean;
155
+ /**
156
+ * ISO 3166-1 alpha-3 country code validation
157
+ */
158
+ export declare function isISO31661Alpha3(iso31661Alpha3: string): boolean;
159
+ /**
160
+ * Username validation
161
+ */
162
+ export declare function validateUsername(username: string): boolean;
163
+ /**
164
+ * Latitude validation
165
+ */
166
+ export declare function isLatitude(latitude: string): boolean;
167
+ /**
168
+ * Longitude validation
169
+ */
170
+ export declare function isLongitude(longitude: string): boolean;
@@ -0,0 +1,272 @@
1
+ export function isEmail(email) {
2
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
3
+ }
4
+ export function isStrongPassword(password) {
5
+ if (password.length < 8)
6
+ return !1;
7
+ const hasUpperCase = /[A-Z]/.test(password), hasLowerCase = /[a-z]/.test(password), hasNumber = /\d/.test(password), hasSymbol = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password);
8
+ return hasUpperCase && hasLowerCase && hasNumber && hasSymbol;
9
+ }
10
+ export function isAlphanumeric(str) {
11
+ return /^[a-zA-Z0-9]+$/.test(str);
12
+ }
13
+ export function isURL(url) {
14
+ try {
15
+ const parsed = new URL(url);
16
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
17
+ } catch {
18
+ return !1;
19
+ }
20
+ }
21
+ export function isMobilePhone(phoneNumber) {
22
+ const phoneRegex = /^\+?[1-9]\d{6,14}$/, cleaned = phoneNumber.replace(/[\s()-]/g, "");
23
+ return phoneRegex.test(cleaned);
24
+ }
25
+ export function isAlpha(str) {
26
+ return /^[a-zA-Z]+$/.test(str);
27
+ }
28
+ export function isPostalCode(zipCode) {
29
+ const usZip = /^\d{5}(?:-\d{4})?$/, ukPostcode = /^[A-Z]{1,2}\d{1,2}[A-Z]?\s?\d[A-Z]{2}$/i, canadaPostcode = /^[A-Z]\d[A-Z]\s?\d[A-Z]\d$/i, generic = /^[a-zA-Z0-9]{3,10}$/;
30
+ return usZip.test(zipCode) || ukPostcode.test(zipCode) || canadaPostcode.test(zipCode) || generic.test(zipCode);
31
+ }
32
+ export function isNumeric(str) {
33
+ return /^\d+$/.test(str);
34
+ }
35
+ export function isHexColor(color) {
36
+ return /^#(?:[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color);
37
+ }
38
+ export function isHexadecimal(hex) {
39
+ return /^[A-Fa-f0-9]+$/.test(hex);
40
+ }
41
+ export function isBase64(base64) {
42
+ try {
43
+ return btoa(atob(base64)) === base64;
44
+ } catch {
45
+ return !1;
46
+ }
47
+ }
48
+ export function isUUID(uuid) {
49
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);
50
+ }
51
+ export function isJSON(json) {
52
+ try {
53
+ JSON.parse(json);
54
+ return !0;
55
+ } catch {
56
+ return !1;
57
+ }
58
+ }
59
+ export function isCreditCard(creditCard) {
60
+ const cleaned = creditCard.replace(/[\s-]/g, "");
61
+ if (!/^\d{13,19}$/.test(cleaned))
62
+ return !1;
63
+ let sum = 0, isEven = !1;
64
+ for (let i = cleaned.length - 1;i >= 0; i--) {
65
+ let digit = parseInt(cleaned.charAt(i), 10);
66
+ if (isEven) {
67
+ digit *= 2;
68
+ if (digit > 9)
69
+ digit -= 9;
70
+ }
71
+ sum += digit;
72
+ isEven = !isEven;
73
+ }
74
+ return sum % 10 === 0;
75
+ }
76
+ export function isISBN(isbn) {
77
+ const cleaned = isbn.replace(/[\s-]/g, "");
78
+ if (cleaned.length === 10) {
79
+ let sum = 0;
80
+ for (let i = 0;i < 9; i++) {
81
+ const digit = parseInt(cleaned.charAt(i), 10);
82
+ if (isNaN(digit))
83
+ return !1;
84
+ sum += digit * (10 - i);
85
+ }
86
+ const checkChar = cleaned.charAt(9), checkDigit = checkChar === "X" ? 10 : parseInt(checkChar, 10);
87
+ if (isNaN(checkDigit) && checkChar !== "X")
88
+ return !1;
89
+ sum += checkDigit;
90
+ return sum % 11 === 0;
91
+ }
92
+ if (cleaned.length === 13) {
93
+ let sum = 0;
94
+ for (let i = 0;i < 12; i++) {
95
+ const digit = parseInt(cleaned.charAt(i), 10);
96
+ if (isNaN(digit))
97
+ return !1;
98
+ sum += digit * (i % 2 === 0 ? 1 : 3);
99
+ }
100
+ const checkDigit = parseInt(cleaned.charAt(12), 10);
101
+ if (isNaN(checkDigit))
102
+ return !1;
103
+ return (10 - sum % 10) % 10 === checkDigit;
104
+ }
105
+ return !1;
106
+ }
107
+ export function isIP(ip) {
108
+ if (/^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ip))
109
+ return !0;
110
+ return /^(?:(?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/.test(ip);
111
+ }
112
+ export function isIPRange(ip) {
113
+ const parts = ip.split("/");
114
+ if (parts.length !== 2)
115
+ return !1;
116
+ const [address, cidr] = parts;
117
+ if (address === void 0 || cidr === void 0)
118
+ return !1;
119
+ const cidrNum = parseInt(cidr, 10);
120
+ if (!isIP(address))
121
+ return !1;
122
+ if (address.includes(":"))
123
+ return cidrNum >= 0 && cidrNum <= 128;
124
+ else
125
+ return cidrNum >= 0 && cidrNum <= 32;
126
+ }
127
+ export function isMACAddress(macAddress) {
128
+ return /^(?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2})$/.test(macAddress);
129
+ }
130
+ export function isLatLong(latlong) {
131
+ const parts = latlong.split(",");
132
+ if (parts.length !== 2)
133
+ return !1;
134
+ const lat = parseFloat((parts[0] ?? "").trim()), long = parseFloat((parts[1] ?? "").trim());
135
+ return !isNaN(lat) && !isNaN(long) && lat >= -90 && lat <= 90 && long >= -180 && long <= 180;
136
+ }
137
+ export function isCurrency(currency) {
138
+ return /^[$\u00A3\u20AC\u00A5]?\d{1,3}(?:,?\d{3})*(?:\.\d{2})?$/.test(currency.trim());
139
+ }
140
+ export function isDataURI(dataURI) {
141
+ return /^data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)*)?;base64,(?:[a-z0-9+/]+=*)/i.test(dataURI) || /^data:,/.test(dataURI);
142
+ }
143
+ export function isMimeType(mimeType) {
144
+ return /^[a-z]+\/[a-z0-9\-+.]+$/i.test(mimeType);
145
+ }
146
+ export function isJWT(jwt) {
147
+ const parts = jwt.split(".");
148
+ if (parts.length !== 3)
149
+ return !1;
150
+ try {
151
+ parts.forEach((part) => {
152
+ atob(part.replace(/-/g, "+").replace(/_/g, "/"));
153
+ });
154
+ return !0;
155
+ } catch {
156
+ return !1;
157
+ }
158
+ }
159
+ export function isAscii(ascii) {
160
+ return /^[\x00-\x7F]*$/.test(ascii);
161
+ }
162
+ export function isBase32(base32) {
163
+ return /^[A-Z2-7]+=*$/.test(base32.toUpperCase());
164
+ }
165
+ export function isByteLength(str, options) {
166
+ const min = options?.min ?? 0, max = options?.max ?? 1 / 0, byteLength = new TextEncoder().encode(str).length;
167
+ return byteLength >= min && byteLength <= max;
168
+ }
169
+ export function isFQDN(fqdn) {
170
+ return /^(?=.{1,253}$)(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,}$/.test(fqdn);
171
+ }
172
+ export function isFullWidth(fullWidth) {
173
+ return /^[\uFF00-\uFFEF]+$/.test(fullWidth);
174
+ }
175
+ export function isHalfWidth(halfWidth) {
176
+ return /^[\u0020-\u007E\uFF61-\uFF9F]+$/.test(halfWidth);
177
+ }
178
+ export function isHash(hash, algorithm) {
179
+ const expectedLength = {
180
+ md5: 32,
181
+ sha1: 40,
182
+ sha256: 64,
183
+ sha384: 96,
184
+ sha512: 128
185
+ }[algorithm];
186
+ if (!expectedLength)
187
+ return !1;
188
+ return hash.length === expectedLength && /^[a-f0-9]+$/i.test(hash);
189
+ }
190
+ export function isHSL(hsl) {
191
+ return /^hsl\(?:\s*(?:\d+)\s*,\s*(?:\d+(?:\.\d+)?%)\s*,\s*(?:\d+(?:\.\d+)?%)\s*\)$/.test(hsl);
192
+ }
193
+ export function isIBAN(iban) {
194
+ const cleaned = iban.replace(/\s/g, "").toUpperCase();
195
+ if (!/^[A-Z]{2}\d{2}[A-Z0-9]+$/.test(cleaned))
196
+ return !1;
197
+ if (cleaned.length < 15 || cleaned.length > 34)
198
+ return !1;
199
+ const digits = (cleaned.slice(4) + cleaned.slice(0, 4)).split("").map((char) => {
200
+ const code = char.charCodeAt(0);
201
+ return code >= 65 && code <= 90 ? (code - 55).toString() : char;
202
+ }).join("");
203
+ let remainder = digits.slice(0, 2);
204
+ for (let i = 2;i < digits.length; i += 7)
205
+ remainder = (parseInt(remainder + digits.slice(i, i + 7), 10) % 97).toString();
206
+ return parseInt(remainder, 10) === 1;
207
+ }
208
+ export function isIdentityCard(identityCard) {
209
+ const cleaned = identityCard.replace(/[\s-]/g, "");
210
+ return /^[A-Z0-9]{5,20}$/i.test(cleaned);
211
+ }
212
+ export function isISIN(isin) {
213
+ if (!/^[A-Z]{2}[A-Z0-9]{9}\d$/.test(isin))
214
+ return !1;
215
+ const digits = isin.split("").map((char) => {
216
+ const code = char.charCodeAt(0);
217
+ return code >= 65 && code <= 90 ? (code - 55).toString() : char;
218
+ }).join("");
219
+ let sum = 0, isEven = !0;
220
+ for (let i = digits.length - 1;i >= 0; i--) {
221
+ let digit = parseInt(digits.charAt(i), 10);
222
+ if (isEven) {
223
+ digit *= 2;
224
+ if (digit > 9)
225
+ digit -= 9;
226
+ }
227
+ sum += digit;
228
+ isEven = !isEven;
229
+ }
230
+ return sum % 10 === 0;
231
+ }
232
+ export function isISO8601(iso8601) {
233
+ if (!/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d{3})?(?:Z|[+-]\d{2}:\d{2})?)?$/.test(iso8601))
234
+ return !1;
235
+ try {
236
+ const date = new Date(iso8601);
237
+ return !isNaN(date.getTime());
238
+ } catch {
239
+ return !1;
240
+ }
241
+ }
242
+ export function isISRC(isrc) {
243
+ return /^[A-Z]{2}[A-Z0-9]{3}\d{2}\d{5}$/.test(isrc.replace(/-/g, ""));
244
+ }
245
+ export function isISSN(issn) {
246
+ const cleaned = issn.replace(/[\s-]/g, "");
247
+ if (!/^\d{7}[\dX]$/.test(cleaned))
248
+ return !1;
249
+ let sum = 0;
250
+ for (let i = 0;i < 7; i++)
251
+ sum += parseInt(cleaned.charAt(i), 10) * (8 - i);
252
+ const checkChar = cleaned.charAt(7), checkDigit = checkChar === "X" ? 10 : parseInt(checkChar, 10);
253
+ sum += checkDigit;
254
+ return sum % 11 === 0;
255
+ }
256
+ export function isISO31661Alpha2(iso31661Alpha2) {
257
+ return /^[A-Z]{2}$/.test(iso31661Alpha2);
258
+ }
259
+ export function isISO31661Alpha3(iso31661Alpha3) {
260
+ return /^[A-Z]{3}$/.test(iso31661Alpha3);
261
+ }
262
+ export function validateUsername(username) {
263
+ return isAlphanumeric(username);
264
+ }
265
+ export function isLatitude(latitude) {
266
+ const lat = parseFloat(latitude);
267
+ return !isNaN(lat) && lat >= -90 && lat <= 90;
268
+ }
269
+ export function isLongitude(longitude) {
270
+ const long = parseFloat(longitude);
271
+ return !isNaN(long) && long >= -180 && long <= 180;
272
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/strings",
3
3
  "type": "module",
4
- "version": "0.70.88",
4
+ "version": "0.70.91",
5
5
  "description": "The Stacks string utilities.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -64,9 +64,9 @@
64
64
  "prepublishOnly": "bun run build"
65
65
  },
66
66
  "devDependencies": {
67
- "@stacksjs/alias": "0.70.88",
67
+ "@stacksjs/alias": "0.70.91",
68
68
  "better-dx": "^0.2.16",
69
- "@stacksjs/types": "0.70.88"
69
+ "@stacksjs/types": "0.70.91"
70
70
  },
71
71
  "sideEffects": false
72
72
  }