@slates/provider 1.0.0-rc.7 → 1.0.0-rc.9

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.
Files changed (89) hide show
  1. package/dist/index.cjs +1619 -2
  2. package/dist/index.d.cts +620 -0
  3. package/dist/index.d.ts +620 -9
  4. package/dist/index.module.js +1547 -2
  5. package/package.json +6 -4
  6. package/src/action/action.ts +18 -3
  7. package/src/action/attachment.ts +41 -0
  8. package/src/action/builder.ts +21 -4
  9. package/src/action/index.ts +2 -2
  10. package/src/action/scopes.test.ts +77 -0
  11. package/src/action/scopes.ts +101 -0
  12. package/src/action/tool.ts +8 -4
  13. package/src/action/trigger.ts +11 -11
  14. package/src/auth/auth.ts +3 -3
  15. package/src/auth/types.ts +7 -6
  16. package/src/axios/index.ts +55 -23
  17. package/src/axios/trace.ts +443 -0
  18. package/src/config/config.ts +3 -3
  19. package/src/context/context.ts +29 -2
  20. package/src/context/hook.ts +1 -1
  21. package/src/error/axios.ts +219 -0
  22. package/src/error/base.test.ts +178 -0
  23. package/src/error/base.ts +108 -2
  24. package/src/error/declaration.ts +7 -2
  25. package/src/error/defaults.ts +50 -0
  26. package/src/error/index.ts +3 -0
  27. package/src/error/service.ts +160 -0
  28. package/src/error/types.ts +73 -0
  29. package/src/error/utils.ts +155 -0
  30. package/src/index.ts +1 -0
  31. package/src/logger/logger.test.ts +57 -0
  32. package/src/logger/logger.ts +72 -11
  33. package/src/specification/slate.ts +2 -2
  34. package/src/specification/specification.ts +2 -2
  35. package/dist/action/action.d.ts +0 -98
  36. package/dist/action/action.d.ts.map +0 -1
  37. package/dist/action/builder.d.ts +0 -27
  38. package/dist/action/builder.d.ts.map +0 -1
  39. package/dist/action/index.d.ts +0 -5
  40. package/dist/action/index.d.ts.map +0 -1
  41. package/dist/action/tool.d.ts +0 -14
  42. package/dist/action/tool.d.ts.map +0 -1
  43. package/dist/action/trigger.d.ts +0 -32
  44. package/dist/action/trigger.d.ts.map +0 -1
  45. package/dist/auth/auth.d.ts +0 -30
  46. package/dist/auth/auth.d.ts.map +0 -1
  47. package/dist/auth/index.d.ts +0 -3
  48. package/dist/auth/index.d.ts.map +0 -1
  49. package/dist/auth/types.d.ts +0 -150
  50. package/dist/auth/types.d.ts.map +0 -1
  51. package/dist/axios/index.d.ts +0 -4
  52. package/dist/axios/index.d.ts.map +0 -1
  53. package/dist/config/config.d.ts +0 -26
  54. package/dist/config/config.d.ts.map +0 -1
  55. package/dist/config/index.d.ts +0 -2
  56. package/dist/config/index.d.ts.map +0 -1
  57. package/dist/context/context.d.ts +0 -20
  58. package/dist/context/context.d.ts.map +0 -1
  59. package/dist/context/hook.d.ts +0 -4
  60. package/dist/context/hook.d.ts.map +0 -1
  61. package/dist/context/index.d.ts +0 -3
  62. package/dist/context/index.d.ts.map +0 -1
  63. package/dist/error/base.d.ts +0 -5
  64. package/dist/error/base.d.ts.map +0 -1
  65. package/dist/error/declaration.d.ts +0 -6
  66. package/dist/error/declaration.d.ts.map +0 -1
  67. package/dist/error/index.d.ts +0 -3
  68. package/dist/error/index.d.ts.map +0 -1
  69. package/dist/index.cjs.map +0 -1
  70. package/dist/index.d.ts.map +0 -1
  71. package/dist/index.modern.js +0 -2
  72. package/dist/index.modern.js.map +0 -1
  73. package/dist/index.module.js.map +0 -1
  74. package/dist/index.umd.js +0 -2
  75. package/dist/index.umd.js.map +0 -1
  76. package/dist/logger/index.d.ts +0 -2
  77. package/dist/logger/index.d.ts.map +0 -1
  78. package/dist/logger/logger.d.ts +0 -20
  79. package/dist/logger/logger.d.ts.map +0 -1
  80. package/dist/specification/index.d.ts +0 -3
  81. package/dist/specification/index.d.ts.map +0 -1
  82. package/dist/specification/slate.d.ts +0 -15
  83. package/dist/specification/slate.d.ts.map +0 -1
  84. package/dist/specification/specification.d.ts +0 -30
  85. package/dist/specification/specification.d.ts.map +0 -1
  86. package/dist/specification/zero.d.ts +0 -13
  87. package/dist/specification/zero.d.ts.map +0 -1
  88. package/dist/tokens.d.ts +0 -34
  89. package/dist/tokens.d.ts.map +0 -1
