@contractspec/integration.runtime 2.10.0 → 3.1.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.
Files changed (75) hide show
  1. package/dist/channel/dispatcher.d.ts +37 -0
  2. package/dist/channel/dispatcher.js +130 -0
  3. package/dist/channel/dispatcher.test.d.ts +1 -0
  4. package/dist/channel/github.d.ts +47 -0
  5. package/dist/channel/github.js +58 -0
  6. package/dist/channel/github.test.d.ts +1 -0
  7. package/dist/channel/index.d.ts +14 -0
  8. package/dist/channel/index.js +1463 -0
  9. package/dist/channel/memory-store.d.ts +28 -0
  10. package/dist/channel/memory-store.js +223 -0
  11. package/dist/channel/policy.d.ts +23 -0
  12. package/dist/channel/policy.js +119 -0
  13. package/dist/channel/policy.test.d.ts +1 -0
  14. package/dist/channel/postgres-queries.d.ts +11 -0
  15. package/dist/channel/postgres-queries.js +222 -0
  16. package/dist/channel/postgres-schema.d.ts +1 -0
  17. package/dist/channel/postgres-schema.js +94 -0
  18. package/dist/channel/postgres-store.d.ts +21 -0
  19. package/dist/channel/postgres-store.js +498 -0
  20. package/dist/channel/postgres-store.test.d.ts +1 -0
  21. package/dist/channel/replay-fixtures.d.ts +9 -0
  22. package/dist/channel/replay-fixtures.js +42 -0
  23. package/dist/channel/replay.test.d.ts +1 -0
  24. package/dist/channel/service.d.ts +26 -0
  25. package/dist/channel/service.js +319 -0
  26. package/dist/channel/service.test.d.ts +1 -0
  27. package/dist/channel/slack.d.ts +42 -0
  28. package/dist/channel/slack.js +82 -0
  29. package/dist/channel/slack.test.d.ts +1 -0
  30. package/dist/channel/store.d.ts +83 -0
  31. package/dist/channel/store.js +1 -0
  32. package/dist/channel/telemetry.d.ts +17 -0
  33. package/dist/channel/telemetry.js +1 -0
  34. package/dist/channel/types.d.ts +115 -0
  35. package/dist/channel/types.js +1 -0
  36. package/dist/channel/whatsapp-meta.d.ts +55 -0
  37. package/dist/channel/whatsapp-meta.js +66 -0
  38. package/dist/channel/whatsapp-meta.test.d.ts +1 -0
  39. package/dist/channel/whatsapp-twilio.d.ts +20 -0
  40. package/dist/channel/whatsapp-twilio.js +61 -0
  41. package/dist/channel/whatsapp-twilio.test.d.ts +1 -0
  42. package/dist/index.d.ts +2 -0
  43. package/dist/index.js +1621 -1
  44. package/dist/node/channel/dispatcher.js +129 -0
  45. package/dist/node/channel/github.js +57 -0
  46. package/dist/node/channel/index.js +1462 -0
  47. package/dist/node/channel/memory-store.js +222 -0
  48. package/dist/node/channel/policy.js +118 -0
  49. package/dist/node/channel/postgres-queries.js +221 -0
  50. package/dist/node/channel/postgres-schema.js +93 -0
  51. package/dist/node/channel/postgres-store.js +497 -0
  52. package/dist/node/channel/replay-fixtures.js +41 -0
  53. package/dist/node/channel/service.js +318 -0
  54. package/dist/node/channel/slack.js +81 -0
  55. package/dist/node/channel/store.js +0 -0
  56. package/dist/node/channel/telemetry.js +0 -0
  57. package/dist/node/channel/types.js +0 -0
  58. package/dist/node/channel/whatsapp-meta.js +65 -0
  59. package/dist/node/channel/whatsapp-twilio.js +60 -0
  60. package/dist/node/index.js +1621 -1
  61. package/dist/node/transport/auth-resolver.js +51 -0
  62. package/dist/node/transport/index.js +162 -0
  63. package/dist/node/transport/transport-factory.js +77 -0
  64. package/dist/node/transport/version-negotiator.js +36 -0
  65. package/dist/runtime.d.ts +16 -0
  66. package/dist/runtime.health.test.d.ts +1 -0
  67. package/dist/transport/auth-resolver.d.ts +20 -0
  68. package/dist/transport/auth-resolver.js +52 -0
  69. package/dist/transport/index.d.ts +3 -0
  70. package/dist/transport/index.js +163 -0
  71. package/dist/transport/transport-factory.d.ts +31 -0
  72. package/dist/transport/transport-factory.js +78 -0
  73. package/dist/transport/version-negotiator.d.ts +14 -0
  74. package/dist/transport/version-negotiator.js +37 -0
  75. package/package.json +273 -6
