@slates/provider 1.0.0-rc.6 → 1.0.0-rc.8

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 (87) hide show
  1. package/dist/index.cjs +1588 -2
  2. package/dist/index.d.cts +602 -0
  3. package/dist/index.d.ts +602 -9
  4. package/dist/index.module.js +1519 -2
  5. package/package.json +8 -6
  6. package/src/action/action.ts +16 -3
  7. package/src/action/builder.ts +21 -4
  8. package/src/action/index.ts +2 -2
  9. package/src/action/scopes.test.ts +77 -0
  10. package/src/action/scopes.ts +101 -0
  11. package/src/action/tool.ts +8 -4
  12. package/src/action/trigger.ts +11 -11
  13. package/src/auth/auth.ts +3 -3
  14. package/src/auth/types.ts +7 -6
  15. package/src/axios/index.ts +56 -9
  16. package/src/axios/trace.ts +443 -0
  17. package/src/config/config.ts +3 -3
  18. package/src/context/context.ts +29 -2
  19. package/src/context/hook.ts +1 -1
  20. package/src/error/axios.ts +219 -0
  21. package/src/error/base.test.ts +178 -0
  22. package/src/error/base.ts +108 -2
  23. package/src/error/declaration.ts +7 -2
  24. package/src/error/defaults.ts +50 -0
  25. package/src/error/index.ts +3 -0
  26. package/src/error/service.ts +160 -0
  27. package/src/error/types.ts +73 -0
  28. package/src/error/utils.ts +155 -0
  29. package/src/logger/logger.test.ts +57 -0
  30. package/src/logger/logger.ts +72 -11
  31. package/src/specification/slate.ts +2 -2
  32. package/src/specification/specification.ts +2 -2
  33. package/dist/action/action.d.ts +0 -98
  34. package/dist/action/action.d.ts.map +0 -1
  35. package/dist/action/builder.d.ts +0 -27
  36. package/dist/action/builder.d.ts.map +0 -1
  37. package/dist/action/index.d.ts +0 -5
  38. package/dist/action/index.d.ts.map +0 -1
  39. package/dist/action/tool.d.ts +0 -14
  40. package/dist/action/tool.d.ts.map +0 -1
  41. package/dist/action/trigger.d.ts +0 -32
  42. package/dist/action/trigger.d.ts.map +0 -1
  43. package/dist/auth/auth.d.ts +0 -30
  44. package/dist/auth/auth.d.ts.map +0 -1
  45. package/dist/auth/index.d.ts +0 -3
  46. package/dist/auth/index.d.ts.map +0 -1
  47. package/dist/auth/types.d.ts +0 -150
  48. package/dist/auth/types.d.ts.map +0 -1
  49. package/dist/axios/index.d.ts +0 -4
  50. package/dist/axios/index.d.ts.map +0 -1
  51. package/dist/config/config.d.ts +0 -26
  52. package/dist/config/config.d.ts.map +0 -1
  53. package/dist/config/index.d.ts +0 -2
  54. package/dist/config/index.d.ts.map +0 -1
  55. package/dist/context/context.d.ts +0 -20
  56. package/dist/context/context.d.ts.map +0 -1
  57. package/dist/context/hook.d.ts +0 -4
  58. package/dist/context/hook.d.ts.map +0 -1
  59. package/dist/context/index.d.ts +0 -3
  60. package/dist/context/index.d.ts.map +0 -1
  61. package/dist/error/base.d.ts +0 -5
  62. package/dist/error/base.d.ts.map +0 -1
  63. package/dist/error/declaration.d.ts +0 -6
  64. package/dist/error/declaration.d.ts.map +0 -1
  65. package/dist/error/index.d.ts +0 -3
  66. package/dist/error/index.d.ts.map +0 -1
  67. package/dist/index.cjs.map +0 -1
  68. package/dist/index.d.ts.map +0 -1
  69. package/dist/index.modern.js +0 -2
  70. package/dist/index.modern.js.map +0 -1
  71. package/dist/index.module.js.map +0 -1
  72. package/dist/index.umd.js +0 -2
  73. package/dist/index.umd.js.map +0 -1
  74. package/dist/logger/index.d.ts +0 -2
  75. package/dist/logger/index.d.ts.map +0 -1
  76. package/dist/logger/logger.d.ts +0 -20
  77. package/dist/logger/logger.d.ts.map +0 -1
  78. package/dist/specification/index.d.ts +0 -3
  79. package/dist/specification/index.d.ts.map +0 -1
  80. package/dist/specification/slate.d.ts +0 -15
  81. package/dist/specification/slate.d.ts.map +0 -1
  82. package/dist/specification/specification.d.ts +0 -30
  83. package/dist/specification/specification.d.ts.map +0 -1
  84. package/dist/specification/zero.d.ts +0 -13
  85. package/dist/specification/zero.d.ts.map +0 -1
  86. package/dist/tokens.d.ts +0 -34
  87. package/dist/tokens.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slates/provider",