@@ -0,0 +1,443 @@
1
+ import type { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
2
+ import type { SlateContext } from '../context';
3
+
4
+ export interface SlateHttpTraceTextBody {
5
+ contentType?: string;
6
+ text: string;
7
+ truncated?: boolean;
8
+ }
9
+
10
+ export interface SlateHttpTrace {
11
+ startedAt: string;
12
+ durationMs: number;
13
+ request: {
14
+ method: string;
15
+ url: string;
16
+ headers?: Record<string, string>;
17
+ body?: SlateHttpTraceTextBody;
18
+ };
19
+ response?: {
20
+ status: number;
21
+ statusText?: string;
22
+ headers?: Record<string, string>;
23
+ body?: SlateHttpTraceTextBody;
24
+ };
25
+ error?: {
26
+ code?: string;
27
+ message: string;
28
+ };
29
+ }
30
+
31
+ interface SlateHttpTraceDraft {
32
+ context: SlateContext<any, any, any>;
33
+ startedAt: string;
34
+ startedAtMs: number;
35
+ request: SlateHttpTrace['request'];
36
+ }
37
+
38
+ type TraceAwareAxiosRequestConfig = InternalAxiosRequestConfig & {
39
+ __slatesHttpTraceDraft?: SlateHttpTraceDraft;
40
+ };
41
+
42
+ let traceDraftsByConfig = new WeakMap<object, SlateHttpTraceDraft>();
43
+ let traceDraftsByHeaders = new WeakMap<object, SlateHttpTraceDraft>();
44
+
45
+ let TRACE_TEXT_LIMIT = 10 * 1024;
46
+ let REDACTED_VALUE = '[redacted]';
47
+ let STRUCTURED_DEPTH_LIMIT = 6;
48
+ let STRUCTURED_ENTRY_LIMIT = 50;
49
+
50
+ let SECRET_KEY_PATTERN =
51
+ /(authorization|proxy[-_]?authorization|token|secret|password|passwd|api[-_]?key|access[-_]?token|refresh[-_]?token|client[-_]?secret|session|cookie|set-cookie|private[-_]?key|signature|sig|credential)/i;
52
+
53
+ let SAFE_HEADER_NAMES = new Set([
54
+ 'accept',
55
+ 'accept-encoding',
56
+ 'cache-control',
57
+ 'content-encoding',
58
+ 'content-language',
59
+ 'content-length',
60
+ 'content-type',
61
+ 'etag',
62
+ 'if-modified-since',
63
+ 'if-none-match',
64
+ 'last-modified',
65
+ 'location',
66
+ 'request-id',
67
+ 'retry-after',
68
+ 'user-agent',
69
+ 'x-correlation-id',
70
+ 'x-request-id',
71
+ 'x-slates-provider'
72
+ ]);
73
+
74
+ let SAFE_HEADER_PREFIXES = ['ratelimit-', 'x-ratelimit-'];
75
+
76
+ let isRecord = (value: unknown): value is Record<string, unknown> =>
77
+ typeof value === 'object' && value !== null && !Array.isArray(value);
78
+
79
+ let isObjectLike = (value: unknown): value is object =>
80
+ typeof value === 'object' && value !== null;
81
+
82
+ let normalizeContentType = (value: string | undefined) =>
83
+ value?.split(';')[0]?.trim().toLowerCase() || undefined;
84
+
85
+ let isTextContentType = (contentType?: string) =>
86
+ !contentType ||
87
+ /(^text\/)|json|xml|html|javascript|graphql|x-www-form-urlencoded|svg/.test(contentType);
88
+
89
+ let isSafeHeaderName = (name: string) => {
90
+ let normalized = name.toLowerCase();
91
+ if (SECRET_KEY_PATTERN.test(normalized)) return false;
92
+ if (SAFE_HEADER_NAMES.has(normalized)) return true;
93
+ return SAFE_HEADER_PREFIXES.some(prefix => normalized.startsWith(prefix));
94
+ };
95
+
96
+ let truncateText = (text: string) => {
97
+ if (text.length <= TRACE_TEXT_LIMIT) {
98
+ return { text, truncated: false };
99
+ }
100
+
101
+ return {
102
+ text: `${text.slice(0, TRACE_TEXT_LIMIT)}...[truncated]`,
103
+ truncated: true
104
+ };
105
+ };
106
+
107
+ let sanitizeFreeText = (text: string) =>
108
+ text
109
+ .replace(
110
+ /\b(Bearer|Basic)\s+[A-Za-z0-9\-._~+/]+=*/gi,
111
+ (_match, prefix: string) => `${prefix} ${REDACTED_VALUE}`
112
+ )
113
+ .replace(
114
+ /((?:api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|password|passwd|token|secret|signature|sig)=)([^&\s]+)/gi,
115
+ `$1${REDACTED_VALUE}`
116
+ )
117
+ .replace(
118
+ /("(?:(?:api[_-]?key)|(?:access[_-]?token)|(?:refresh[_-]?token)|(?:client[_-]?secret)|password|passwd|token|secret|signature|sig)"\s*:\s*")([^"]+)(")/gi,
119
+ `$1${REDACTED_VALUE}$3`
120
+ );
121
+
122
+ let sanitizeScalar = (value: string) => truncateText(sanitizeFreeText(value));
123
+
124
+ let redactStructuredValue = (value: unknown, depth = 0): unknown => {
125
+ if (value == null) return value;
126
+ if (depth >= STRUCTURED_DEPTH_LIMIT) return '[truncated]';
127
+
128
+ if (
129
+ typeof value === 'string' ||
130
+ typeof value === 'number' ||
131
+ typeof value === 'boolean' ||
132
+ typeof value === 'bigint'
133
+ ) {
134
+ return typeof value === 'string' ? sanitizeFreeText(value) : value;
135
+ }
136
+
137
+ if (Array.isArray(value)) {
138
+ return value
139
+ .slice(0, STRUCTURED_ENTRY_LIMIT)
140
+ .map(entry => redactStructuredValue(entry, depth + 1));
141
+ }
142
+
143
+ if (isRecord(value)) {
144
+ return Object.fromEntries(
145
+ Object.entries(value)
146
+ .slice(0, STRUCTURED_ENTRY_LIMIT)
147
+ .map(([key, entry]) => [
148
+ key,
149
+ SECRET_KEY_PATTERN.test(key)
150
+ ? REDACTED_VALUE
151
+ : redactStructuredValue(entry, depth + 1)
152
+ ])
153
+ );
154
+ }
155
+
156
+ return String(value);
157
+ };
158
+
159
+ let maybeParseJson = (text: string, contentType?: string) => {
160
+ let trimmed = text.trim();
161
+ if (!trimmed) return null;
162
+ if (!contentType?.includes('json') && !/^[\[{]/.test(trimmed)) {
163
+ return null;
164
+ }
165
+
166
+ try {
167
+ return JSON.parse(trimmed);
168
+ } catch {
169
+ return null;
170
+ }
171
+ };
172
+
173
+ let sanitizeTextBody = (
174
+ text: string,
175
+ contentType?: string
176
+ ): SlateHttpTraceTextBody | undefined => {
177
+ if (!text) return undefined;
178
+
179
+ let normalizedContentType = normalizeContentType(contentType);
180
+ let parsedJson = maybeParseJson(text, normalizedContentType);
181
+ let normalizedText =
182
+ parsedJson !== null
183
+ ? JSON.stringify(redactStructuredValue(parsedJson))
184
+ : sanitizeFreeText(text);
185
+ let { text: truncatedText, truncated } = truncateText(normalizedText);
186
+
187
+ return {
188
+ ...(normalizedContentType ? { contentType: normalizedContentType } : {}),
189
+ text: truncatedText,
190
+ ...(truncated ? { truncated: true } : {})
191
+ };
192
+ };
193
+
194
+ let isBinaryLike = (value: unknown) => {
195
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) return true;
196
+ if (value instanceof ArrayBuffer) return true;
197
+ if (ArrayBuffer.isView(value)) return true;
198
+ if (typeof Blob !== 'undefined' && value instanceof Blob) return true;
199
+ if (typeof FormData !== 'undefined' && value instanceof FormData) return true;
200
+ return false;
201
+ };
202
+
203
+ let serializeTextBody = (
204
+ value: unknown,
205
+ contentType?: string
206
+ ): SlateHttpTraceTextBody | undefined => {
207
+ let normalizedContentType = normalizeContentType(contentType);
208
+
209
+ if (value == null || isBinaryLike(value) || !isTextContentType(normalizedContentType)) {
210
+ return undefined;
211
+ }
212
+
213
+ if (typeof value === 'string') {
214
+ return sanitizeTextBody(value, normalizedContentType);
215
+ }
216
+
217
+ if (value instanceof URLSearchParams) {
218
+ return sanitizeTextBody(value.toString(), normalizedContentType);
219
+ }
220
+
221
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
222
+ return sanitizeTextBody(String(value), normalizedContentType);
223
+ }
224
+
225
+ if (Array.isArray(value) || isRecord(value)) {
226
+ return sanitizeTextBody(JSON.stringify(redactStructuredValue(value)), 'application/json');
227
+ }
228
+
229
+ return undefined;
230
+ };
231
+
232
+ let buildUrl = (baseURL: string | undefined, url: string | undefined) => {
233
+ if (!url) return '';
234
+ if (!baseURL) return url;
235
+
236
+ try {
237
+ return new URL(url, baseURL).toString();
238
+ } catch {
239
+ return url;
240
+ }
241
+ };
242
+
243
+ let sanitizeUrl = (value: string) => {
244
+ if (!value) return value;
245
+
246
+ try {
247
+ let url = new URL(value);
248
+ for (let key of Array.from(url.searchParams.keys())) {
249
+ if (SECRET_KEY_PATTERN.test(key)) {
250
+ url.searchParams.set(key, REDACTED_VALUE);
251
+ }
252
+ }
253
+ return url.toString();
254
+ } catch {
255
+ return value.replace(
256
+ /([?&]([^=&#]+)=)([^&#]+)/g,
257
+ (_match, prefix: string, key: string, rawValue: string) =>
258
+ SECRET_KEY_PATTERN.test(key) ? `${prefix}${REDACTED_VALUE}` : `${prefix}${rawValue}`
259
+ );
260
+ }
261
+ };
262
+
263
+ let flattenHeaderValue = (value: unknown): string | undefined => {
264
+ if (typeof value === 'string') return value;
265
+ if (typeof value === 'number' || typeof value === 'boolean') return String(value);
266
+ if (Array.isArray(value)) {
267
+ let normalized = value
268
+ .map(entry => flattenHeaderValue(entry))
269
+ .filter((entry): entry is string => typeof entry === 'string' && entry.length > 0);
270
+ return normalized.length > 0 ? normalized.join(', ') : undefined;
271
+ }
272
+ return undefined;
273
+ };
274
+
275
+ let headerEntries = (headers: unknown): [string, string][] => {
276
+ if (!headers) return [];
277
+
278
+ if (typeof (headers as { toJSON?: unknown }).toJSON === 'function') {
279
+ let json = (headers as { toJSON(): unknown }).toJSON();
280
+ if (isRecord(json)) {
281
+ return Object.entries(json)
282
+ .map(([key, value]) => {
283
+ let flattened = flattenHeaderValue(value);
284
+ return flattened ? ([key, flattened] as [string, string]) : null;
285
+ })
286
+ .filter((entry): entry is [string, string] => entry !== null);
287
+ }
288
+ }
289
+
290
+ if (typeof (headers as { entries?: unknown }).entries === 'function') {
291
+ return Array.from(
292
+ (headers as { entries(): IterableIterator<[string, string]> }).entries()
293
+ ).map(([key, value]) => [key, value]);
294
+ }
295
+
296
+ if (isRecord(headers)) {
297
+ return Object.entries(headers)
298
+ .map(([key, value]) => {
299
+ let flattened = flattenHeaderValue(value);
300
+ return flattened ? ([key, flattened] as [string, string]) : null;
301
+ })
302
+ .filter((entry): entry is [string, string] => entry !== null);
303
+ }
304
+
305
+ return [];
306
+ };
307
+
308
+ let sanitizeHeaders = (headers: unknown) => {
309
+ let entries = headerEntries(headers)
310
+ .map(([key, value]) => [key.toLowerCase(), sanitizeScalar(value).text] as [string, string])
311
+ .filter(([key]) => isSafeHeaderName(key));
312
+
313
+ if (entries.length === 0) return undefined;
314
+ return Object.fromEntries(entries);
315
+ };
316
+
317
+ let getContentType = (headers: unknown) =>
318
+ headerEntries(headers).find(([key]) => key.toLowerCase() === 'content-type')?.[1];
319
+
320
+ let takeTraceDraft = (config: TraceAwareAxiosRequestConfig | undefined) => {
321
+ if (!config) return undefined;
322
+
323
+ let draft =
324
+ config.__slatesHttpTraceDraft ??
325
+ (isObjectLike(config) ? traceDraftsByConfig.get(config) : undefined) ??
326
+ (isObjectLike(config.headers) ? traceDraftsByHeaders.get(config.headers) : undefined);
327
+
328
+ if (!draft) return undefined;
329
+
330
+ return draft;
331
+ };
332
+
333
+ let clearTraceDraft = (
334
+ config: TraceAwareAxiosRequestConfig | undefined,
335
+ draft: SlateHttpTraceDraft
336
+ ) => {
337
+ if (!config) return;
338
+
339
+ if (config.__slatesHttpTraceDraft === draft) {
340
+ delete config.__slatesHttpTraceDraft;
341
+ }
342
+
343
+ if (isObjectLike(config)) {
344
+ traceDraftsByConfig.delete(config);
345
+ }
346
+
347
+ if (isObjectLike(config.headers)) {
348
+ traceDraftsByHeaders.delete(config.headers);
349
+ }
350
+ };
351
+
352
+ export let attachHttpTraceDraft = (
353
+ config: InternalAxiosRequestConfig,
354
+ context: SlateContext<any, any, any>
355
+ ) => {
356
+ let traceAwareConfig = config as TraceAwareAxiosRequestConfig;
357
+ let contentType = getContentType(config.headers);
358
+
359
+ traceAwareConfig.__slatesHttpTraceDraft = {
360
+ context,
361
+ startedAt: new Date().toISOString(),
362
+ startedAtMs: Date.now(),
363
+ request: {
364
+ method: (config.method ?? 'GET').toUpperCase(),
365
+ url: sanitizeUrl(buildUrl(config.baseURL, config.url)),
366
+ ...(sanitizeHeaders(config.headers) ? { headers: sanitizeHeaders(config.headers) } : {}),
367
+ ...(serializeTextBody(config.data, contentType)
368
+ ? { body: serializeTextBody(config.data, contentType) }
369
+ : {})
370
+ }
371
+ };
372
+
373
+ if (isObjectLike(traceAwareConfig)) {
374
+ traceDraftsByConfig.set(traceAwareConfig, traceAwareConfig.__slatesHttpTraceDraft);
375
+ }
376
+
377
+ if (isObjectLike(traceAwareConfig.headers)) {
378
+ traceDraftsByHeaders.set(
379
+ traceAwareConfig.headers,
380
+ traceAwareConfig.__slatesHttpTraceDraft
381
+ );
382
+ }
383
+
384
+ return config;
385
+ };
386
+
387
+ export let recordHttpTraceFromResponse = (response: AxiosResponse) => {
388
+ let config = response.config as TraceAwareAxiosRequestConfig | undefined;
389
+ let draft = takeTraceDraft(config);
390
+ if (!draft) return response;
391
+ clearTraceDraft(config, draft);
392
+
393
+ let contentType = getContentType(response.headers);
394
+ draft.context.recordHttpTrace({
395
+ startedAt: draft.startedAt,
396
+ durationMs: Math.max(Date.now() - draft.startedAtMs, 0),
397
+ request: draft.request,
398
+ response: {
399
+ status: response.status,
400
+ ...(response.statusText ? { statusText: response.statusText } : {}),
401
+ ...(sanitizeHeaders(response.headers)
402
+ ? { headers: sanitizeHeaders(response.headers) }
403
+ : {}),
404
+ ...(serializeTextBody(response.data, contentType)
405
+ ? { body: serializeTextBody(response.data, contentType) }
406
+ : {})
407
+ }
408
+ });
409
+
410
+ return response;
411
+ };
412
+
413
+ export let recordHttpTraceFromError = (error: AxiosError) => {
414
+ let config = error.config as TraceAwareAxiosRequestConfig | undefined;
415
+ let draft = takeTraceDraft(config);
416
+ if (!draft) return;
417
+ clearTraceDraft(config, draft);
418
+
419
+ let responseContentType = getContentType(error.response?.headers);
420
+ draft.context.recordHttpTrace({
421
+ startedAt: draft.startedAt,
422
+ durationMs: Math.max(Date.now() - draft.startedAtMs, 0),
423
+ request: draft.request,
424
+ ...(error.response
425
+ ? {
426
+ response: {
427
+ status: error.response.status,
428
+ ...(error.response.statusText ? { statusText: error.response.statusText } : {}),
429
+ ...(sanitizeHeaders(error.response.headers)
430
+ ? { headers: sanitizeHeaders(error.response.headers) }
431
+ : {}),
432
+ ...(serializeTextBody(error.response.data, responseContentType)
433
+ ? { body: serializeTextBody(error.response.data, responseContentType) }
434
+ : {})
435
+ }
436
+ }
437
+ : {}),
438
+ error: {
439
+ ...(error.code ? { code: error.code } : {}),
440
+ message: error.message
441
+ }
442
+ });
443
+ };
@@ -1,4 +1,4 @@
1
- import z from 'zod';
1
+ import type z from 'zod';
2
2
 
3
3
  export class SlateConfig<ConfigType extends {}> {
4
4
  #configSchema: z.ZodType<ConfigType>;
@@ -6,7 +6,7 @@ export class SlateConfig<ConfigType extends {}> {
6
6
  | ((params: {
7
7
  previousConfig: ConfigType | null;
8
8
  newConfig: ConfigType;
9
- }) => { config: ConfigType | undefined } | void)
9
+ }) => { config: ConfigType | undefined } | undefined)
10
10
  | null = null;
11
11
  #getDefaultConfig: (() => ConfigType) | null = null;
12
12
 
@@ -29,7 +29,7 @@ export class SlateConfig<ConfigType extends {}> {
29
29
  handler: (params: {
30
30
  previousConfig: ConfigType | null;
31
31
  newConfig: ConfigType;
32
- }) => { config: ConfigType | undefined } | void
32
+ }) => { config: ConfigType | undefined } | undefined
33
33
  ): SlateConfig<ConfigType> {
34
34
  this.#configChanged = handler;
35
35
  return this;
@@ -1,10 +1,12 @@
1
- import { SlateLogger, SlateLogMessageInput } from '../logger';
2
- import { SlateSpecification } from '../specification/specification';
1
+ import type { SlateHttpTrace } from '../axios/trace';
2
+ import type { SlateLogger, SlateLogMessageInput } from '../logger';
3
+ import type { SlateSpecification } from '../specification/specification';
3
4
 
4
5
  export class SlateContext<ConfigType extends {}, AuthType extends {}, InputType extends {}> {
5
6
  #config: ConfigType;
6
7
  #input: InputType;
7
8
  #auth: AuthType;
9
+ #httpTraces: SlateHttpTrace[] = [];
8
10
 
9
11
  constructor(
10
12
  config: ConfigType,
@@ -50,6 +52,31 @@ export class SlateContext<ConfigType extends {}, AuthType extends {}, InputType
50
52
  return Object.freeze(this.#auth);
51
53
  }
52
54
 
55
+ recordHttpTrace(trace: SlateHttpTrace) {
56
+ this.#httpTraces.push(trace);
57
+ }
58
+
59
+ getHttpTraces() {
60
+ return this.#httpTraces.map(trace => ({
61
+ ...trace,
62
+ request: {
63
+ ...trace.request,
64
+ ...(trace.request.headers ? { headers: { ...trace.request.headers } } : {}),
65
+ ...(trace.request.body ? { body: { ...trace.request.body } } : {})
66
+ },
67
+ ...(trace.response
68
+ ? {
69
+ response: {
70
+ ...trace.response,
71
+ ...(trace.response.headers ? { headers: { ...trace.response.headers } } : {}),
72
+ ...(trace.response.body ? { body: { ...trace.response.body } } : {})
73
+ }
74
+ }
75
+ : {}),
76
+ ...(trace.error ? { error: { ...trace.error } } : {})
77
+ }));
78
+ }
79
+
53
80
  info(message: SlateLogMessageInput) {
54
81
  this.logger.info(message);
55
82
  }
@@ -1,5 +1,5 @@
1
1
  import { AsyncLocalStorage } from 'async_hooks';
2
- import { SlateContext } from './context';
2
+ import type { SlateContext } from './context';
3
3
 
4
4
  let asyncLocalStorage = new AsyncLocalStorage<SlateContext<any, any, any>>();
5
5