@slates/provider 1.0.0-rc.31 → 1.0.0-rc.32
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 +4 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.module.js +4 -0
- package/package.json +1 -1
- package/src/auth/types.ts +4 -1
- package/src/error/base.test.ts +77 -0
- package/src/error/service.ts +3 -0
- package/src/error/types.ts +2 -0
- package/src/error/utils.ts +9 -0
package/dist/index.cjs
CHANGED
|
@@ -1309,6 +1309,8 @@ var sanitizeForBaggage = (value, options = {}) => {
|
|
|
1309
1309
|
return BAGGAGE_UNSERIALIZABLE_VALUE;
|
|
1310
1310
|
}
|
|
1311
1311
|
};
|
|
1312
|
+
var UPSTREAM_CODE_PATTERN = /^[\w.:-]{1,128}$/;
|
|
1313
|
+
var normalizeUpstreamCode = (value) => typeof value === "string" && UPSTREAM_CODE_PATTERN.test(value) ? value : void 0;
|
|
1312
1314
|
var normalizeSlateErrorInput = (input) => {
|
|
1313
1315
|
let code = input.code || DEFAULT_CODE;
|
|
1314
1316
|
return {
|
|
@@ -1323,6 +1325,7 @@ var normalizeSlateErrorInput = (input) => {
|
|
|
1323
1325
|
...input.upstream,
|
|
1324
1326
|
status: input.upstream?.status ?? input.status
|
|
1325
1327
|
}),
|
|
1328
|
+
upstreamCode: normalizeUpstreamCode(input.upstreamCode) ?? normalizeUpstreamCode(input.upstream?.code),
|
|
1326
1329
|
baggage: cleanRecord(input.baggage),
|
|
1327
1330
|
requestTraces: input.requestTraces
|
|
1328
1331
|
};
|
|
@@ -1785,6 +1788,7 @@ var mapServiceErrorToSlateErrorInput = (error, defaults = {}) => {
|
|
|
1785
1788
|
issues: defaults.issues ?? normalizeServiceErrorIssues(data.errors),
|
|
1786
1789
|
provider: defaults.provider,
|
|
1787
1790
|
upstream: defaults.upstream,
|
|
1791
|
+
upstreamCode: normalizeUpstreamCode(defaults.upstreamCode) ?? normalizeUpstreamCode(data.upstreamCode),
|
|
1788
1792
|
baggage: cleanRecord({
|
|
1789
1793
|
...defaults.baggage ?? {},
|
|
1790
1794
|
...getServiceErrorBaggage(data)
|
package/dist/index.d.cts
CHANGED
|
@@ -192,6 +192,7 @@ type SlateAuthWithOauth<InputType extends {}, OutputType extends {
|
|
|
192
192
|
adapters?: string[];
|
|
193
193
|
syncTokensAcrossConnections?: boolean;
|
|
194
194
|
adminSetup?: {
|
|
195
|
+
required?: boolean;
|
|
195
196
|
getAuthorizationUrl?: (ctx: SlateOauthAuthorizationUrlContext<InputType>) => Promise<SlateOauthAuthorizationUrlResult<InputType>>;
|
|
196
197
|
handleCallback?: (ctx: SlateOauthAdminCallbackContext<InputType>) => Promise<{
|
|
197
198
|
instructions?: string;
|
|
@@ -1076,6 +1077,7 @@ interface SlateErrorResponse {
|
|
|
1076
1077
|
issues?: SlateErrorIssue[];
|
|
1077
1078
|
provider?: SlateErrorProviderInfo;
|
|
1078
1079
|
upstream?: SlateErrorUpstreamInfo;
|
|
1080
|
+
upstreamCode?: string;
|
|
1079
1081
|
baggage?: Record<string, unknown>;
|
|
1080
1082
|
requestTraces?: SlateHttpTrace[];
|
|
1081
1083
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -192,6 +192,7 @@ type SlateAuthWithOauth<InputType extends {}, OutputType extends {
|
|
|
192
192
|
adapters?: string[];
|
|
193
193
|
syncTokensAcrossConnections?: boolean;
|
|
194
194
|
adminSetup?: {
|
|
195
|
+
required?: boolean;
|
|
195
196
|
getAuthorizationUrl?: (ctx: SlateOauthAuthorizationUrlContext<InputType>) => Promise<SlateOauthAuthorizationUrlResult<InputType>>;
|
|
196
197
|
handleCallback?: (ctx: SlateOauthAdminCallbackContext<InputType>) => Promise<{
|
|
197
198
|
instructions?: string;
|
|
@@ -1076,6 +1077,7 @@ interface SlateErrorResponse {
|
|
|
1076
1077
|
issues?: SlateErrorIssue[];
|
|
1077
1078
|
provider?: SlateErrorProviderInfo;
|
|
1078
1079
|
upstream?: SlateErrorUpstreamInfo;
|
|
1080
|
+
upstreamCode?: string;
|
|
1079
1081
|
baggage?: Record<string, unknown>;
|
|
1080
1082
|
requestTraces?: SlateHttpTrace[];
|
|
1081
1083
|
}
|
package/dist/index.module.js
CHANGED
|
@@ -1184,6 +1184,8 @@ var sanitizeForBaggage = (value, options = {}) => {
|
|
|
1184
1184
|
return BAGGAGE_UNSERIALIZABLE_VALUE;
|
|
1185
1185
|
}
|
|
1186
1186
|
};
|
|
1187
|
+
var UPSTREAM_CODE_PATTERN = /^[\w.:-]{1,128}$/;
|
|
1188
|
+
var normalizeUpstreamCode = (value) => typeof value === "string" && UPSTREAM_CODE_PATTERN.test(value) ? value : void 0;
|
|
1187
1189
|
var normalizeSlateErrorInput = (input) => {
|
|
1188
1190
|
let code = input.code || DEFAULT_CODE;
|
|
1189
1191
|
return {
|
|
@@ -1198,6 +1200,7 @@ var normalizeSlateErrorInput = (input) => {
|
|
|
1198
1200
|
...input.upstream,
|
|
1199
1201
|
status: input.upstream?.status ?? input.status
|
|
1200
1202
|
}),
|
|
1203
|
+
upstreamCode: normalizeUpstreamCode(input.upstreamCode) ?? normalizeUpstreamCode(input.upstream?.code),
|
|
1201
1204
|
baggage: cleanRecord(input.baggage),
|
|
1202
1205
|
requestTraces: input.requestTraces
|
|
1203
1206
|
};
|
|
@@ -1660,6 +1663,7 @@ var mapServiceErrorToSlateErrorInput = (error, defaults = {}) => {
|
|
|
1660
1663
|
issues: defaults.issues ?? normalizeServiceErrorIssues(data.errors),
|
|
1661
1664
|
provider: defaults.provider,
|
|
1662
1665
|
upstream: defaults.upstream,
|
|
1666
|
+
upstreamCode: normalizeUpstreamCode(defaults.upstreamCode) ?? normalizeUpstreamCode(data.upstreamCode),
|
|
1663
1667
|
baggage: cleanRecord({
|
|
1664
1668
|
...defaults.baggage ?? {},
|
|
1665
1669
|
...getServiceErrorBaggage(data)
|
package/package.json
CHANGED
package/src/auth/types.ts
CHANGED
|
@@ -65,8 +65,11 @@ export type SlateAuthWithOauth<
|
|
|
65
65
|
syncTokensAcrossConnections?: boolean;
|
|
66
66
|
|
|
67
67
|
// Missing hooks fall back to the regular flow (callback output discarded).
|
|
68
|
-
// Returned instructions are markdown shown to the admin.
|
|
68
|
+
// Returned instructions are markdown shown to the admin. `required` means users
|
|
69
|
+
// can't connect until an admin has completed setup; otherwise it is optional.
|
|
69
70
|
adminSetup?: {
|
|
71
|
+
required?: boolean;
|
|
72
|
+
|
|
70
73
|
getAuthorizationUrl?: (
|
|
71
74
|
ctx: SlateOauthAuthorizationUrlContext<InputType>
|
|
72
75
|
) => Promise<SlateOauthAuthorizationUrlResult<InputType>>;
|
package/src/error/base.test.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from '@lowerdeck/error';
|
|
18
18
|
import { AxiosError, type AxiosResponse, type InternalAxiosRequestConfig } from 'axios';
|
|
19
19
|
import { describe, expect, it } from 'vitest';
|
|
20
|
+
import { createApiServiceError } from './api';
|
|
20
21
|
import { SlateError } from './base';
|
|
21
22
|
|
|
22
23
|
let createConfig = (overrides: Partial<InternalAxiosRequestConfig> = {}) =>
|
|
@@ -175,4 +176,80 @@ describe('SlateError', () => {
|
|
|
175
176
|
});
|
|
176
177
|
}
|
|
177
178
|
});
|
|
179
|
+
|
|
180
|
+
it('exposes the upstream API error code as a top-level upstreamCode', () => {
|
|
181
|
+
expect(
|
|
182
|
+
new SlateError({ code: 'upstream.error', message: 'x', upstream: { code: 'E42' } }).data
|
|
183
|
+
.upstreamCode
|
|
184
|
+
).toBe('E42');
|
|
185
|
+
expect(
|
|
186
|
+
new SlateError({
|
|
187
|
+
code: 'upstream.error',
|
|
188
|
+
message: 'x',
|
|
189
|
+
upstream: { code: 'nested' },
|
|
190
|
+
upstreamCode: 'explicit'
|
|
191
|
+
}).data.upstreamCode
|
|
192
|
+
).toBe('explicit');
|
|
193
|
+
expect(
|
|
194
|
+
SlateError.fromServiceError(
|
|
195
|
+
createApiServiceError('x', { upstreamCode: 'card_declined' })
|
|
196
|
+
).data.upstreamCode
|
|
197
|
+
).toBe('card_declined');
|
|
198
|
+
expect(new SlateError({ code: 'upstream.error', message: 'x' }).data.upstreamCode).toBe(
|
|
199
|
+
undefined
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('accepts only code-shaped upstreamCode values', () => {
|
|
204
|
+
for (let code of [
|
|
205
|
+
'AADSTS700016',
|
|
206
|
+
'invalid_grant',
|
|
207
|
+
'RATE_LIMITED',
|
|
208
|
+
'request.bad',
|
|
209
|
+
'a:b-c'
|
|
210
|
+
]) {
|
|
211
|
+
expect(
|
|
212
|
+
new SlateError({ code: 'upstream.error', message: 'x', upstreamCode: code }).data
|
|
213
|
+
.upstreamCode
|
|
214
|
+
).toBe(code);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for (let code of ['', 'x'.repeat(129), 'has space', 'token=abc&x', 'line\nbreak', '`x`']) {
|
|
218
|
+
expect(
|
|
219
|
+
new SlateError({ code: 'upstream.error', message: 'x', upstreamCode: code }).data
|
|
220
|
+
.upstreamCode
|
|
221
|
+
).toBe(undefined);
|
|
222
|
+
expect(
|
|
223
|
+
SlateError.fromServiceError(createApiServiceError('x', { upstreamCode: code })).data
|
|
224
|
+
.upstreamCode
|
|
225
|
+
).toBe(undefined);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
expect(
|
|
229
|
+
SlateError.fromServiceError(
|
|
230
|
+
createApiServiceError('x', { upstreamCode: { nested: true } as unknown as string })
|
|
231
|
+
).data.upstreamCode
|
|
232
|
+
).toBe(undefined);
|
|
233
|
+
expect(
|
|
234
|
+
new SlateError({ code: 'upstream.error', message: 'x', upstreamCode: 'x'.repeat(128) })
|
|
235
|
+
.data.upstreamCode
|
|
236
|
+
).toBe('x'.repeat(128));
|
|
237
|
+
expect(
|
|
238
|
+
new SlateError({
|
|
239
|
+
code: 'upstream.error',
|
|
240
|
+
message: 'x',
|
|
241
|
+
upstreamCode: 'bad value',
|
|
242
|
+
upstream: { code: 'fallback_code' }
|
|
243
|
+
}).data.upstreamCode
|
|
244
|
+
).toBe('fallback_code');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('also keeps upstreamCode in service error baggage for older hubs', () => {
|
|
248
|
+
let error = SlateError.fromServiceError(
|
|
249
|
+
createApiServiceError('x', { upstreamCode: 'card_declined' })
|
|
250
|
+
);
|
|
251
|
+
expect(error.data.baggage?.serviceErrorData).toMatchObject({
|
|
252
|
+
upstreamCode: 'card_declined'
|
|
253
|
+
});
|
|
254
|
+
});
|
|
178
255
|
});
|
package/src/error/service.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
isRecord,
|
|
8
8
|
normalizeKind,
|
|
9
9
|
normalizeRetryable,
|
|
10
|
+
normalizeUpstreamCode,
|
|
10
11
|
sanitizeForBaggage
|
|
11
12
|
} from './utils';
|
|
12
13
|
|
|
@@ -162,6 +163,8 @@ export let mapServiceErrorToSlateErrorInput = (
|
|
|
162
163
|
issues: defaults.issues ?? normalizeServiceErrorIssues(data.errors),
|
|
163
164
|
provider: defaults.provider,
|
|
164
165
|
upstream: defaults.upstream,
|
|
166
|
+
upstreamCode:
|
|
167
|
+
normalizeUpstreamCode(defaults.upstreamCode) ?? normalizeUpstreamCode(data.upstreamCode),
|
|
165
168
|
baggage: cleanRecord({
|
|
166
169
|
...(defaults.baggage ?? {}),
|
|
167
170
|
...getServiceErrorBaggage(data)
|
package/src/error/types.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface SlateErrorResponse {
|
|
|
45
45
|
issues?: SlateErrorIssue[];
|
|
46
46
|
provider?: SlateErrorProviderInfo;
|
|
47
47
|
upstream?: SlateErrorUpstreamInfo;
|
|
48
|
+
// The upstream API's own error code, next to the normalized protocol `code`.
|
|
49
|
+
upstreamCode?: string;
|
|
48
50
|
baggage?: Record<string, unknown>;
|
|
49
51
|
requestTraces?: SlateHttpTrace[];
|
|
50
52
|
}
|
package/src/error/utils.ts
CHANGED
|
@@ -389,6 +389,13 @@ export let sanitizeForBaggage = (
|
|
|
389
389
|
}
|
|
390
390
|
};
|
|
391
391
|
|
|
392
|
+
let UPSTREAM_CODE_PATTERN = /^[\w.:-]{1,128}$/;
|
|
393
|
+
|
|
394
|
+
// upstreamCode is copied from untrusted upstream responses to the top level of the
|
|
395
|
+
// error, outside the baggage sanitizer, so anything not code-shaped is dropped.
|
|
396
|
+
export let normalizeUpstreamCode = (value: unknown): string | undefined =>
|
|
397
|
+
typeof value === 'string' && UPSTREAM_CODE_PATTERN.test(value) ? value : undefined;
|
|
398
|
+
|
|
392
399
|
export let normalizeSlateErrorInput = (input: SlateErrorInput): SlateErrorResponse => {
|
|
393
400
|
let code = input.code || DEFAULT_CODE;
|
|
394
401
|
return {
|
|
@@ -405,6 +412,8 @@ export let normalizeSlateErrorInput = (input: SlateErrorInput): SlateErrorRespon
|
|
|
405
412
|
...input.upstream,
|
|
406
413
|
status: input.upstream?.status ?? input.status
|
|
407
414
|
}) as SlateErrorUpstreamInfo | undefined,
|
|
415
|
+
upstreamCode:
|
|
416
|
+
normalizeUpstreamCode(input.upstreamCode) ?? normalizeUpstreamCode(input.upstream?.code),
|
|
408
417
|
baggage: cleanRecord(input.baggage),
|
|
409
418
|
requestTraces: input.requestTraces
|
|
410
419
|
};
|