@@ -0,0 +1,51 @@
1
+ // src/transport/auth-resolver.ts
2
+ import { findAuthConfig } from "@contractspec/lib.contracts-integrations/integrations/auth";
3
+ import {
4
+ buildAuthHeaders,
5
+ refreshOAuth2Token,
6
+ isOAuth2TokenExpired
7
+ } from "@contractspec/lib.contracts-integrations/integrations/auth-helpers";
8
+ async function resolveAuth(options) {
9
+ const authConfig = findAuthConfig(options.supportedAuthMethods, options.activeAuthMethod);
10
+ if (!authConfig) {
11
+ return { headers: {}, tokenRefreshed: false };
12
+ }
13
+ if (authConfig.type === "oauth2" && options.oauth2State) {
14
+ if (isOAuth2TokenExpired(options.oauth2State)) {
15
+ const clientId = options.secrets.clientId ?? "";
16
+ const clientSecret = options.secrets.clientSecret ?? "";
17
+ try {
18
+ const newState = await refreshOAuth2Token(authConfig, options.oauth2State, { clientId, clientSecret }, options.fetchFn);
19
+ const mergedSecrets2 = {
20
+ ...options.secrets,
21
+ accessToken: newState.accessToken
22
+ };
23
+ return {
24
+ headers: buildAuthHeaders(authConfig, mergedSecrets2),
25
+ tokenRefreshed: true,
26
+ updatedOAuth2State: newState
27
+ };
28
+ } catch {
29
+ return {
30
+ headers: buildAuthHeaders(authConfig, options.secrets),
31
+ tokenRefreshed: false
32
+ };
33
+ }
34
+ }
35
+ const mergedSecrets = {
36
+ ...options.secrets,
37
+ accessToken: options.oauth2State.accessToken
38
+ };
39
+ return {
40
+ headers: buildAuthHeaders(authConfig, mergedSecrets),
41
+ tokenRefreshed: false
42
+ };
43
+ }
44
+ return {
45
+ headers: buildAuthHeaders(authConfig, options.secrets),
46
+ tokenRefreshed: false
47
+ };
48
+ }
49
+ export {
50
+ resolveAuth
51
+ };
@@ -0,0 +1,162 @@
1
+ // src/transport/transport-factory.ts
2
+ import { findTransportConfig } from "@contractspec/lib.contracts-integrations/integrations/transport";
3
+
4
+ class RestTransportClient {
5
+ config;
6
+ authHeaders;
7
+ fetchFn;
8
+ type = "rest";
9
+ constructor(config, authHeaders = {}, fetchFn = globalThis.fetch) {
10
+ this.config = config;
11
+ this.authHeaders = authHeaders;
12
+ this.fetchFn = fetchFn;
13
+ }
14
+ async request(method, path, options) {
15
+ const url = new URL(path, this.config.baseUrl ?? "https://localhost");
16
+ if (options?.queryParams) {
17
+ for (const [key, value] of Object.entries(options.queryParams)) {
18
+ url.searchParams.set(key, value);
19
+ }
20
+ }
21
+ const headers = {
22
+ ...this.config.defaultHeaders,
23
+ ...this.authHeaders,
24
+ ...options?.headers
25
+ };
26
+ if (options?.body && !headers["Content-Type"]) {
27
+ headers["Content-Type"] = "application/json";
28
+ }
29
+ const response = await this.fetchFn(url.toString(), {
30
+ method,
31
+ headers,
32
+ body: options?.body ? JSON.stringify(options.body) : undefined,
33
+ signal: options?.signal
34
+ });
35
+ const responseHeaders = {};
36
+ response.headers.forEach((value, key) => {
37
+ responseHeaders[key] = value;
38
+ });
39
+ const data = await response.json().catch(() => null);
40
+ let rateLimitRemaining;
41
+ let rateLimitReset;
42
+ if (this.config.rateLimitHeaders) {
43
+ const remaining = responseHeaders[this.config.rateLimitHeaders.remaining];
44
+ const reset = responseHeaders[this.config.rateLimitHeaders.reset];
45
+ if (remaining)
46
+ rateLimitRemaining = Number(remaining);
47
+ if (reset)
48
+ rateLimitReset = Number(reset);
49
+ }
50
+ return {
51
+ data,
52
+ status: response.status,
53
+ headers: responseHeaders,
54
+ rateLimitRemaining,
55
+ rateLimitReset
56
+ };
57
+ }
58
+ }
59
+ function createTransportClient(transports, targetType, authHeaders = {}, fetchFn) {
60
+ const config = findTransportConfig(transports, targetType);
61
+ if (!config)
62
+ return;
63
+ switch (config.type) {
64
+ case "rest":
65
+ return new RestTransportClient(config, authHeaders, fetchFn);
66
+ case "mcp":
67
+ case "webhook":
68
+ case "sdk":
69
+ return;
70
+ default:
71
+ return;
72
+ }
73
+ }
74
+
75
+ // src/transport/auth-resolver.ts
76
+ import { findAuthConfig } from "@contractspec/lib.contracts-integrations/integrations/auth";
77
+ import {
78
+ buildAuthHeaders,
79
+ refreshOAuth2Token,
80
+ isOAuth2TokenExpired
81
+ } from "@contractspec/lib.contracts-integrations/integrations/auth-helpers";
82
+ async function resolveAuth(options) {
83
+ const authConfig = findAuthConfig(options.supportedAuthMethods, options.activeAuthMethod);
84
+ if (!authConfig) {
85
+ return { headers: {}, tokenRefreshed: false };
86
+ }
87
+ if (authConfig.type === "oauth2" && options.oauth2State) {
88
+ if (isOAuth2TokenExpired(options.oauth2State)) {
89
+ const clientId = options.secrets.clientId ?? "";
90
+ const clientSecret = options.secrets.clientSecret ?? "";
91
+ try {
92
+ const newState = await refreshOAuth2Token(authConfig, options.oauth2State, { clientId, clientSecret }, options.fetchFn);
93
+ const mergedSecrets2 = {
94
+ ...options.secrets,
95
+ accessToken: newState.accessToken
96
+ };
97
+ return {
98
+ headers: buildAuthHeaders(authConfig, mergedSecrets2),
99
+ tokenRefreshed: true,
100
+ updatedOAuth2State: newState
101
+ };
102
+ } catch {
103
+ return {
104
+ headers: buildAuthHeaders(authConfig, options.secrets),
105
+ tokenRefreshed: false
106
+ };
107
+ }
108
+ }
109
+ const mergedSecrets = {
110
+ ...options.secrets,
111
+ accessToken: options.oauth2State.accessToken
112
+ };
113
+ return {
114
+ headers: buildAuthHeaders(authConfig, mergedSecrets),
115
+ tokenRefreshed: false
116
+ };
117
+ }
118
+ return {
119
+ headers: buildAuthHeaders(authConfig, options.secrets),
120
+ tokenRefreshed: false
121
+ };
122
+ }
123
+
124
+ // src/transport/version-negotiator.ts
125
+ import {
126
+ resolveApiVersion,
127
+ isVersionDeprecated
128
+ } from "@contractspec/lib.contracts-integrations/integrations/versioning";
129
+ function negotiateVersion(policy, connectionOverride) {
130
+ if (!policy) {
131
+ return {
132
+ resolvedVersion: undefined,
133
+ deprecated: false,
134
+ versionHeaders: {},
135
+ versionQueryParams: {}
136
+ };
137
+ }
138
+ const version = resolveApiVersion(policy, connectionOverride);
139
+ const deprecated = version ? isVersionDeprecated(policy, version) : false;
140
+ const versionHeaders = {};
141
+ const versionQueryParams = {};
142
+ if (version) {
143
+ if (policy.versionHeader) {
144
+ versionHeaders[policy.versionHeader] = version;
145
+ }
146
+ if (policy.versionQueryParam) {
147
+ versionQueryParams[policy.versionQueryParam] = version;
148
+ }
149
+ }
150
+ return {
151
+ resolvedVersion: version,
152
+ deprecated,
153
+ versionHeaders,
154
+ versionQueryParams
155
+ };
156
+ }
157
+ export {
158
+ resolveAuth,
159
+ negotiateVersion,
160
+ createTransportClient,
161
+ RestTransportClient
162
+ };
@@ -0,0 +1,77 @@
1
+ // src/transport/transport-factory.ts
2
+ import { findTransportConfig } from "@contractspec/lib.contracts-integrations/integrations/transport";
3
+
4
+ class RestTransportClient {
5
+ config;
6
+ authHeaders;
7
+ fetchFn;
8
+ type = "rest";
9
+ constructor(config, authHeaders = {}, fetchFn = globalThis.fetch) {
10
+ this.config = config;
11
+ this.authHeaders = authHeaders;
12
+ this.fetchFn = fetchFn;
13
+ }
14
+ async request(method, path, options) {
15
+ const url = new URL(path, this.config.baseUrl ?? "https://localhost");
16
+ if (options?.queryParams) {
17
+ for (const [key, value] of Object.entries(options.queryParams)) {
18
+ url.searchParams.set(key, value);
19
+ }
20
+ }
21
+ const headers = {
22
+ ...this.config.defaultHeaders,
23
+ ...this.authHeaders,
24
+ ...options?.headers
25
+ };
26
+ if (options?.body && !headers["Content-Type"]) {
27
+ headers["Content-Type"] = "application/json";
28
+ }
29
+ const response = await this.fetchFn(url.toString(), {
30
+ method,
31
+ headers,
32
+ body: options?.body ? JSON.stringify(options.body) : undefined,
33
+ signal: options?.signal
34
+ });
35
+ const responseHeaders = {};
36
+ response.headers.forEach((value, key) => {
37
+ responseHeaders[key] = value;
38
+ });
39
+ const data = await response.json().catch(() => null);
40
+ let rateLimitRemaining;
41
+ let rateLimitReset;
42
+ if (this.config.rateLimitHeaders) {
43
+ const remaining = responseHeaders[this.config.rateLimitHeaders.remaining];
44
+ const reset = responseHeaders[this.config.rateLimitHeaders.reset];
45
+ if (remaining)
46
+ rateLimitRemaining = Number(remaining);
47
+ if (reset)
48
+ rateLimitReset = Number(reset);
49
+ }
50
+ return {
51
+ data,
52
+ status: response.status,
53
+ headers: responseHeaders,
54
+ rateLimitRemaining,
55
+ rateLimitReset
56
+ };
57
+ }
58
+ }
59
+ function createTransportClient(transports, targetType, authHeaders = {}, fetchFn) {
60
+ const config = findTransportConfig(transports, targetType);
61
+ if (!config)
62
+ return;
63
+ switch (config.type) {
64
+ case "rest":
65
+ return new RestTransportClient(config, authHeaders, fetchFn);
66
+ case "mcp":
67
+ case "webhook":
68
+ case "sdk":
69
+ return;
70
+ default:
71
+ return;
72
+ }
73
+ }
74
+ export {
75
+ createTransportClient,
76
+ RestTransportClient
77
+ };
@@ -0,0 +1,36 @@
1
+ // src/transport/version-negotiator.ts
2
+ import {
3
+ resolveApiVersion,
4
+ isVersionDeprecated
5
+ } from "@contractspec/lib.contracts-integrations/integrations/versioning";
6
+ function negotiateVersion(policy, connectionOverride) {
7
+ if (!policy) {
8
+ return {
9
+ resolvedVersion: undefined,
10
+ deprecated: false,
11
+ versionHeaders: {},
12
+ versionQueryParams: {}
13
+ };
14
+ }
15
+ const version = resolveApiVersion(policy, connectionOverride);
16
+ const deprecated = version ? isVersionDeprecated(policy, version) : false;
17
+ const versionHeaders = {};
18
+ const versionQueryParams = {};
19
+ if (version) {
20
+ if (policy.versionHeader) {
21
+ versionHeaders[policy.versionHeader] = version;
22
+ }
23
+ if (policy.versionQueryParam) {
24
+ versionQueryParams[policy.versionQueryParam] = version;
25
+ }
26
+ }
27
+ return {
28
+ resolvedVersion: version,
29
+ deprecated,
30
+ versionHeaders,
31
+ versionQueryParams
32
+ };
33
+ }
34
+ export {
35
+ negotiateVersion
36
+ };
package/dist/runtime.d.ts CHANGED
@@ -101,3 +101,19 @@ export declare function resolveHealthStrategyOrder(options?: HealthRuntimeStrate
101
101
  export declare function isUnofficialHealthProviderAllowed(providerKey: string, options?: HealthRuntimeStrategyOptions): boolean;
102
102
  export declare function ensureConnectionReady(integration: ResolvedIntegration): void;
103
103
  export declare function connectionStatusLabel(status: ConnectionStatus): string;
104
+ /**
105
+ * Optional Composio fallback configuration.
106
+ * When present, the IntegrationProviderFactory will delegate unsupported
107
+ * integration keys to Composio's 850+ toolkit catalog.
108
+ */
109
+ export interface ComposioRuntimeConfig {
110
+ apiKey: string;
111
+ baseUrl?: string;
112
+ preferredTransport?: 'mcp' | 'sdk';
113
+ }
114
+ export interface IntegrationRuntimeConfig {
115
+ secretProvider: SecretProvider;
116
+ telemetry?: IntegrationTelemetryEmitter;
117
+ healthStrategy?: HealthRuntimeStrategyOptions;
118
+ composio?: ComposioRuntimeConfig;
119
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Resolves authentication credentials for an integration connection.
3
+ */
4
+ import type { IntegrationAuthConfig, IntegrationAuthType, OAuth2TokenState } from '@contractspec/lib.contracts-integrations/integrations/auth';
5
+ export interface AuthResolutionResult {
6
+ headers: Record<string, string>;
7
+ tokenRefreshed: boolean;
8
+ updatedOAuth2State?: OAuth2TokenState;
9
+ }
10
+ export interface AuthResolverOptions {
11
+ supportedAuthMethods: IntegrationAuthConfig[];
12
+ activeAuthMethod: IntegrationAuthType;
13
+ secrets: Record<string, string>;
14
+ oauth2State?: OAuth2TokenState;
15
+ fetchFn?: typeof globalThis.fetch;
16
+ }
17
+ /**
18
+ * Resolve auth headers, refreshing OAuth2 tokens if needed.
19
+ */
20
+ export declare function resolveAuth(options: AuthResolverOptions): Promise<AuthResolutionResult>;
@@ -0,0 +1,52 @@
1
+ // @bun
2
+ // src/transport/auth-resolver.ts
3
+ import { findAuthConfig } from "@contractspec/lib.contracts-integrations/integrations/auth";
4
+ import {
5
+ buildAuthHeaders,
6
+ refreshOAuth2Token,
7
+ isOAuth2TokenExpired
8
+ } from "@contractspec/lib.contracts-integrations/integrations/auth-helpers";
9
+ async function resolveAuth(options) {
10
+ const authConfig = findAuthConfig(options.supportedAuthMethods, options.activeAuthMethod);
11
+ if (!authConfig) {
12
+ return { headers: {}, tokenRefreshed: false };
13
+ }
14
+ if (authConfig.type === "oauth2" && options.oauth2State) {
15
+ if (isOAuth2TokenExpired(options.oauth2State)) {
16
+ const clientId = options.secrets.clientId ?? "";
17
+ const clientSecret = options.secrets.clientSecret ?? "";
18
+ try {
19
+ const newState = await refreshOAuth2Token(authConfig, options.oauth2State, { clientId, clientSecret }, options.fetchFn);
20
+ const mergedSecrets2 = {
21
+ ...options.secrets,
22
+ accessToken: newState.accessToken
23
+ };
24
+ return {
25
+ headers: buildAuthHeaders(authConfig, mergedSecrets2),
26
+ tokenRefreshed: true,
27
+ updatedOAuth2State: newState
28
+ };
29
+ } catch {
30
+ return {
31
+ headers: buildAuthHeaders(authConfig, options.secrets),
32
+ tokenRefreshed: false
33
+ };
34
+ }
35
+ }
36
+ const mergedSecrets = {
37
+ ...options.secrets,
38
+ accessToken: options.oauth2State.accessToken
39
+ };
40
+ return {
41
+ headers: buildAuthHeaders(authConfig, mergedSecrets),
42
+ tokenRefreshed: false
43
+ };
44
+ }
45
+ return {
46
+ headers: buildAuthHeaders(authConfig, options.secrets),
47
+ tokenRefreshed: false
48
+ };
49
+ }
50
+ export {
51
+ resolveAuth
52
+ };
@@ -0,0 +1,3 @@
1
+ export * from './transport-factory';
2
+ export * from './auth-resolver';
3
+ export * from './version-negotiator';
@@ -0,0 +1,163 @@
1
+ // @bun
2
+ // src/transport/transport-factory.ts
3
+ import { findTransportConfig } from "@contractspec/lib.contracts-integrations/integrations/transport";
4
+
5
+ class RestTransportClient {
6
+ config;
7
+ authHeaders;
8
+ fetchFn;
9
+ type = "rest";
10
+ constructor(config, authHeaders = {}, fetchFn = globalThis.fetch) {
11
+ this.config = config;
12
+ this.authHeaders = authHeaders;
13
+ this.fetchFn = fetchFn;
14
+ }
15
+ async request(method, path, options) {
16
+ const url = new URL(path, this.config.baseUrl ?? "https://localhost");
17
+ if (options?.queryParams) {
18
+ for (const [key, value] of Object.entries(options.queryParams)) {
19
+ url.searchParams.set(key, value);
20
+ }
21
+ }
22
+ const headers = {
23
+ ...this.config.defaultHeaders,
24
+ ...this.authHeaders,
25
+ ...options?.headers
26
+ };
27
+ if (options?.body && !headers["Content-Type"]) {
28
+ headers["Content-Type"] = "application/json";
29
+ }
30
+ const response = await this.fetchFn(url.toString(), {
31
+ method,
32
+ headers,
33
+ body: options?.body ? JSON.stringify(options.body) : undefined,
34
+ signal: options?.signal
35
+ });
36
+ const responseHeaders = {};
37
+ response.headers.forEach((value, key) => {
38
+ responseHeaders[key] = value;
39
+ });
40
+ const data = await response.json().catch(() => null);
41
+ let rateLimitRemaining;
42
+ let rateLimitReset;
43
+ if (this.config.rateLimitHeaders) {
44
+ const remaining = responseHeaders[this.config.rateLimitHeaders.remaining];
45
+ const reset = responseHeaders[this.config.rateLimitHeaders.reset];
46
+ if (remaining)
47
+ rateLimitRemaining = Number(remaining);
48
+ if (reset)
49
+ rateLimitReset = Number(reset);
50
+ }
51
+ return {
52
+ data,
53
+ status: response.status,
54
+ headers: responseHeaders,
55
+ rateLimitRemaining,
56
+ rateLimitReset
57
+ };
58
+ }
59
+ }
60
+ function createTransportClient(transports, targetType, authHeaders = {}, fetchFn) {
61
+ const config = findTransportConfig(transports, targetType);
62
+ if (!config)
63
+ return;
64
+ switch (config.type) {
65
+ case "rest":
66
+ return new RestTransportClient(config, authHeaders, fetchFn);
67
+ case "mcp":
68
+ case "webhook":
69
+ case "sdk":
70
+ return;
71
+ default:
72
+ return;
73
+ }
74
+ }
75
+
76
+ // src/transport/auth-resolver.ts
77
+ import { findAuthConfig } from "@contractspec/lib.contracts-integrations/integrations/auth";
78
+ import {
79
+ buildAuthHeaders,
80
+ refreshOAuth2Token,
81
+ isOAuth2TokenExpired
82
+ } from "@contractspec/lib.contracts-integrations/integrations/auth-helpers";
83
+ async function resolveAuth(options) {
84
+ const authConfig = findAuthConfig(options.supportedAuthMethods, options.activeAuthMethod);
85
+ if (!authConfig) {
86
+ return { headers: {}, tokenRefreshed: false };
87
+ }
88
+ if (authConfig.type === "oauth2" && options.oauth2State) {
89
+ if (isOAuth2TokenExpired(options.oauth2State)) {
90
+ const clientId = options.secrets.clientId ?? "";
91
+ const clientSecret = options.secrets.clientSecret ?? "";
92
+ try {
93
+ const newState = await refreshOAuth2Token(authConfig, options.oauth2State, { clientId, clientSecret }, options.fetchFn);
94
+ const mergedSecrets2 = {
95
+ ...options.secrets,
96
+ accessToken: newState.accessToken
97
+ };
98
+ return {
99
+ headers: buildAuthHeaders(authConfig, mergedSecrets2),
100
+ tokenRefreshed: true,
101
+ updatedOAuth2State: newState
102
+ };
103
+ } catch {
104
+ return {
105
+ headers: buildAuthHeaders(authConfig, options.secrets),
106
+ tokenRefreshed: false
107
+ };
108
+ }
109
+ }
110
+ const mergedSecrets = {
111
+ ...options.secrets,
112
+ accessToken: options.oauth2State.accessToken
113
+ };
114
+ return {
115
+ headers: buildAuthHeaders(authConfig, mergedSecrets),
116
+ tokenRefreshed: false
117
+ };
118
+ }
119
+ return {
120
+ headers: buildAuthHeaders(authConfig, options.secrets),
121
+ tokenRefreshed: false
122
+ };
123
+ }
124
+
125
+ // src/transport/version-negotiator.ts
126
+ import {
127
+ resolveApiVersion,
128
+ isVersionDeprecated
129
+ } from "@contractspec/lib.contracts-integrations/integrations/versioning";
130
+ function negotiateVersion(policy, connectionOverride) {
131
+ if (!policy) {
132
+ return {
133
+ resolvedVersion: undefined,
134
+ deprecated: false,
135
+ versionHeaders: {},
136
+ versionQueryParams: {}
137
+ };
138
+ }
139
+ const version = resolveApiVersion(policy, connectionOverride);
140
+ const deprecated = version ? isVersionDeprecated(policy, version) : false;
141
+ const versionHeaders = {};
142
+ const versionQueryParams = {};
143
+ if (version) {
144
+ if (policy.versionHeader) {
145
+ versionHeaders[policy.versionHeader] = version;
146
+ }
147
+ if (policy.versionQueryParam) {
148
+ versionQueryParams[policy.versionQueryParam] = version;
149
+ }
150
+ }
151
+ return {
152
+ resolvedVersion: version,
153
+ deprecated,
154
+ versionHeaders,
155
+ versionQueryParams
156
+ };
157
+ }
158
+ export {
159
+ resolveAuth,
160
+ negotiateVersion,
161
+ createTransportClient,
162
+ RestTransportClient
163
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Factory for creating transport clients from integration transport configs.
3
+ */
4
+ import type { IntegrationTransportConfig, IntegrationTransportType, RestTransportConfig } from '@contractspec/lib.contracts-integrations/integrations/transport';
5
+ export interface TransportClient {
6
+ readonly type: IntegrationTransportType;
7
+ request<T>(method: string, path: string, options?: TransportRequestOptions): Promise<TransportResponse<T>>;
8
+ }
9
+ export interface TransportRequestOptions {
10
+ body?: unknown;
11
+ headers?: Record<string, string>;
12
+ queryParams?: Record<string, string>;
13
+ timeoutMs?: number;
14
+ signal?: AbortSignal;
15
+ }
16
+ export interface TransportResponse<T> {
17
+ data: T;
18
+ status: number;
19
+ headers: Record<string, string>;
20
+ rateLimitRemaining?: number;
21
+ rateLimitReset?: number;
22
+ }
23
+ export declare class RestTransportClient implements TransportClient {
24
+ private readonly config;
25
+ private readonly authHeaders;
26
+ private readonly fetchFn;
27
+ readonly type: "rest";
28
+ constructor(config: RestTransportConfig, authHeaders?: Record<string, string>, fetchFn?: typeof globalThis.fetch);
29
+ request<T>(method: string, path: string, options?: TransportRequestOptions): Promise<TransportResponse<T>>;
30
+ }
31
+ export declare function createTransportClient(transports: IntegrationTransportConfig[], targetType: IntegrationTransportType, authHeaders?: Record<string, string>, fetchFn?: typeof globalThis.fetch): TransportClient | undefined;