@chainfuse/helpers 2.2.3 → 2.3.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/dist/net.d.mts +2 -1
- package/dist/net.mjs +61 -206
- package/package.json +5 -5
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,91 @@ 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) => {
|
|
27
|
+
const customUrl = new URL(url);
|
|
28
|
+
const loggingItems = ['CF Rest', date, id, methodOrStatus, `${customUrl.pathname}${customUrl.search}${customUrl.hash}`];
|
|
29
|
+
const customHeaders = new Headers(headers);
|
|
30
|
+
if (customHeaders.has('cf-ray'))
|
|
31
|
+
loggingItems.splice(3, 0, customHeaders.get('cf-ray'));
|
|
32
|
+
console.debug(...loggingItems);
|
|
33
|
+
};
|
|
37
34
|
}
|
|
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
|
-
}),
|
|
35
|
+
return this.loggingFetch(info, init, undefined, logger);
|
|
36
|
+
},
|
|
203
37
|
}));
|
|
204
38
|
}
|
|
39
|
+
static isRequestLike(obj) {
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
41
|
+
return typeof obj?.url === 'string';
|
|
42
|
+
}
|
|
205
43
|
static loggingFetch(info, init, body = false, logger = false) {
|
|
44
|
+
return import("./crypto.mjs")
|
|
45
|
+
.then(({ CryptoHelpers }) => CryptoHelpers.base62secret(8))
|
|
46
|
+
.then(async (id) => {
|
|
47
|
+
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())];
|
|
48
|
+
if (body)
|
|
49
|
+
loggingItems.push(this.initBodyTrimmer(init));
|
|
50
|
+
if (typeof logger === 'boolean') {
|
|
51
|
+
if (logger) {
|
|
52
|
+
await Promise.all([import('chalk'), import("./index.mjs")])
|
|
53
|
+
.then(([{ Chalk }, { Helpers }]) => {
|
|
54
|
+
const chalk = new Chalk({ level: 1 });
|
|
55
|
+
loggingItems.splice(1, 1, chalk.rgb(...Helpers.uniqueIdColor(id))(`[${id}]`));
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
57
|
+
console.debug(...loggingItems);
|
|
58
|
+
})
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
60
|
+
.catch(() => console.debug(...loggingItems));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
logger(...loggingItems);
|
|
65
|
+
}
|
|
66
|
+
return id;
|
|
67
|
+
})
|
|
68
|
+
.then((id) =>
|
|
206
69
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
207
|
-
|
|
70
|
+
new Promise((resolve, reject) => fetch(info, init)
|
|
208
71
|
.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
|
-
};
|
|
72
|
+
const loggingItems = [new Date().toISOString(), id, response.status, response.url, Object.fromEntries(this.stripSensitiveHeaders(response.headers).entries())];
|
|
219
73
|
if (body) {
|
|
74
|
+
const loggingClone = response.clone();
|
|
220
75
|
if (response.headers.get('Content-Type')?.toLowerCase().startsWith('application/json')) {
|
|
221
|
-
|
|
222
|
-
loggingContent['body'] = await response.json();
|
|
76
|
+
loggingItems.push(await loggingClone.json());
|
|
223
77
|
}
|
|
224
78
|
else {
|
|
225
|
-
|
|
79
|
+
loggingItems.push(await loggingClone.text());
|
|
226
80
|
}
|
|
227
81
|
}
|
|
228
82
|
if (typeof logger === 'boolean') {
|
|
229
83
|
if (logger) {
|
|
230
|
-
await import('chalk')
|
|
231
|
-
.then(({ Chalk }) => {
|
|
84
|
+
await Promise.all([import('chalk'), import("./index.mjs")])
|
|
85
|
+
.then(([{ Chalk }, { Helpers }]) => {
|
|
232
86
|
const chalk = new Chalk({ level: 1 });
|
|
233
|
-
|
|
234
|
-
|
|
87
|
+
loggingItems.splice(1, 1, chalk.rgb(...Helpers.uniqueIdColor(id))(`[${id}]`));
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
89
|
+
console.debug(...loggingItems);
|
|
235
90
|
})
|
|
236
|
-
// eslint-disable-next-line @typescript-eslint/no-
|
|
237
|
-
.catch(() => console.debug(
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
92
|
+
.catch(() => console.debug(...loggingItems));
|
|
238
93
|
}
|
|
239
94
|
}
|
|
240
95
|
else {
|
|
241
|
-
|
|
242
|
-
logger(`Fetch response ${response.ok} ${response.url || info.toString()} ${JSON.stringify(loggingContent, null, '\t')}`);
|
|
96
|
+
logger(...loggingItems);
|
|
243
97
|
}
|
|
98
|
+
resolve(response);
|
|
244
99
|
})
|
|
245
|
-
.catch(reject));
|
|
100
|
+
.catch(reject)));
|
|
246
101
|
}
|
|
247
102
|
/**
|
|
248
103
|
* 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.0",
|
|
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.
|
|
59
|
-
"@types/node": "^22.
|
|
57
|
+
"@chainfuse/types": "^2.5.0",
|
|
58
|
+
"@cloudflare/workers-types": "^4.20250407.0",
|
|
59
|
+
"@types/node": "^22.14.0"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "bbe7acd905d1ac371a01cbdbcba1fb0023ee185c"
|
|
62
62
|
}
|