@chainfuse/helpers 2.2.4 → 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 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<C extends CacheStorage>(apiKey: string, cacheStorageRef?: C, cacheTtl?: number, logger?: CustomLoging): Promise<import("cloudflare").Cloudflare>;
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.delete('Set-Cookie');
18
- originalHeaders.delete('Authorization');
19
- return originalHeaders;
16
+ const mutableHeaders = new Headers(originalHeaders);
17
+ mutableHeaders.delete('Set-Cookie');
18
+ mutableHeaders.delete('Authorization');
19
+ return mutableHeaders;
20
20
  }
21
- static cfApi(apiKey, cacheStorageRef, cacheTtl, logger = false) {
21
+ static cfApi(apiKey, logger = false) {
22
22
  return import('cloudflare').then(({ Cloudflare }) => new Cloudflare({
23
23
  apiToken: apiKey,
24
- fetch: (info, init) =>
25
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
26
- new Promise(async (resolve) => {
27
- const cacheKey = new Request(info, { ...init, headers: this.stripSensitiveHeaders(new Headers(init?.headers)) });
28
- if (typeof logger === 'boolean') {
29
- if (logger) {
30
- await import('chalk')
31
- .then(({ Chalk }) => {
32
- const chalk = new Chalk({ level: 1 });
33
- console.debug(chalk.magenta('CF Fetch request'), chalk.magenta(cacheKey.url), JSON.stringify(this.initBodyTrimmer({ ...init, headers: Object.fromEntries(this.stripSensitiveHeaders(new Headers(init?.headers)).entries()) }), null, '\t'));
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
- else {
39
- logger(`CF Fetch request ${cacheKey.url} ${JSON.stringify(this.initBodyTrimmer({ ...init, headers: Object.fromEntries(this.stripSensitiveHeaders(new Headers(init?.headers)).entries()) }), null, '\t')}`);
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
- return new Promise((resolve, reject) => fetch(info, init)
70
+ new Promise((resolve, reject) => fetch(info, init)
208
71
  .then(async (response) => {
209
- resolve(response.clone());
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
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
222
- loggingContent['body'] = await response.json();
76
+ loggingItems.push(await loggingClone.json());
223
77
  }
224
78
  else {
225
- loggingContent['body'] = await response.text();
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
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
234
- console.debug(response.ok ? chalk.green('Fetch response') : chalk.red('Fetch response'), response.ok, response.ok ? chalk.green(response.url || info.toString()) : chalk.red(response.url || info.toString()), JSON.stringify(loggingContent, null, '\t'));
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-base-to-string
237
- .catch(() => console.debug('Fetch response', response.ok, response.url || info.toString(), JSON.stringify(loggingContent, null, '\t')));
91
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
92
+ .catch(() => console.debug(...loggingItems));
238
93
  }
239
94
  }
240
95
  else {
241
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
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.2.4",
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.4.0",
58
- "@cloudflare/workers-types": "^4.20250402.0",
57
+ "@chainfuse/types": "^2.5.0",
58
+ "@cloudflare/workers-types": "^4.20250407.0",
59
59
  "@types/node": "^22.14.0"
60
60
  },
61
- "gitHead": "03a624d8b16b8043fdd51595a2f293aaf3017f51"
61
+ "gitHead": "bbe7acd905d1ac371a01cbdbcba1fb0023ee185c"
62
62
  }