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/CHANGELOG.md +53 -0
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/index.d.ts +58 -0
- package/index.js +36 -0
- package/package.json +49 -0
- package/src/async.js +229 -0
- package/src/crypto.js +216 -0
- package/src/errors.js +79 -0
- package/src/formatting.js +299 -0
- package/src/network.js +216 -0
- package/src/transformation.js +273 -0
- package/src/validation.js +403 -0
package/src/network.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Network & HTTP Utilities Module
|
|
5
|
+
* HTTP client helpers, retry logic, and network utilities
|
|
6
|
+
*
|
|
7
|
+
* @module network
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const https = require('https');
|
|
11
|
+
const http = require('http');
|
|
12
|
+
const { URL } = require('url');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Make HTTP/HTTPS request with automatic protocol detection
|
|
16
|
+
*/
|
|
17
|
+
function request(url, options = {}) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const parsedUrl = new URL(url);
|
|
20
|
+
const client = parsedUrl.protocol === 'https:' ? https : http;
|
|
21
|
+
|
|
22
|
+
const req = client.request(url, options, (res) => {
|
|
23
|
+
let data = '';
|
|
24
|
+
res.on('data', (chunk) => data += chunk);
|
|
25
|
+
res.on('end', () => {
|
|
26
|
+
resolve({
|
|
27
|
+
statusCode: res.statusCode,
|
|
28
|
+
headers: res.headers,
|
|
29
|
+
body: data
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
req.on('error', reject);
|
|
35
|
+
|
|
36
|
+
if (options.body) {
|
|
37
|
+
req.write(options.body);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
req.end();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* GET request
|
|
46
|
+
*/
|
|
47
|
+
function get(url, options = {}) {
|
|
48
|
+
return request(url, { ...options, method: 'GET' });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* POST request
|
|
53
|
+
*/
|
|
54
|
+
function post(url, body, options = {}) {
|
|
55
|
+
return request(url,{
|
|
56
|
+
...options,
|
|
57
|
+
method: 'POST',
|
|
58
|
+
body: typeof body === 'string' ? body : JSON.stringify(body)
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Retry a function with exponential backoff
|
|
64
|
+
*/
|
|
65
|
+
async function retry(fn, options = {}) {
|
|
66
|
+
const {
|
|
67
|
+
retries = 3,
|
|
68
|
+
delay = 1000,
|
|
69
|
+
factor = 2,
|
|
70
|
+
maxDelay = 30000
|
|
71
|
+
} = options;
|
|
72
|
+
|
|
73
|
+
let lastError;
|
|
74
|
+
let currentDelay = delay;
|
|
75
|
+
|
|
76
|
+
for (let i = 0; i <= retries; i++) {
|
|
77
|
+
try {
|
|
78
|
+
return await fn();
|
|
79
|
+
} catch (error) {
|
|
80
|
+
lastError = error;
|
|
81
|
+
|
|
82
|
+
if (i < retries) {
|
|
83
|
+
await sleep(currentDelay);
|
|
84
|
+
currentDelay = Math.min(currentDelay * factor, maxDelay);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw lastError;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Sleep helper
|
|
94
|
+
*/
|
|
95
|
+
function sleep(ms) {
|
|
96
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Parse query string
|
|
101
|
+
*/
|
|
102
|
+
function parseQueryString(query) {
|
|
103
|
+
return query
|
|
104
|
+
.replace(/^\?/, '')
|
|
105
|
+
.split('&')
|
|
106
|
+
.filter(Boolean)
|
|
107
|
+
.reduce((acc, pair) => {
|
|
108
|
+
const [key, value] = pair.split('=');
|
|
109
|
+
acc[decodeURIComponent(key)] = decodeURIComponent(value || '');
|
|
110
|
+
return acc;
|
|
111
|
+
}, {});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Build query string
|
|
116
|
+
*/
|
|
117
|
+
function buildQueryString(params) {
|
|
118
|
+
return Object.entries(params)
|
|
119
|
+
.filter(([, value]) => value != null)
|
|
120
|
+
.map(([key, value]) =>
|
|
121
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
122
|
+
)
|
|
123
|
+
.join('&');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Parse URL with validation
|
|
128
|
+
*/
|
|
129
|
+
function parseURL(urlString) {
|
|
130
|
+
try {
|
|
131
|
+
const url = new URL(urlString);
|
|
132
|
+
return {
|
|
133
|
+
protocol: url.protocol,
|
|
134
|
+
hostname: url.hostname,
|
|
135
|
+
port: url.port,
|
|
136
|
+
pathname: url.pathname,
|
|
137
|
+
search: url.search,
|
|
138
|
+
hash: url.hash,
|
|
139
|
+
query: parseQueryString(url.search)
|
|
140
|
+
};
|
|
141
|
+
} catch (error) {
|
|
142
|
+
throw new Error(`Invalid URL: ${urlString}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Check if URL is absolute
|
|
148
|
+
*/
|
|
149
|
+
function isAbsoluteURL(url) {
|
|
150
|
+
return /^https?:\/\//i.test(url);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Join URL paths
|
|
155
|
+
*/
|
|
156
|
+
function joinURL(base, ...paths) {
|
|
157
|
+
const url = base.replace(/\/+$/, '');
|
|
158
|
+
const path = paths
|
|
159
|
+
.map(p => p.replace(/^\/+|\/+$/g, ''))
|
|
160
|
+
.filter(Boolean)
|
|
161
|
+
.join('/');
|
|
162
|
+
return path ? `${url}/${path}` : url;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Timeout wrapper for promises
|
|
167
|
+
*/
|
|
168
|
+
function timeout(promise, ms) {
|
|
169
|
+
return Promise.race([
|
|
170
|
+
promise,
|
|
171
|
+
new Promise((_, reject) =>
|
|
172
|
+
setTimeout(() => reject(new Error('Timeout')), ms)
|
|
173
|
+
)
|
|
174
|
+
]);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Parallel request limiter
|
|
179
|
+
*/
|
|
180
|
+
async function parallel(urls, options = {}) {
|
|
181
|
+
const { concurrency = 5 } = options;
|
|
182
|
+
const results = [];
|
|
183
|
+
const queue = [...urls];
|
|
184
|
+
|
|
185
|
+
async function worker() {
|
|
186
|
+
while (queue.length > 0) {
|
|
187
|
+
const url = queue.shift();
|
|
188
|
+
try {
|
|
189
|
+
const result = await get(url, options);
|
|
190
|
+
results.push({ url, success: true, data: result });
|
|
191
|
+
} catch (error) {
|
|
192
|
+
results.push({ url, success: false, error: error.message });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const workers = Array(concurrency).fill(null).map(() => worker());
|
|
198
|
+
await Promise.all(workers);
|
|
199
|
+
|
|
200
|
+
return results;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
module.exports = {
|
|
204
|
+
request,
|
|
205
|
+
get,
|
|
206
|
+
post,
|
|
207
|
+
retry,
|
|
208
|
+
sleep,
|
|
209
|
+
parseQueryString,
|
|
210
|
+
buildQueryString,
|
|
211
|
+
parseURL,
|
|
212
|
+
isAbsoluteURL,
|
|
213
|
+
joinURL,
|
|
214
|
+
timeout,
|
|
215
|
+
parallel
|
|
216
|
+
};
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Transformation Module
|
|
5
|
+
* Provides functional programming utilities for data transformation
|
|
6
|
+
*
|
|
7
|
+
* @module transformation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Compose functions into a pipeline
|
|
12
|
+
*/
|
|
13
|
+
function pipe(...fns) {
|
|
14
|
+
return (value) => {
|
|
15
|
+
return fns.reduce((acc, fn) => fn(acc), value);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Map transformation
|
|
21
|
+
*/
|
|
22
|
+
function map(fn) {
|
|
23
|
+
return (array) => {
|
|
24
|
+
if (!Array.isArray(array)) {
|
|
25
|
+
throw new Error('map() requires an array');
|
|
26
|
+
}
|
|
27
|
+
return array.map(fn);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Filter transformation
|
|
33
|
+
*/
|
|
34
|
+
function filter(predicate) {
|
|
35
|
+
return (array) => {
|
|
36
|
+
if (!Array.isArray(array)) {
|
|
37
|
+
throw new Error('filter() requires an array');
|
|
38
|
+
}
|
|
39
|
+
return array.filter(predicate);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Reduce transformation
|
|
45
|
+
*/
|
|
46
|
+
function reduce(fn, initial) {
|
|
47
|
+
return (array) => {
|
|
48
|
+
if (!Array.isArray(array)) {
|
|
49
|
+
throw new Error('reduce() requires an array');
|
|
50
|
+
}
|
|
51
|
+
return array.reduce(fn, initial);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Sort transformation
|
|
57
|
+
*/
|
|
58
|
+
function sort(compareFn = null) {
|
|
59
|
+
return (array) => {
|
|
60
|
+
if (!Array.isArray(array)) {
|
|
61
|
+
throw new Error('sort() requires an array');
|
|
62
|
+
}
|
|
63
|
+
const copy = [...array];
|
|
64
|
+
return compareFn ? copy.sort(compareFn) : copy.sort();
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Unique/distinct values
|
|
70
|
+
*/
|
|
71
|
+
function unique() {
|
|
72
|
+
return (array) => {
|
|
73
|
+
if (!Array.isArray(array)) {
|
|
74
|
+
throw new Error('unique() requires an array');
|
|
75
|
+
}
|
|
76
|
+
return [...new Set(array)];
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Chunk array into smaller arrays
|
|
82
|
+
*/
|
|
83
|
+
function chunk(size) {
|
|
84
|
+
return (array) => {
|
|
85
|
+
if (!Array.isArray(array)) {
|
|
86
|
+
throw new Error('chunk() requires an array');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = [];
|
|
90
|
+
for (let i = 0; i < array.length; i += size) {
|
|
91
|
+
result.push(array.slice(i, i + size));
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Flatten nested arrays
|
|
99
|
+
*/
|
|
100
|
+
function flatten(depth = Infinity) {
|
|
101
|
+
return (array) => {
|
|
102
|
+
if (!Array.isArray(array)) {
|
|
103
|
+
throw new Error('flatten() requires an array');
|
|
104
|
+
}
|
|
105
|
+
return array.flat(depth);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Group by key
|
|
111
|
+
*/
|
|
112
|
+
function groupBy(keyFn) {
|
|
113
|
+
return (array) => {
|
|
114
|
+
if (!Array.isArray(array)) {
|
|
115
|
+
throw new Error('groupBy() requires an array');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return array.reduce((acc, item) => {
|
|
119
|
+
const key = keyFn(item);
|
|
120
|
+
if (!acc[key]) {
|
|
121
|
+
acc[key] = [];
|
|
122
|
+
}
|
|
123
|
+
acc[key].push(item);
|
|
124
|
+
return acc;
|
|
125
|
+
}, {});
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Partition array based on predicate
|
|
131
|
+
*/
|
|
132
|
+
function partition(predicate) {
|
|
133
|
+
return (array) => {
|
|
134
|
+
if (!Array.isArray(array)) {
|
|
135
|
+
throw new Error('partition() requires an array');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return array.reduce((acc, item) => {
|
|
139
|
+
acc[predicate(item) ? 0 : 1].push(item);
|
|
140
|
+
return acc;
|
|
141
|
+
}, [[], []]);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Take first N elements
|
|
147
|
+
*/
|
|
148
|
+
function take(n) {
|
|
149
|
+
return (array) => {
|
|
150
|
+
if (!Array.isArray(array)) {
|
|
151
|
+
throw new Error('take() requires an array');
|
|
152
|
+
}
|
|
153
|
+
return array.slice(0, n);
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Drop first N elements
|
|
159
|
+
*/
|
|
160
|
+
function drop(n) {
|
|
161
|
+
return (array) => {
|
|
162
|
+
if (!Array.isArray(array)) {
|
|
163
|
+
throw new Error('drop() requires an array');
|
|
164
|
+
}
|
|
165
|
+
return array.slice(n);
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Zip multiple arrays
|
|
171
|
+
*/
|
|
172
|
+
function zip(...arrays) {
|
|
173
|
+
const maxLength = Math.max(...arrays.map(arr => arr.length));
|
|
174
|
+
const result = [];
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < maxLength; i++) {
|
|
177
|
+
result.push(arrays.map(arr => arr[i]));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Object transformation: pick keys
|
|
185
|
+
*/
|
|
186
|
+
function pick(keys) {
|
|
187
|
+
return (obj) => {
|
|
188
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
189
|
+
throw new Error('pick() requires an object');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return keys.reduce((acc, key) => {
|
|
193
|
+
if (key in obj) {
|
|
194
|
+
acc[key] = obj[key];
|
|
195
|
+
}
|
|
196
|
+
return acc;
|
|
197
|
+
}, {});
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Object transformation: omit keys
|
|
203
|
+
*/
|
|
204
|
+
function omit(keys) {
|
|
205
|
+
return (obj) => {
|
|
206
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
207
|
+
throw new Error('omit() requires an object');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const result = { ...obj };
|
|
211
|
+
keys.forEach(key => delete result[key]);
|
|
212
|
+
return result;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Deep clone
|
|
218
|
+
*/
|
|
219
|
+
function clone(obj) {
|
|
220
|
+
if (obj === null || typeof obj !== 'object') {
|
|
221
|
+
return obj;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (Array.isArray(obj)) {
|
|
225
|
+
return obj.map(clone);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return Object.keys(obj).reduce((acc, key) => {
|
|
229
|
+
acc[key] = clone(obj[key]);
|
|
230
|
+
return acc;
|
|
231
|
+
}, {});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Merge objects deeply
|
|
236
|
+
*/
|
|
237
|
+
function merge(...objects) {
|
|
238
|
+
const result = {};
|
|
239
|
+
|
|
240
|
+
for (const obj of objects) {
|
|
241
|
+
for (const key in obj) {
|
|
242
|
+
if (obj.hasOwnProperty(key)) {
|
|
243
|
+
if (typeof obj[key] === 'object' && !Array.isArray(obj[key]) && obj[key] !== null) {
|
|
244
|
+
result[key] = merge(result[key] || {}, obj[key]);
|
|
245
|
+
} else {
|
|
246
|
+
result[key] = obj[key];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
module.exports = {
|
|
256
|
+
pipe,
|
|
257
|
+
map,
|
|
258
|
+
filter,
|
|
259
|
+
reduce,
|
|
260
|
+
sort,
|
|
261
|
+
unique,
|
|
262
|
+
chunk,
|
|
263
|
+
flatten,
|
|
264
|
+
groupBy,
|
|
265
|
+
partition,
|
|
266
|
+
take,
|
|
267
|
+
drop,
|
|
268
|
+
zip,
|
|
269
|
+
pick,
|
|
270
|
+
omit,
|
|
271
|
+
clone,
|
|
272
|
+
merge
|
|
273
|
+
};
|