3
- "version": "1.0.0-rc.6",
3
+ "version": "1.0.0-rc.8",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -11,7 +11,7 @@
11
11
  "package.json"
12
12
  ],
13
13
  "author": "Tobias Herber",
14
- "license": "Apache 2",
14
+ "license": "FSL 1.1",
15
15
  "type": "module",
16
16
  "source": "src/index.ts",
17
17
  "exports": {
@@ -23,21 +23,23 @@
23
23
  "main": "./dist/index.cjs",
24
24
  "module": "./dist/index.module.js",
25
25
  "types": "dist/index.d.ts",
26
- "unpkg": "./dist/index.umd.js",
26
+ "unpkg": "./dist/index.module.js",
27
27
  "scripts": {
28
28
  "test": "vitest run --passWithNoTests",
29
29
  "lint": "prettier src/**/*.ts --check",
30
- "build": "microbundle"
30
+ "build": "tsup --config ../../tsup.packages.config.ts --external async_hooks",
31
+ "typecheck": "tsc --noEmit"
31
32
  },
32
33
  "dependencies": {
34
+ "@lowerdeck/error": "^1.1.0",
33
35
  "@toon-format/toon": "^2.1.0",
34
36
  "axios": "^1.13.2",
35
37
  "zod": "^4.2.1"
36
38
  },
37
39
  "devDependencies": {
38
40
  "microbundle": "^0.15.1",
39
- "@slates/tsconfig": "^1.0.0",
41
+ "@slates/tsconfig": "1.0.0-rc.1",
40
42
  "typescript": "5.8.2",
41
43
  "vitest": "^3.1.2"
42
44
  }
43
- }
45
+ }
@@ -1,9 +1,17 @@
1
- import { z } from 'zod';
2
- import { SlateContext } from '../context';
3
- import { SlateSpecification } from '../specification/specification';
1
+ import type { z } from 'zod';
2
+ import type { SlateContext } from '../context';
3
+ import type { SlateSpecification } from '../specification/specification';
4
4
 
5
5
  export type SlateActionType = 'tool' | 'trigger';
6
6
 
7
+ export interface SlateActionScopeClause {
8
+ OR: string[];
9
+ }
10
+
11
+ export interface SlateActionScopes {
12
+ AND: SlateActionScopeClause[];
13
+ }
14
+
7
15
  export interface SlateActionParameters {
8
16
  key: string;
9
17
  name: string;
@@ -16,6 +24,7 @@ export interface SlateActionParameters {
16
24
  [key: string]: boolean | undefined;
17
25
  };
18
26
  metadata?: Record<string, any>;
27
+ scopes?: SlateActionScopes;
19
28
  }
20
29
 
21
30
  export type SlateToolInvocationHandler<
@@ -188,4 +197,8 @@ export abstract class SlateAction<
188
197
  get metadata() {
189
198
  return this._params.metadata;
190
199
  }
200
+
201
+ get scopes() {
202
+ return this._params.scopes;
203
+ }
191
204
  }
@@ -1,9 +1,10 @@
1
- import z from 'zod';
1
+ import type z from 'zod';
2
2
  import { SlateDeclarationError } from '../error';
