@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 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,98 @@ 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, ...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
- 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
- }),
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
- return new Promise((resolve, reject) => fetch(info, init)
77
+ new Promise((resolve, reject) => fetch(info, init)
208
78
  .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
- };
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
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
222
- loggingContent['body'] = await response.json();
83
+ loggingItems.push(await loggingClone.json());
223
84
  }
224
85
  else {
225
- loggingContent['body'] = await response.text();
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
- // 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'));
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-base-to-string
237
- .catch(() => console.debug('Fetch response', response.ok, response.url || info.toString(), JSON.stringify(loggingContent, null, '\t')));
98
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
99
+ .catch(() => console.debug(...loggingItems));
238
100
  }
239
101
  }
240
102
  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')}`);
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.2.4",
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.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": "7ffca16c4f67ac274f440639b665c3b86da35195"
62
62
  }