@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.
- package/dist/index.cjs +1619 -2
- package/dist/index.d.cts +620 -0
- package/dist/index.d.ts +620 -9
- package/dist/index.module.js +1547 -2
- package/package.json +6 -4
- package/src/action/action.ts +18 -3
- package/src/action/attachment.ts +41 -0
- package/src/action/builder.ts +21 -4
- package/src/action/index.ts +2 -2
- package/src/action/scopes.test.ts +77 -0
- package/src/action/scopes.ts +101 -0
- package/src/action/tool.ts +8 -4
- package/src/action/trigger.ts +11 -11
- package/src/auth/auth.ts +3 -3
- package/src/auth/types.ts +7 -6
- package/src/axios/index.ts +55 -23
- package/src/axios/trace.ts +443 -0
- package/src/config/config.ts +3 -3
- package/src/context/context.ts +29 -2
- package/src/context/hook.ts +1 -1
- package/src/error/axios.ts +219 -0
- package/src/error/base.test.ts +178 -0
- package/src/error/base.ts +108 -2
- package/src/error/declaration.ts +7 -2
- package/src/error/defaults.ts +50 -0
- package/src/error/index.ts +3 -0
- package/src/error/service.ts +160 -0
- package/src/error/types.ts +73 -0
- package/src/error/utils.ts +155 -0
- package/src/index.ts +1 -0
- package/src/logger/logger.test.ts +57 -0
- package/src/logger/logger.ts +72 -11
- package/src/specification/slate.ts +2 -2
- package/src/specification/specification.ts +2 -2
- package/dist/action/action.d.ts +0 -98
- package/dist/action/action.d.ts.map +0 -1
- package/dist/action/builder.d.ts +0 -27
- package/dist/action/builder.d.ts.map +0 -1
- package/dist/action/index.d.ts +0 -5
- package/dist/action/index.d.ts.map +0 -1
- package/dist/action/tool.d.ts +0 -14
- package/dist/action/tool.d.ts.map +0 -1
- package/dist/action/trigger.d.ts +0 -32
- package/dist/action/trigger.d.ts.map +0 -1
- package/dist/auth/auth.d.ts +0 -30
- package/dist/auth/auth.d.ts.map +0 -1
- package/dist/auth/index.d.ts +0 -3
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/types.d.ts +0 -150
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/axios/index.d.ts +0 -4
- package/dist/axios/index.d.ts.map +0 -1
- package/dist/config/config.d.ts +0 -26
- package/dist/config/config.d.ts.map +0 -1
- package/dist/config/index.d.ts +0 -2
- package/dist/config/index.d.ts.map +0 -1
- package/dist/context/context.d.ts +0 -20
- package/dist/context/context.d.ts.map +0 -1
- package/dist/context/hook.d.ts +0 -4
- package/dist/context/hook.d.ts.map +0 -1
- package/dist/context/index.d.ts +0 -3
- package/dist/context/index.d.ts.map +0 -1
- package/dist/error/base.d.ts +0 -5
- package/dist/error/base.d.ts.map +0 -1
- package/dist/error/declaration.d.ts +0 -6
- package/dist/error/declaration.d.ts.map +0 -1
- package/dist/error/index.d.ts +0 -3
- package/dist/error/index.d.ts.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.modern.js +0 -2
- package/dist/index.modern.js.map +0 -1
- package/dist/index.module.js.map +0 -1
- package/dist/index.umd.js +0 -2
- package/dist/index.umd.js.map +0 -1
- package/dist/logger/index.d.ts +0 -2
- package/dist/logger/index.d.ts.map +0 -1
- package/dist/logger/logger.d.ts +0 -20
- package/dist/logger/logger.d.ts.map +0 -1
- package/dist/specification/index.d.ts +0 -3
- package/dist/specification/index.d.ts.map +0 -1
- package/dist/specification/slate.d.ts +0 -15
- package/dist/specification/slate.d.ts.map +0 -1
- package/dist/specification/specification.d.ts +0 -30
- package/dist/specification/specification.d.ts.map +0 -1
- package/dist/specification/zero.d.ts +0 -13
- package/dist/specification/zero.d.ts.map +0 -1
- package/dist/tokens.d.ts +0 -34
- 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
|
+
};
|
package/src/config/config.ts
CHANGED
|
@@ -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 } |
|
|
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 } |
|
|
32
|
+
}) => { config: ConfigType | undefined } | undefined
|
|
33
33
|
): SlateConfig<ConfigType> {
|
|
34
34
|
this.#configChanged = handler;
|
|
35
35
|
return this;
|
package/src/context/context.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
}
|
package/src/context/hook.ts
CHANGED