3
- import { SlateSpecification } from '../specification/specification';
4
- import {
3
+ import type { SlateSpecification } from '../specification/specification';
4
+ import type {
5
5
  SlateAction,
6
6
  SlateActionCreateParameters,
7
+ SlateActionScopes,
7
8
  SlateActionParameters,
8
9
  SlateActionParametersTool,
9
10
  SlateActionParametersTrigger,
@@ -16,6 +17,7 @@ import {
16
17
  SlateTriggerWebhookAutoUnregistrationHandler,
17
18
  SlateTriggerWebhookRequestHandler
18
19
  } from './action';
20
+ import { validateScopes } from './scopes';
19
21
 
20
22
  export class SlateActionBuilder<
21
23
  Type extends SlateActionType,
@@ -29,6 +31,7 @@ export class SlateActionBuilder<
29
31
  #authSchema: z.ZodType<AuthType>;
30
32
  #inputSchema: z.ZodType<InputType> | null = null;
31
33
  #outputSchema: z.ZodType<OutputType> | null = null;
34
+ #scopes: SlateActionScopes | undefined;
32
35
 
33
36
  #toolParams: SlateActionParametersTool<ConfigType, AuthType, InputType, OutputType> | null =
34
37
  null;
@@ -41,7 +44,7 @@ export class SlateActionBuilder<
41
44
 
42
45
  constructor(
43
46
  private readonly type: Type,
44
- private readonly spec: SlateSpecification<ConfigType, AuthType>,
47
+ readonly spec: SlateSpecification<ConfigType, AuthType>,
45
48
  private readonly params: SlateActionParameters,
46
49
  private readonly factory: (
47
50
  params: SlateActionCreateParameters<any, any, any, any>
@@ -65,6 +68,14 @@ export class SlateActionBuilder<
65
68
  return this as any;
66
69
  }
67
70
 
71
+ scopes(
72
+ scopes: SlateActionScopes
73
+ ): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result> {
74
+ validateScopes(scopes);
75
+ this.#scopes = scopes;
76
+ return this;
77
+ }
78
+
68
79
  handleInvocation(
69
80
  handler: SlateToolInvocationHandler<ConfigType, AuthType, InputType, OutputType>
70
81
  ): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result> {
@@ -123,6 +134,11 @@ export class SlateActionBuilder<
123
134
  }
124
135
 
125
136
  build() {
137
+ let scopes = this.#scopes ?? this.params.scopes;
138
+ if (scopes) {
139
+ validateScopes(scopes);
140
+ }
141
+
126
142
  if (!this.#inputSchema) {
127
143
  throw new SlateDeclarationError('Input schema is not defined');
128
144
  }
@@ -138,6 +154,7 @@ export class SlateActionBuilder<
138
154
 
139
155
  return this.factory({
140
156
  ...this.params,
157
+ scopes,
141
158
  configSchema: this.#configSchema,
142
159
  authSchema: this.#authSchema,
143
160
  inputSchema: this.#inputSchema,
@@ -1,5 +1,5 @@
1
1
  export * from './action';
2
+ export * from './scopes';
3
+ export type * from './builder';
2
4
  export * from './tool';
3
5
  export * from './trigger';
4
-
5
- export type * from './builder';
@@ -0,0 +1,77 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { allOf, anyOf, mergeScopes, scopeClause, validateScopes } from './scopes';
3
+
4
+ describe('action scope helpers', () => {
5
+ it('builds OR and AND scope clauses', () => {
6
+ expect(scopeClause('scope:read', 'scope:admin')).toEqual({
7
+ OR: ['scope:read', 'scope:admin']
8
+ });
9
+
10
+ expect(anyOf('scope:read', 'scope:admin')).toEqual({
11
+ AND: [{ OR: ['scope:read', 'scope:admin'] }]
12
+ });
13
+
14
+ expect(allOf(['scope:calendar'], ['scope:sharing', 'scope:sharing:admin'])).toEqual({
15
+ AND: [
16
+ { OR: ['scope:calendar'] },
17
+ { OR: ['scope:sharing', 'scope:sharing:admin'] }
18
+ ]
19
+ });
20
+ });
21
+
22
+ it('accepts nested scope structures and flattens them', () => {
23
+ expect(
24
+ allOf(
25
+ anyOf('scope:events.read', 'scope:events.write'),
26
+ scopeClause('scope:calendar'),
27
+ 'scope:profile'
28
+ )
29
+ ).toEqual({
30
+ AND: [
31
+ { OR: ['scope:events.read', 'scope:events.write'] },
32
+ { OR: ['scope:calendar'] },
33
+ { OR: ['scope:profile'] }
34
+ ]
35
+ });
36
+ });
37
+
38
+ it('merges multiple scope sets and skips empty inputs', () => {
39
+ expect(
40
+ mergeScopes(
41
+ anyOf('scope:calendar'),
42
+ undefined,
43
+ false,
44
+ allOf(['scope:events.read', 'scope:events.write'], 'scope:profile')
45
+ )
46
+ ).toEqual({
47
+ AND: [
48
+ { OR: ['scope:calendar'] },
49
+ { OR: ['scope:events.read', 'scope:events.write'] },
50
+ { OR: ['scope:profile'] }
51
+ ]
52
+ });
53
+
54
+ expect(mergeScopes(undefined, false, null)).toBeUndefined();
55
+ });
56
+
57
+ it('validates and clones incoming scope objects', () => {
58
+ let input = {
59
+ AND: [{ OR: ['scope:calendar', 'scope:calendar:admin'] }]
60
+ };
61
+ let validated = validateScopes(input);
62
+
63
+ expect(validated).toEqual({
64
+ AND: [{ OR: ['scope:calendar', 'scope:calendar:admin'] }]
65
+ });
66
+ expect(validated).not.toBe(input);
67
+ expect(validated.AND[0]).not.toBe(input.AND[0]);
68
+ });
69
+
70
+ it('throws helpful errors for invalid scope definitions', () => {
71
+ expect(() => scopeClause()).toThrow('Each action scope clause must include at least one OR scope');
72
+ expect(() => scopeClause('')).toThrow('Action scopes must be non-empty strings');
73
+ expect(() => validateScopes({ AND: [] })).toThrow(
74
+ 'Action scopes must include at least one AND clause'
75
+ );
76
+ });
77
+ });
@@ -0,0 +1,101 @@
1
+ import { SlateDeclarationError } from '../error';
2
+ import type { SlateActionScopeClause, SlateActionScopes } from './action';
3
+
4
+ export type SlateActionScopeValue = string;
5
+
6
+ export type SlateActionScopeClauseInput =
7
+ | SlateActionScopeValue
8
+ | readonly SlateActionScopeValue[]
9
+ | SlateActionScopeClause;
10
+
11
+ export type SlateActionScopesInput = SlateActionScopeClauseInput | SlateActionScopes;
12
+
13
+ export let scopeClause = (...scopes: SlateActionScopeValue[]): SlateActionScopeClause => {
14
+ assertScopeValues(scopes);
15
+ return {
16
+ OR: [...scopes]
17
+ };
18
+ };
19
+
20
+ export let anyOf = (...scopes: SlateActionScopeValue[]): SlateActionScopes => ({
21
+ AND: [scopeClause(...scopes)]
22
+ });
23
+
24
+ export let allOf = (...inputs: SlateActionScopesInput[]): SlateActionScopes => {
25
+ let AND = inputs.flatMap(input => normalizeScopes(input).AND);
26
+ return validateScopes({ AND });
27
+ };
28
+
29
+ export let mergeScopes = (
30
+ ...inputs: Array<SlateActionScopes | null | undefined | false>
31
+ ): SlateActionScopes | undefined => {
32
+ let AND = inputs.flatMap(input => (input ? input.AND : []));
33
+ if (AND.length === 0) {
34
+ return undefined;
35
+ }
36
+
37
+ return validateScopes({ AND });
38
+ };
39
+
40
+ export let validateScopes = (scopes: SlateActionScopes): SlateActionScopes => {
41
+ if (!Array.isArray(scopes.AND) || scopes.AND.length === 0) {
42
+ throw new SlateDeclarationError('Action scopes must include at least one AND clause');
43
+ }
44
+
45
+ return {
46
+ AND: scopes.AND.map(clause => normalizeClause(clause))
47
+ };
48
+ };
49
+
50
+ let normalizeScopes = (input: SlateActionScopesInput): SlateActionScopes => {
51
+ if (isScopes(input)) {
52
+ return validateScopes(input);
53
+ }
54
+
55
+ return {
56
+ AND: [normalizeClause(input)]
57
+ };
58
+ };
59
+
60
+ let normalizeClause = (input: SlateActionScopeClauseInput): SlateActionScopeClause => {
61
+ if (typeof input === 'string') {
62
+ return scopeClause(input);
63
+ }
64
+
65
+ if (Array.isArray(input)) {
66
+ return scopeClause(...input);
67
+ }
68
+
69
+ if (!isClause(input)) {
70
+ throw new SlateDeclarationError('Action scope clauses must be strings or OR clauses');
71
+ }
72
+
73
+ assertScopeValues(input.OR);
74
+ return {
75
+ OR: [...input.OR]
76
+ };
77
+ };
78
+
79
+ let assertScopeValues = (scopes: readonly SlateActionScopeValue[]) => {
80
+ if (scopes.length === 0) {
81
+ throw new SlateDeclarationError('Each action scope clause must include at least one OR scope');
82
+ }
83
+
84
+ for (let scope of scopes) {
85
+ if (typeof scope !== 'string' || scope.trim().length === 0) {
86
+ throw new SlateDeclarationError('Action scopes must be non-empty strings');
87
+ }
88
+ }
89
+ };
90
+
91
+ let isClause = (value: unknown): value is SlateActionScopeClause =>
92
+ typeof value === 'object' &&
93
+ value !== null &&
94
+ 'OR' in value &&
95
+ Array.isArray((value as SlateActionScopeClause).OR);
96
+
97
+ let isScopes = (value: unknown): value is SlateActionScopes =>
98
+ typeof value === 'object' &&
99
+ value !== null &&
100
+ 'AND' in value &&
101
+ Array.isArray((value as SlateActionScopes).AND);
@@ -1,6 +1,10 @@
1
- import z from 'zod';
2
- import { SlateSpecification } from '../specification/specification';
3
- import { SlateAction, SlateActionParameters, SlateToolInvocationHandler } from './action';
1
+ import type z from 'zod';
2
+ import type { SlateSpecification } from '../specification/specification';
3
+ import {
4
+ SlateAction,
5
+ type SlateActionParameters,
6
+ type SlateToolInvocationHandler
7
+ } from './action';
4
8
  import { SlateActionBuilder } from './builder';
5
9
 
6
10
  export interface SlateToolParameters<
@@ -36,7 +40,7 @@ export class SlateTool<
36
40
  params: SlateActionParameters
37
41
  ) {
38
42
  return new SlateActionBuilder('tool', spec, params, params => {
39
- if (params.type != 'tool') throw new Error('Invalid action type for tool');
43
+ if (params.type !== 'tool') throw new Error('Invalid action type for tool');
40
44
  return new SlateTool(spec, params.inputSchema, params.outputSchema, params);
41
45
  });
42
46
  }
@@ -1,18 +1,18 @@
1
- import z from 'zod';
2
- import { SlateSpecification } from '../specification/specification';
1
+ import type z from 'zod';
2
+ import type { SlateSpecification } from '../specification/specification';
3
3
  import {
4
4
  SlateAction,
5
- SlateActionParameters,
6
- SlatePollingOptions,
7
- SlateTriggerMappingHandler,
8
- SlateTriggerPollingHandler,
9
- SlateTriggerWebhookAutoRegistrationHandler,
10
- SlateTriggerWebhookAutoUnregistrationHandler,
11
- SlateTriggerWebhookRequestHandler
5
+ type SlateActionParameters,
6
+ type SlatePollingOptions,
7
+ type SlateTriggerMappingHandler,
8
+ type SlateTriggerPollingHandler,
9
+ type SlateTriggerWebhookAutoRegistrationHandler,
10
+ type SlateTriggerWebhookAutoUnregistrationHandler,
11
+ type SlateTriggerWebhookRequestHandler
12
12
  } from './action';
13
13
  import { SlateActionBuilder } from './builder';
14
14
 
15
- export const SlateDefaultPollingIntervalSeconds = 60 * 10;
15
+ export let SlateDefaultPollingIntervalSeconds = 60 * 10;
16
16
 
17
17
  export interface SlateTriggerParameters<
18
18
  ConfigType extends {},
@@ -72,7 +72,7 @@ export class SlateTrigger<
72
72
  params: SlateActionParameters
73
73
  ) {
74
74
  return new SlateActionBuilder('trigger', spec, params, params => {
75
- if (params.type != 'trigger') throw new Error('Invalid action type for trigger');
75
+ if (params.type !== 'trigger') throw new Error('Invalid action type for trigger');
76
76
  return new SlateTrigger(spec, params.inputSchema, params.outputSchema, params);
77
77
  });
78
78
  }
package/src/auth/auth.ts CHANGED
@@ -1,5 +1,5 @@
1
- import z from 'zod';
2
- import { SlateAuthType } from './types';
1
+ import type z from 'zod';
2
+ import type { SlateAuthType } from './types';
3
3
 
4
4
  export class SlateAuth<OutputType extends {} = {}> {
5
5
  #outputSchema: z.ZodType<OutputType> | null = null;
@@ -68,7 +68,7 @@ export class SlateAuth<OutputType extends {} = {}> {
68
68
  }
69
69
 
70
70
  get authStack() {
71
- return this.#authStack.filter(a => a.type != 'auth.none');
71
+ return this.#authStack.filter(a => a.type !== 'auth.none');
72
72
  }
73
73
 
74
74
  get outputSchema() {
package/src/auth/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import z from 'zod';
1
+ import type z from 'zod';
2
2
 
3
3
  export type SlateAuthWithOauth<
4
4
  InputType extends {},
@@ -24,7 +24,7 @@ export type SlateAuthWithOauth<
24
24
  onInputChanged?: (params: {
25
25
  previousInput: InputType | null;
26
26
  newInput: InputType;
27
- }) => Promise<{ input?: InputType } | void>;
27
+ }) => Promise<{ input?: InputType } | undefined>;
28
28
 
29
29
  getDefaultInput?: () => Promise<InputType>;
30
30
 
@@ -53,6 +53,7 @@ export type SlateAuthWithOauth<
53
53
  }) => Promise<{
54
54
  output: OutputType;
55
55
  input?: InputType;
56
+ scopes?: string[];
56
57
  }>;
57
58
 
58
59
  handleTokenRefresh?: (ctx: {
@@ -84,7 +85,7 @@ export type SlateAuthWithToken<InputType extends {}, OutputType extends { token?
84
85
  onInputChanged?: (params: {
85
86
  previousInput: InputType;
86
87
  newInput: InputType;
87
- }) => Promise<{ input?: InputType } | void>;
88
+ }) => Promise<{ input?: InputType } | undefined>;
88
89
 
89
90
  getDefaultInput?: () => Promise<InputType>;
90
91
 
@@ -106,7 +107,7 @@ export type SlateAuthWithServiceAccount<InputType extends {}, OutputType extends
106
107
  onInputChanged?: (params: {
107
108
  previousInput: InputType;
108
109
  newInput: InputType;
109
- }) => Promise<{ input?: InputType } | void>;
110
+ }) => Promise<{ input?: InputType } | undefined>;
110
111
 
111
112
  getDefaultInput?: () => Promise<InputType>;
112
113
 
@@ -128,7 +129,7 @@ export type SlateAuthWithCustomData<InputType extends {}, OutputType extends {}>
128
129
  onInputChanged?: (params: {
129
130
  previousInput: InputType;
130
131
  newInput: InputType;
131
- }) => Promise<{ input?: InputType } | void>;
132
+ }) => Promise<{ input?: InputType } | undefined>;
132
133
 
