cr-static-shared-components 9.9.0

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/src/crypto.js ADDED
@@ -0,0 +1,216 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Cryptography & Hashing Utilities Module
5
+ * Secure hashing, encoding, and basic cryptographic operations
6
+ *
7
+ * @module crypto
8
+ */
9
+
10
+ const crypto = require('crypto');
11
+
12
+ /**
13
+ * Hash data with specified algorithm
14
+ */
15
+ function hash(data, algorithm = 'sha256', encoding = 'hex') {
16
+ return crypto
17
+ .createHash(algorithm)
18
+ .update(data)
19
+ .digest(encoding);
20
+ }
21
+
22
+ /**
23
+ * MD5 hash
24
+ */
25
+ function md5(data) {
26
+ return hash(data, 'md5');
27
+ }
28
+
29
+ /**
30
+ * SHA1 hash
31
+ */
32
+ function sha1(data) {
33
+ return hash(data, 'sha1');
34
+ }
35
+
36
+ /**
37
+ * SHA256 hash
38
+ */
39
+ function sha256(data) {
40
+ return hash(data, 'sha256');
41
+ }
42
+
43
+ /**
44
+ * SHA512 hash
45
+ */
46
+ function sha512(data) {
47
+ return hash(data, 'sha512');
48
+ }
49
+
50
+ /**
51
+ * HMAC with specified algorithm
52
+ */
53
+ function hmac(data, secret, algorithm = 'sha256', encoding = 'hex') {
54
+ return crypto
55
+ .createHmac(algorithm, secret)
56
+ .update(data)
57
+ .digest(encoding);
58
+ }
59
+
60
+ /**
61
+ * Generate random bytes
62
+ */
63
+ function randomBytes(size = 16, encoding = 'hex') {
64
+ return crypto.randomBytes(size).toString(encoding);
65
+ }
66
+
67
+ /**
68
+ * Generate UUID v4
69
+ */
70
+ function uuid() {
71
+ return randomBytes(16, 'hex').replace(
72
+ /^(.{8})(.{4})(.{4})(.{4})(.{12})$/,
73
+ '$1-$2-$3-$4-$5'
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Generate random string
79
+ */
80
+ function randomString(length = 32, charset = 'alphanumeric') {
81
+ const charsets = {
82
+ alphanumeric: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
83
+ alpha: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
84
+ numeric: '0123456789',
85
+ hex: '0123456789abcdef'
86
+ };
87
+
88
+ const chars = charsets[charset] || charsets.alphanumeric;
89
+ let result = '';
90
+
91
+ for (let i = 0; i < length; i++) {
92
+ const randomIndex = crypto.randomInt(0, chars.length);
93
+ result += chars[randomIndex];
94
+ }
95
+
96
+ return result;
97
+ }
98
+
99
+ /**
100
+ * Encrypt data with AES-256-CBC
101
+ */
102
+ function encrypt(data, key) {
103
+ const iv = crypto.randomBytes(16);
104
+ const keyBuffer = crypto.scryptSync(key, 'salt', 32);
105
+ const cipher = crypto.createCipheriv('aes-256-cbc', keyBuffer, iv);
106
+
107
+ let encrypted = cipher.update(data, 'utf8', 'hex');
108
+ encrypted += cipher.final('hex');
109
+
110
+ return `${iv.toString('hex')}:${encrypted}`;
111
+ }
112
+
113
+ /**
114
+ * Decrypt data with AES-256-CBC
115
+ */
116
+ function decrypt(encrypted, key) {
117
+ const [ivHex, encryptedData] = encrypted.split(':');
118
+ const iv = Buffer.from(ivHex, 'hex');
119
+ const keyBuffer = crypto.scryptSync(key, 'salt', 32);
120
+ const decipher = crypto.createDecipheriv('aes-256-cbc', keyBuffer, iv);
121
+
122
+ let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
123
+ decrypted += decipher.final('utf8');
124
+
125
+ return decrypted;
126
+ }
127
+
128
+ /**
129
+ * Base64 encode
130
+ */
131
+ function base64Encode(data) {
132
+ return Buffer.from(data).toString('base64');
133
+ }
134
+
135
+ /**
136
+ * Base64 decode
137
+ */
138
+ function base64Decode(data) {
139
+ return Buffer.from(data, 'base64').toString('utf8');
140
+ }
141
+
142
+ /**
143
+ * URL-safe Base64 encode
144
+ */
145
+ function base64URLEncode(data) {
146
+ return base64Encode(data)
147
+ .replace(/\+/g, '-')
148
+ .replace(/\//g, '_')
149
+ .replace(/=+$/, '');
150
+ }
151
+
152
+ /**
153
+ * URL-safe Base64 decode
154
+ */
155
+ function base64URLDecode(data) {
156
+ let padded = data.replace(/-/g, '+').replace(/_/g, '/');
157
+ while (padded.length % 4) {
158
+ padded += '=';
159
+ }
160
+ return base64Decode(padded);
161
+ }
162
+
163
+ /**
164
+ * Constant-time string comparison
165
+ */
166
+ function timingSafeEqual(a, b) {
167
+ if (a.length !== b.length) {
168
+ return false;
169
+ }
170
+
171
+ const bufA = Buffer.from(a);
172
+ const bufB = Buffer.from(b);
173
+
174
+ return crypto.timingSafeEqual(bufA, bufB);
175
+ }
176
+
177
+ /**
178
+ * Hash password with bcrypt-like algorithm (PBKDF2)
179
+ */
180
+ function hashPassword(password, salt = null, iterations = 100000) {
181
+ const saltBuffer = salt ? Buffer.from(salt, 'hex') : crypto.randomBytes(16);
182
+ const hash = crypto.pbkdf2Sync(password, saltBuffer, iterations, 64, 'sha512');
183
+
184
+ return `${saltBuffer.toString('hex')}:${hash.toString('hex')}`;
185
+ }
186
+
187
+ /**
188
+ * Verify password hash
189
+ */
190
+ function verifyPassword(password, hashedPassword) {
191
+ const [salt, hash] = hashedPassword.split(':');
192
+ const verifyHash = crypto.pbkdf2Sync(password, Buffer.from(salt, 'hex'), 100000, 64, 'sha512');
193
+
194
+ return timingSafeEqual(hash, verifyHash.toString('hex'));
195
+ }
196
+
197
+ module.exports = {
198
+ hash,
199
+ md5,
200
+ sha1,
201
+ sha256,
202
+ sha512,
203
+ hmac,
204
+ randomBytes,
205
+ uuid,
206
+ randomString,
207
+ encrypt,
208
+ decrypt,
209
+ base64Encode,
210
+ base64Decode,
211
+ base64URLEncode,
212
+ base64URLDecode,
213
+ timingSafeEqual,
214
+ hashPassword,
215
+ verifyPassword
216
+ };
package/src/errors.js ADDED
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Error Handling Module
5
+ * Provides structured error types and handling utilities
6
+ *
7
+ * @module errors
8
+ */
9
+
10
+ /**
11
+ * Validation Error
12
+ */
13
+ class ValidationError extends Error {
14
+ constructor(message, errors = []) {
15
+ super(message);
16
+ this.name = 'ValidationError';
17
+ this.errors = errors;
18
+ Error.captureStackTrace(this, ValidationError);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Format Error
24
+ */
25
+ class FormatError extends Error {
26
+ constructor(message, value) {
27
+ super(message);
28
+ this.name = 'FormatError';
29
+ this.value = value;
30
+ Error.captureStackTrace(this, FormatError);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Transform Error
36
+ */
37
+ class TransformError extends Error {
38
+ constructor(message, input) {
39
+ super(message);
40
+ this.name = 'TransformError';
41
+ this.input = input;
42
+ Error.captureStackTrace(this, TransformError);
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Error handler
48
+ */
49
+ function handle(error) {
50
+ const response = {
51
+ code: error.name || 'Error',
52
+ message: error.message
53
+ };
54
+
55
+ if (error instanceof ValidationError) {
56
+ response.errors = error.errors;
57
+ }
58
+
59
+ if (error instanceof FormatError) {
60
+ response.value = error.value;
61
+ }
62
+
63
+ if (error instanceof TransformError) {
64
+ response.input = error.input;
65
+ }
66
+
67
+ if (process.env.NODE_ENV !== 'production') {
68
+ response.stack = error.stack;
69
+ }
70
+
71
+ return response;
72
+ }
73
+
74
+ module.exports = {
75
+ ValidationError,
76
+ FormatError,
77
+ TransformError,
78
+ handle
79
+ };
@@ -0,0 +1,299 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Formatting Module
5
+ * Provides utilities for formatting dates, numbers, currency, and other data types
6
+ *
7
+ * @module formatting
8
+ */
9
+
10
+ /**
11
+ * Date formatting with timezone support
12
+ */
13
+ function date(input, pattern = 'YYYY-MM-DD', timezone = null) {
14
+ const d = input instanceof Date ? input : new Date(input);
15
+
16
+ if (isNaN(d.getTime())) {
17
+ throw new Error('Invalid date input');
18
+ }
19
+
20
+ // Timezone offset adjustment
21
+ let workingDate = new Date(d);
22
+ if (timezone) {
23
+ // Simple timezone offset (real implementation would use Intl.DateTimeFormat)
24
+ const offset = getTimezoneOffset(timezone);
25
+ workingDate = new Date(d.getTime() + offset * 60000);
26
+ }
27
+
28
+ const tokens = {
29
+ 'YYYY': workingDate.getFullYear(),
30
+ 'YY': String(workingDate.getFullYear()).slice(-2),
31
+ 'MM': String(workingDate.getMonth() + 1).padStart(2, '0'),
32
+ 'M': workingDate.getMonth() + 1,
33
+ 'DD': String(workingDate.getDate()).padStart(2, '0'),
34
+ 'D': workingDate.getDate(),
35
+ 'HH': String(workingDate.getHours()).padStart(2, '0'),
36
+ 'H': workingDate.getHours(),
37
+ 'hh': String(workingDate.getHours() % 12 || 12).padStart(2, '0'),
38
+ 'h': workingDate.getHours() % 12 || 12,
39
+ 'mm': String(workingDate.getMinutes()).padStart(2, '0'),
40
+ 'm': workingDate.getMinutes(),
41
+ 'ss': String(workingDate.getSeconds()).padStart(2, '0'),
42
+ 's': workingDate.getSeconds(),
43
+ 'SSS': String(workingDate.getMilliseconds()).padStart(3, '0'),
44
+ 'A': workingDate.getHours() >= 12 ? 'PM' : 'AM',
45
+ 'a': workingDate.getHours() >= 12 ? 'pm' : 'am'
46
+ };
47
+
48
+ let formatted = pattern;
49
+ for (const [token, value] of Object.entries(tokens)) {
50
+ formatted = formatted.replace(new RegExp(token, 'g'), String(value));
51
+ }
52
+
53
+ return formatted;
54
+ }
55
+
56
+ /**
57
+ * Get timezone offset (simplified)
58
+ */
59
+ function getTimezoneOffset(timezone) {
60
+ // Simplified mapping - real implementation would use proper timezone database
61
+ const offsets = {
62
+ 'UTC': 0,
63
+ 'America/New_York': -300,
64
+ 'America/Chicago': -360,
65
+ 'America/Denver': -420,
66
+ 'America/Los_Angeles': -480,
67
+ 'Europe/London': 0,
68
+ 'Europe/Paris': 60,
69
+ 'Asia/Tokyo': 540,
70
+ 'Australia/Sydney': 660
71
+ };
72
+
73
+ return offsets[timezone] || 0;
74
+ }
75
+
76
+ /**
77
+ * Currency formatting
78
+ */
79
+ function currency(amount, currencyCode = 'USD', locale = 'en-US') {
80
+ if (typeof amount !== 'number' || isNaN(amount)) {
81
+ throw new Error('Invalid amount');
82
+ }
83
+
84
+ try {
85
+ return new Intl.NumberFormat(locale, {
86
+ style: 'currency',
87
+ currency: currencyCode
88
+ }).format(amount);
89
+ } catch (e) {
90
+ // Fallback
91
+ const symbols = {
92
+ 'USD': '$',
93
+ 'EUR': '€',
94
+ 'GBP': '£',
95
+ 'JPY': '¥',
96
+ 'CNY': '¥'
97
+ };
98
+
99
+ const symbol = symbols[currencyCode] || currencyCode;
100
+ return `${symbol}${amount.toFixed(2)}`;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Number formatting
106
+ */
107
+ function number(value, options = {}) {
108
+ if (typeof value !== 'number' || isNaN(value)) {
109
+ throw new Error('Invalid number');
110
+ }
111
+
112
+ const defaults = {
113
+ precision: 2,
114
+ grouping: true,
115
+ percentage: false
116
+ };
117
+
118
+ const opts = { ...defaults, ...options };
119
+
120
+ let formatted = value;
121
+
122
+ if (opts.percentage) {
123
+ formatted = value * 100;
124
+ }
125
+
126
+ if (opts.precision !== null) {
127
+ formatted = formatted.toFixed(opts.precision);
128
+ }
129
+
130
+ if (opts.grouping) {
131
+ const parts = String(formatted).split('.');
132
+ parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
133
+ formatted = parts.join('.');
134
+ }
135
+
136
+ if (opts.percentage) {
137
+ formatted += '%';
138
+ }
139
+
140
+ return formatted;
141
+ }
142
+
143
+ /**
144
+ * Bytes formatting
145
+ */
146
+ function bytes(bytesValue, decimals = 2) {
147
+ if (typeof bytesValue !== 'number' || bytesValue < 0) {
148
+ throw new Error('Invalid bytes value');
149
+ }
150
+
151
+ if (bytesValue === 0) return '0 Bytes';
152
+
153
+ const k = 1024;
154
+ const dm = decimals < 0 ? 0 : decimals;
155
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
156
+
157
+ const i = Math.floor(Math.log(bytesValue) / Math.log(k));
158
+
159
+ return parseFloat((bytesValue / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
160
+ }
161
+
162
+ /**
163
+ * Percentage formatting
164
+ */
165
+ function percentage(value, decimals = 2) {
166
+ if (typeof value !== 'number' || isNaN(value)) {
167
+ throw new Error('Invalid percentage value');
168
+ }
169
+
170
+ return (value * 100).toFixed(decimals) + '%';
171
+ }
172
+
173
+ /**
174
+ * Ordinal number formatting (1st, 2nd, 3rd, etc.)
175
+ */
176
+ function ordinal(value) {
177
+ if (typeof value !== 'number' || !Number.isInteger(value)) {
178
+ throw new Error('Invalid ordinal value');
179
+ }
180
+
181
+ const suffixes = ['th', 'st', 'nd', 'rd'];
182
+ const v = value % 100;
183
+
184
+ return value + (suffixes[(v - 20) % 10] || suffixes[v] || suffixes[0]);
185
+ }
186
+
187
+ /**
188
+ * Duration formatting (milliseconds to human-readable)
189
+ */
190
+ function duration(ms) {
191
+ if (typeof ms !== 'number' || ms < 0) {
192
+ throw new Error('Invalid duration');
193
+ }
194
+
195
+ const units = {
196
+ day: 86400000,
197
+ hour: 3600000,
198
+ minute: 60000,
199
+ second: 1000,
200
+ millisecond: 1
201
+ };
202
+
203
+ const parts = [];
204
+ let remaining = ms;
205
+
206
+ for (const [unit, value] of Object.entries(units)) {
207
+ const count = Math.floor(remaining / value);
208
+ if (count > 0) {
209
+ parts.push(`${count} ${unit}${count > 1 ? 's' : ''}`);
210
+ remaining %= value;
211
+ }
212
+ }
213
+
214
+ return parts.length > 0 ? parts.join(', ') : '0 milliseconds';
215
+ }
216
+
217
+ /**
218
+ * Pluralize words
219
+ */
220
+ function pluralize(count, singular, plural = null) {
221
+ if (typeof count !== 'number') {
222
+ throw new Error('Invalid count');
223
+ }
224
+
225
+ const word = count === 1 ? singular : (plural || singular + 's');
226
+ return `${count} ${word}`;
227
+ }
228
+
229
+ /**
230
+ * Titlecase formatting
231
+ */
232
+ function titleCase(str) {
233
+ if (typeof str !== 'string') {
234
+ throw new Error('Invalid string');
235
+ }
236
+
237
+ const smallWords = ['a', 'an', 'and', 'as', 'at', 'but', 'by', 'for', 'if', 'in', 'of', 'on', 'or', 'the', 'to', 'via'];
238
+
239
+ return str.toLowerCase().split(' ').map((word, index) => {
240
+ if (index === 0 || !smallWords.includes(word)) {
241
+ return word.charAt(0).toUpperCase() + word.slice(1);
242
+ }
243
+ return word;
244
+ }).join(' ');
245
+ }
246
+
247
+ /**
248
+ * Truncate string with ellipsis
249
+ */
250
+ function truncate(str, maxLength, suffix = '...') {
251
+ if (typeof str !== 'string') {
252
+ throw new Error('Invalid string');
253
+ }
254
+
255
+ if (str.length <= maxLength) {
256
+ return str;
257
+ }
258
+
259
+ return str.slice(0, maxLength - suffix.length) + suffix;
260
+ }
261
+
262
+ /**
263
+ * Pad string
264
+ */
265
+ function pad(str, length, char = ' ', direction = 'right') {
266
+ if (typeof str !== 'string') {
267
+ str = String(str);
268
+ }
269
+
270
+ if (str.length >= length) {
271
+ return str;
272
+ }
273
+
274
+ const padding = char.repeat(length - str.length);
275
+
276
+ if (direction === 'left') {
277
+ return padding + str;
278
+ } else if (direction === 'center') {
279
+ const leftPad = Math.floor(padding.length / 2);
280
+ const rightPad = padding.length - leftPad;
281
+ return char.repeat(leftPad) + str + char.repeat(rightPad);
282
+ }
283
+
284
+ return str + padding;
285
+ }
286
+
287
+ module.exports = {
288
+ date,
289
+ currency,
290
+ number,
291
+ bytes,
292
+ percentage,
293
+ ordinal,
294
+ duration,
295
+ pluralize,
296
+ titleCase,
297
+ truncate,
298
+ pad
299
+ };