@chainfuse/helpers 2.2.4 → 2.3.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/dist/net.d.mts +2 -1
- package/dist/net.mjs +68 -206
- package/package.json +4 -4
package/dist/net.d.mts
CHANGED
|
@@ -9,7 +9,8 @@ export declare class NetHelpers {
|
|
|
9
9
|
*/
|
|
10
10
|
static initBodyTrimmer(init?: RequestInit): RequestInit;
|
|
11
11
|
static stripSensitiveHeaders(originalHeaders?: Headers): Headers;
|
|
12
|
-
static cfApi
|
|
12
|
+
static cfApi(apiKey: string, logger?: CustomLoging): Promise<import("cloudflare").Cloudflare>;
|
|
13
|
+
private static isRequestLike;
|
|
13
14
|
static loggingFetch(info: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1], body?: boolean, logger?: CustomLoging): Promise<Response>;
|
|
14
15
|
/**
|
|
15
16
|
* Parses the Server-Timing header and returns an object with the metrics.
|
package/dist/net.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { CryptoHelpers } from './crypto.mjs';
|
|
2
1
|
export class NetHelpers {
|
|
3
2
|
/**
|
|
4
3
|
* Removes the `body` property from a RequestInit object to reduce verbosity when logging.
|
|
@@ -14,235 +13,98 @@ export class NetHelpers {
|
|
|
14
13
|
return init;
|
|
15
14
|
}
|
|
16
15
|
static stripSensitiveHeaders(originalHeaders = new Headers()) {
|
|
17
|
-
originalHeaders
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
const mutableHeaders = new Headers(originalHeaders);
|
|
17
|
+
mutableHeaders.delete('Set-Cookie');
|
|
18
|
+
mutableHeaders.delete('Authorization');
|
|
19
|
+
return mutableHeaders;
|
|
20
20
|
}
|
|
21
|
-
static cfApi(apiKey,
|
|
21
|
+
static cfApi(apiKey, logger = false) {
|
|
22
22
|
return import('cloudflare').then(({ Cloudflare }) => new Cloudflare({
|
|
23
23
|
apiToken: apiKey,
|
|
24
|
-
fetch: (info, init) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.catch(() => console.debug('CF Fetch request', cacheKey.url, JSON.stringify(this.initBodyTrimmer({ ...init, headers: Object.fromEntries(this.stripSensitiveHeaders(new Headers(init?.headers)).entries()) }), null, '\t')));
|
|
36
|
-
}
|
|
24
|
+
fetch: async (info, init) => {
|
|
25
|
+
if (typeof logger === 'boolean' && logger) {
|
|
26
|
+
logger = (date, id, methodOrStatus, url, headers, ...rest) => {
|
|
27
|
+
const customUrl = new URL(url);
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
29
|
+
const loggingItems = ['CF Rest', date, id, methodOrStatus, `${customUrl.pathname}${customUrl.search}${customUrl.hash}`, ...rest];
|
|
30
|
+
const customHeaders = new Headers(headers);
|
|
31
|
+
if (customHeaders.has('cf-ray'))
|
|
32
|
+
loggingItems.splice(3, 0, customHeaders.get('cf-ray'));
|
|
33
|
+
console.debug(...loggingItems);
|
|
34
|
+
};
|
|
37
35
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
if (cacheStorageRef || cacheTtl) {
|
|
42
|
-
const cfCacheRef = (cacheStorageRef ?? caches)?.open('cfApi');
|
|
43
|
-
if (cfCacheRef) {
|
|
44
|
-
await cfCacheRef
|
|
45
|
-
.then((cfCache) => cfCache
|
|
46
|
-
.match(cacheKey)
|
|
47
|
-
.then(async (cachedResponse) => {
|
|
48
|
-
if (cachedResponse) {
|
|
49
|
-
if (typeof logger === 'boolean') {
|
|
50
|
-
if (logger) {
|
|
51
|
-
await import('chalk')
|
|
52
|
-
.then(({ Chalk }) => {
|
|
53
|
-
const chalk = new Chalk({ level: 1 });
|
|
54
|
-
console.debug(chalk.green('CF Cache hit'), cacheKey.url);
|
|
55
|
-
})
|
|
56
|
-
.catch(() => console.debug('CF Cache hit', cacheKey.url));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
logger(`CF Cache hit ${cacheKey.url}`);
|
|
61
|
-
}
|
|
62
|
-
if (cachedResponse.status < 500) {
|
|
63
|
-
resolve(cachedResponse);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
void cfCache.delete(cacheKey).then(() => resolve(cachedResponse));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
if (typeof logger === 'boolean') {
|
|
71
|
-
if (logger) {
|
|
72
|
-
await import('chalk')
|
|
73
|
-
.then(({ Chalk }) => {
|
|
74
|
-
const chalk = new Chalk({ level: 1 });
|
|
75
|
-
console.warn(chalk.yellow('CF Cache missed'), cacheKey.url);
|
|
76
|
-
})
|
|
77
|
-
.catch(() => console.warn('CF Cache missed', cacheKey.url));
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
logger(`CF Cache missed ${cacheKey.url}`);
|
|
82
|
-
}
|
|
83
|
-
await this.loggingFetch(info, init, undefined, logger)
|
|
84
|
-
.then((response) => {
|
|
85
|
-
resolve(response.clone());
|
|
86
|
-
return response;
|
|
87
|
-
})
|
|
88
|
-
.then((response) => {
|
|
89
|
-
if (response.status < 500) {
|
|
90
|
-
response = new Response(response.body, response);
|
|
91
|
-
response.headers.set('Cache-Control', `s-maxage=${cacheTtl}`);
|
|
92
|
-
if (response.headers.has('ETag')) {
|
|
93
|
-
return cfCache.put(cacheKey, response).then(async () => {
|
|
94
|
-
if (typeof logger === 'boolean') {
|
|
95
|
-
if (logger) {
|
|
96
|
-
await import('chalk')
|
|
97
|
-
.then(({ Chalk }) => {
|
|
98
|
-
const chalk = new Chalk({ level: 1 });
|
|
99
|
-
console.debug(chalk.gray('CF Cache saved'), cacheKey.url);
|
|
100
|
-
})
|
|
101
|
-
.catch(() => console.debug('CF Cache saved', cacheKey.url));
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
logger(`CF Cache saved ${cacheKey.url}`);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
return (CryptoHelpers.generateETag(response)
|
|
111
|
-
.then((etag) => response.headers.set('ETag', etag))
|
|
112
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
113
|
-
.finally(() => cfCache.put(cacheKey, response).then(async () => {
|
|
114
|
-
if (typeof logger === 'boolean') {
|
|
115
|
-
if (logger) {
|
|
116
|
-
await import('chalk')
|
|
117
|
-
.then(({ Chalk }) => {
|
|
118
|
-
const chalk = new Chalk({ level: 1 });
|
|
119
|
-
console.debug(chalk.gray('CF Cache saved'), cacheKey.url);
|
|
120
|
-
})
|
|
121
|
-
.catch(() => console.debug('CF Cache saved', cacheKey.url));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
logger(`CF Cache saved ${cacheKey.url}`);
|
|
126
|
-
}
|
|
127
|
-
})));
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
.catch(async (error) => {
|
|
137
|
-
if (typeof logger === 'boolean') {
|
|
138
|
-
if (logger) {
|
|
139
|
-
await import('chalk')
|
|
140
|
-
.then(({ Chalk }) => {
|
|
141
|
-
const chalk = new Chalk({ level: 1 });
|
|
142
|
-
console.error(chalk.red('CF Cache match error'), error);
|
|
143
|
-
})
|
|
144
|
-
.catch(() => console.error('CF Cache match error', error));
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
logger(`CF Cache match error ${error}`);
|
|
149
|
-
}
|
|
150
|
-
resolve(this.loggingFetch(info, init, undefined, logger));
|
|
151
|
-
}))
|
|
152
|
-
.catch(async (error) => {
|
|
153
|
-
if (typeof logger === 'boolean') {
|
|
154
|
-
if (logger) {
|
|
155
|
-
await import('chalk')
|
|
156
|
-
.then(({ Chalk }) => {
|
|
157
|
-
const chalk = new Chalk({ level: 1 });
|
|
158
|
-
console.error(chalk.red('CF Cache open error'), error);
|
|
159
|
-
})
|
|
160
|
-
.catch(() => console.error('CF Cache open error', error));
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
logger(`CF Cache open error ${error}`);
|
|
165
|
-
}
|
|
166
|
-
resolve(this.loggingFetch(info, init, undefined, logger));
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
if (typeof logger === 'boolean') {
|
|
171
|
-
if (logger) {
|
|
172
|
-
await import('chalk')
|
|
173
|
-
.then(({ Chalk }) => {
|
|
174
|
-
const chalk = new Chalk({ level: 1 });
|
|
175
|
-
console.warn(chalk.yellow('CF Cache not available'));
|
|
176
|
-
})
|
|
177
|
-
.catch(() => console.warn('CF Cache not available'));
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
logger('CF Cache not available');
|
|
182
|
-
}
|
|
183
|
-
resolve(this.loggingFetch(info, init, undefined, logger));
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
if (typeof logger === 'boolean') {
|
|
188
|
-
if (logger) {
|
|
189
|
-
await import('chalk')
|
|
190
|
-
.then(({ Chalk }) => {
|
|
191
|
-
const chalk = new Chalk({ level: 1 });
|
|
192
|
-
console.warn(chalk.yellow('CF Cache ignored'), cacheKey.url);
|
|
193
|
-
})
|
|
194
|
-
.catch(() => console.warn('CF Cache ignored', cacheKey.url));
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
logger(`CF Cache ignored ${cacheKey.url}`);
|
|
199
|
-
}
|
|
200
|
-
resolve(this.loggingFetch(info, init, undefined, logger));
|
|
201
|
-
}
|
|
202
|
-
}),
|
|
36
|
+
return this.loggingFetch(info, init, undefined, logger);
|
|
37
|
+
},
|
|
203
38
|
}));
|
|
204
39
|
}
|
|
40
|
+
static isRequestLike(obj) {
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
42
|
+
return typeof obj?.url === 'string';
|
|
43
|
+
}
|
|
205
44
|
static loggingFetch(info, init, body = false, logger = false) {
|
|
45
|
+
return import("./crypto.mjs")
|
|
46
|
+
.then(({ CryptoHelpers }) => CryptoHelpers.base62secret(8))
|
|
47
|
+
.then(async (id) => {
|
|
48
|
+
const loggingItems = [new Date().toISOString(), id, init?.method ?? 'GET', this.isRequestLike(info) ? info.url : info.toString(), Object.fromEntries(this.stripSensitiveHeaders(new Headers(init?.headers)).entries())];
|
|
49
|
+
if (body && init?.body) {
|
|
50
|
+
if (new Headers(init.headers).get('Content-Type')?.toLowerCase().startsWith('application/json')) {
|
|
51
|
+
loggingItems.push(JSON.parse(init?.body));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
loggingItems.push(init.body);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (typeof logger === 'boolean') {
|
|
58
|
+
if (logger) {
|
|
59
|
+
await Promise.all([import('chalk'), import("./index.mjs")])
|
|
60
|
+
.then(([{ Chalk }, { Helpers }]) => {
|
|
61
|
+
const chalk = new Chalk({ level: 1 });
|
|
62
|
+
loggingItems.splice(1, 1, chalk.rgb(...Helpers.uniqueIdColor(id))(`[${id}]`));
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
64
|
+
console.debug(...loggingItems);
|
|
65
|
+
})
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
67
|
+
.catch(() => console.debug(...loggingItems));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
logger(...loggingItems);
|
|
72
|
+
}
|
|
73
|
+
return id;
|
|
74
|
+
})
|
|
75
|
+
.then((id) =>
|
|
206
76
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
207
|
-
|
|
77
|
+
new Promise((resolve, reject) => fetch(info, init)
|
|
208
78
|
.then(async (response) => {
|
|
209
|
-
|
|
210
|
-
// Allow mutable headers
|
|
211
|
-
response = new Response(response.body, response);
|
|
212
|
-
const loggingContent = {
|
|
213
|
-
headers: Object.fromEntries(this.stripSensitiveHeaders(response.headers).entries()),
|
|
214
|
-
status: response.status,
|
|
215
|
-
statusText: response.statusText,
|
|
216
|
-
ok: response.ok,
|
|
217
|
-
type: response.type,
|
|
218
|
-
};
|
|
79
|
+
const loggingItems = [new Date().toISOString(), id, response.status, response.url, Object.fromEntries(this.stripSensitiveHeaders(response.headers).entries())];
|
|
219
80
|
if (body) {
|
|
81
|
+
const loggingClone = response.clone();
|
|
220
82
|
if (response.headers.get('Content-Type')?.toLowerCase().startsWith('application/json')) {
|
|
221
|
-
|
|
222
|
-
loggingContent['body'] = await response.json();
|
|
83
|
+
loggingItems.push(await loggingClone.json());
|
|
223
84
|
}
|
|
224
85
|
else {
|
|
225
|
-
|
|
86
|
+
loggingItems.push(await loggingClone.text());
|
|
226
87
|
}
|
|
227
88
|
}
|
|
228
89
|
if (typeof logger === 'boolean') {
|
|
229
90
|
if (logger) {
|
|
230
|
-
await import('chalk')
|
|
231
|
-
.then(({ Chalk }) => {
|
|
91
|
+
await Promise.all([import('chalk'), import("./index.mjs")])
|
|
92
|
+
.then(([{ Chalk }, { Helpers }]) => {
|
|
232
93
|
const chalk = new Chalk({ level: 1 });
|
|
233
|
-
|
|
234
|
-
|
|
94
|
+
loggingItems.splice(1, 1, chalk.rgb(...Helpers.uniqueIdColor(id))(`[${id}]`));
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
96
|
+
console.debug(...loggingItems);
|
|
235
97
|
})
|
|
236
|
-
// eslint-disable-next-line @typescript-eslint/no-
|
|
237
|
-
.catch(() => console.debug(
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
99
|
+
.catch(() => console.debug(...loggingItems));
|
|
238
100
|
}
|
|
239
101
|
}
|
|
240
102
|
else {
|
|
241
|
-
|
|
242
|
-
logger(`Fetch response ${response.ok} ${response.url || info.toString()} ${JSON.stringify(loggingContent, null, '\t')}`);
|
|
103
|
+
logger(...loggingItems);
|
|
243
104
|
}
|
|
105
|
+
resolve(response);
|
|
244
106
|
})
|
|
245
|
-
.catch(reject));
|
|
107
|
+
.catch(reject)));
|
|
246
108
|
}
|
|
247
109
|
/**
|
|
248
110
|
* Parses the Server-Timing header and returns an object with the metrics.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chainfuse/helpers",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "ChainFuse",
|
|
6
6
|
"homepage": "https://github.com/ChainFuse/packages/tree/main/packages/helpers#readme",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"uuid": "^11.1.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@chainfuse/types": "^2.
|
|
58
|
-
"@cloudflare/workers-types": "^4.
|
|
57
|
+
"@chainfuse/types": "^2.5.0",
|
|
58
|
+
"@cloudflare/workers-types": "^4.20250407.0",
|
|
59
59
|
"@types/node": "^22.14.0"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "7ffca16c4f67ac274f440639b665c3b86da35195"
|
|
62
62
|
}
|