133
134
  getDefaultInput?: () => Promise<InputType>;
134
135
 
@@ -137,7 +138,7 @@ export type SlateAuthWithCustomData<InputType extends {}, OutputType extends {}>
137
138
  }>;
138
139
  };
139
140
 
140
- export type SlateAuthWithNone<InputType extends {}, OutputType extends {}> = {
141
+ export type SlateAuthWithNone<_InputType extends {}, _OutputType extends {}> = {
141
142
  type: 'auth.none';
142
143
 
143
144
  name: string;
@@ -1,14 +1,22 @@
1
- import baseAxios, { CreateAxiosDefaults } from 'axios';
1
+ import type { AxiosInstance, CreateAxiosDefaults, InternalAxiosRequestConfig } from 'axios';
2
+ import baseAxios, { getAdapter, isAxiosError } from 'axios';
2
3
  import { getCurrentContext } from '../context/hook';
4
+ import type { SlateAxiosErrorOptions } from '../error';
5
+ import { SlateError } from '../error';
6
+ import {
7
+ attachHttpTraceDraft,
8
+ recordHttpTraceFromError,
9
+ recordHttpTraceFromResponse
10
+ } from './trace';
3
11
 
4
- export let createAxios = (config?: CreateAxiosDefaults) => {
5
- let instance = baseAxios.create({
6
- ...config,
7
- headers: {
8
- ...config?.headers
9
- }
10
- });
12
+ export interface SlateAxiosDefaults extends CreateAxiosDefaults {
13
+ errorMapping?: SlateAxiosErrorOptions;
14
+ }
11
15
 
16
+ let applySlateInterceptors = (
17
+ instance: AxiosInstance,
18
+ errorMapping?: SlateAxiosErrorOptions
19
+ ) => {
12
20
  instance.interceptors.request.use(
13
21
  request => {
14
22
  // Has to be called in the context of an action execution
@@ -16,12 +24,51 @@ export let createAxios = (config?: CreateAxiosDefaults) => {
16
24
  let spec = ctx.specification;
17
25
 
18
26
  request.headers.set('User-Agent', `slates.dev@1.0.0/${spec.key}`);
27
+ request.headers.set('X-Slates-Provider', spec.key);
19
28
 
20
- return request;
29
+ let tracedRequest = attachHttpTraceDraft(request, ctx) as InternalAxiosRequestConfig & {
30
+ __slatesTraceAdapterWrapped?: boolean;
31
+ };
32
+
33
+ if (!tracedRequest.__slatesTraceAdapterWrapped) {
34
+ let adapter = getAdapter(tracedRequest.adapter ?? baseAxios.defaults.adapter);
35
+ tracedRequest.adapter = async config => {
36
+ try {
37
+ let response = await adapter(config);
38
+ return recordHttpTraceFromResponse(response);
39
+ } catch (error) {
40
+ if (isAxiosError(error)) {
41
+ recordHttpTraceFromError(error);
42
+ }
43
+ throw error;
44
+ }
45
+ };
46
+ tracedRequest.__slatesTraceAdapterWrapped = true;
47
+ }
48
+
49
+ return tracedRequest;
21
50
  },
22
51
  error => Promise.reject(error)
23
52
  );
24
53
 
54
+ instance.interceptors.response.use(
55
+ response => response,
56
+ error => Promise.reject(SlateError.fromAxios(error, errorMapping))
57
+ );
58
+ };
59
+
60
+ applySlateInterceptors(baseAxios);
61
+
62
+ export let createAxios = (config?: SlateAxiosDefaults) => {
63
+ let { errorMapping, ...axiosConfig } = config ?? {};
64
+ let instance = baseAxios.create({
65
+ ...axiosConfig,
66
+ headers: {
67
+ ...axiosConfig.headers
68
+ }
69
+ });
70
+
71
+ applySlateInterceptors(instance, errorMapping);
25
72
  return instance;
26
73
  };
27
74