polarity-integration-utils 4.2.1 → 4.3.0
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/dts/requests/polarity-request.d.ts +37 -5
- package/dist/dts/requests/polarity-request.d.ts.map +1 -1
- package/dist/dts/testing/enhanced-utils/create-mock-integration-context.d.ts.map +1 -1
- package/dist/es/requests/polarity-request.js +33 -0
- package/dist/es/testing/enhanced-utils/create-mock-integration-context.js +56 -2
- package/dist/lib/requests/polarity-request.js +33 -0
- package/dist/lib/testing/enhanced-utils/create-mock-integration-context.js +56 -2
- package/dist/polarity-integration-utils.d.ts +46 -8
- package/package.json +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ApiRequestError, NetworkError, RetryRequestError } from '../errors';
|
|
2
|
-
import type { Entity, DoLookupUserOptions } from '@polarityio/integration-types';
|
|
2
|
+
import type { Entity, DoLookupUserOptions, NetworkContext, NetworkProxy } from '@polarityio/integration-types';
|
|
3
|
+
export type { NetworkContext, NetworkProxy };
|
|
3
4
|
/**
|
|
4
5
|
* @public
|
|
5
6
|
*/
|
|
@@ -187,13 +188,21 @@ export type IsApiErrorResult = {
|
|
|
187
188
|
export type IsApiErrorFunction = (response: HttpRequestResponse, requestOptions: HttpRequestOptions, userOptions: DoLookupUserOptions) => IsApiErrorResult;
|
|
188
189
|
/**
|
|
189
190
|
* Minimal interface for a rate limiter compatible with PolarityRequest.
|
|
190
|
-
*
|
|
191
|
+
* Only requires the `schedule` method, which is the sole method used internally.
|
|
192
|
+
* The Polarity server provides a full `Limiter` instance (from
|
|
193
|
+
* `@polarityio/integration-types`) that satisfies this interface, but consumers
|
|
194
|
+
* may also pass simpler objects or mocks that only implement `schedule`.
|
|
191
195
|
*
|
|
192
196
|
* @public
|
|
193
197
|
*/
|
|
194
|
-
export interface
|
|
198
|
+
export interface PolarityRequestLimiter {
|
|
195
199
|
schedule<T>(fn: (...args: unknown[]) => PromiseLike<T>, ...args: unknown[]): Promise<T>;
|
|
196
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* @deprecated Use {@link PolarityRequestLimiter} instead. This alias will be removed in a future major version.
|
|
203
|
+
* @public
|
|
204
|
+
*/
|
|
205
|
+
export type Limiter = PolarityRequestLimiter;
|
|
197
206
|
/**
|
|
198
207
|
* Hook that runs before an HTTP request is made. Each hook receives the output
|
|
199
208
|
* of the previous hook, allowing request options to be modified in a chain.
|
|
@@ -271,13 +280,21 @@ export interface PolarityRequestHooks {
|
|
|
271
280
|
*/
|
|
272
281
|
export interface PolarityRequestOptions {
|
|
273
282
|
defaults?: ConfigRequestProxyOptions;
|
|
283
|
+
/**
|
|
284
|
+
* Per-integration network configuration from `context.network`. When provided,
|
|
285
|
+
* proxy and TLS settings from this object override values from `defaults`.
|
|
286
|
+
*
|
|
287
|
+
* Can also be set (or updated) after construction via the {@link PolarityRequest.network}
|
|
288
|
+
* property to reflect per-request runtime config.
|
|
289
|
+
*/
|
|
290
|
+
network?: NetworkContext;
|
|
274
291
|
isApiError?: IsApiErrorFunction;
|
|
275
292
|
roundedSuccessStatusCodes?: number[];
|
|
276
293
|
httpResponseErrorProperties?: string[];
|
|
277
294
|
httpResponseErrorMessageProperties?: string[];
|
|
278
295
|
requestOptionsToSanitize?: string[];
|
|
279
296
|
hooks?: PolarityRequestHooks;
|
|
280
|
-
limiter?:
|
|
297
|
+
limiter?: PolarityRequestLimiter;
|
|
281
298
|
}
|
|
282
299
|
/**
|
|
283
300
|
* A utility class for making HTTP requests
|
|
@@ -343,7 +360,22 @@ export declare class PolarityRequest {
|
|
|
343
360
|
* through this limiter. Typically provided by the Polarity server via the
|
|
344
361
|
* integration context.
|
|
345
362
|
*/
|
|
346
|
-
limiter:
|
|
363
|
+
limiter: PolarityRequestLimiter | null;
|
|
364
|
+
/**
|
|
365
|
+
* Per-integration network configuration from `context.network`.
|
|
366
|
+
*
|
|
367
|
+
* When set, proxy and TLS settings are applied dynamically on each
|
|
368
|
+
* {@link PolarityRequest.run} call, overriding any static values from
|
|
369
|
+
* the constructor `defaults`. Set this before each `run()` call to
|
|
370
|
+
* reflect the latest runtime config.
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* polarityRequest.network = context.network;
|
|
375
|
+
* const response = await polarityRequest.run(requestOptions);
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
network: NetworkContext | null;
|
|
347
379
|
/**
|
|
348
380
|
* Lifecycle hooks for customizing request behavior. Hooks are configured via the
|
|
349
381
|
* {@link PolarityRequestOptions.hooks} property when creating a new instance of the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"polarity-request.d.ts","sourceRoot":"","sources":["../../../lib/requests/polarity-request.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,iBAAiB,EAElB,MAAM,WAAW,CAAC;AAGnB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"polarity-request.d.ts","sourceRoot":"","sources":["../../../lib/requests/polarity-request.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-mock-integration-context.d.ts","sourceRoot":"","sources":["../../../../lib/testing/enhanced-utils/create-mock-integration-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAExE;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;AAI1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,4BAA4B,GACvC,eAAc,aAAoB,KACjC,
|
|
1
|
+
{"version":3,"file":"create-mock-integration-context.d.ts","sourceRoot":"","sources":["../../../../lib/testing/enhanced-utils/create-mock-integration-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAExE;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;AAI1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,4BAA4B,GACvC,eAAc,aAAoB,KACjC,kBA0GF,CAAC"}
|
|
@@ -78,6 +78,21 @@ class PolarityRequest {
|
|
|
78
78
|
* integration context.
|
|
79
79
|
*/
|
|
80
80
|
limiter = null;
|
|
81
|
+
/**
|
|
82
|
+
* Per-integration network configuration from `context.network`.
|
|
83
|
+
*
|
|
84
|
+
* When set, proxy and TLS settings are applied dynamically on each
|
|
85
|
+
* {@link PolarityRequest.run} call, overriding any static values from
|
|
86
|
+
* the constructor `defaults`. Set this before each `run()` call to
|
|
87
|
+
* reflect the latest runtime config.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* polarityRequest.network = context.network;
|
|
92
|
+
* const response = await polarityRequest.run(requestOptions);
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
network = null;
|
|
81
96
|
/**
|
|
82
97
|
* Lifecycle hooks for customizing request behavior. Hooks are configured via the
|
|
83
98
|
* {@link PolarityRequestOptions.hooks} property when creating a new instance of the
|
|
@@ -133,6 +148,9 @@ class PolarityRequest {
|
|
|
133
148
|
if (options.limiter) {
|
|
134
149
|
this.limiter = options.limiter;
|
|
135
150
|
}
|
|
151
|
+
if (options.network) {
|
|
152
|
+
this.network = options.network;
|
|
153
|
+
}
|
|
136
154
|
const defaultRequestOptions = {
|
|
137
155
|
...(this.configFieldIsValid(ca) && { ca: fs_1.default.readFileSync(ca) }),
|
|
138
156
|
...(this.configFieldIsValid(cert) && { cert: fs_1.default.readFileSync(cert) }),
|
|
@@ -164,6 +182,21 @@ class PolarityRequest {
|
|
|
164
182
|
}
|
|
165
183
|
processedOptions = hookResult;
|
|
166
184
|
}
|
|
185
|
+
// Apply runtime network settings (overrides defaults and hook mutations)
|
|
186
|
+
if (this.network) {
|
|
187
|
+
const url = (processedOptions.url || '').trim().toLowerCase();
|
|
188
|
+
const isHttps = url.startsWith('https://') || !url.startsWith('http://');
|
|
189
|
+
const proxy = isHttps
|
|
190
|
+
? (this.network.proxy?.https ?? this.network.proxy?.http)
|
|
191
|
+
: (this.network.proxy?.http ?? this.network.proxy?.https);
|
|
192
|
+
if (proxy) {
|
|
193
|
+
processedOptions.proxy = proxy;
|
|
194
|
+
}
|
|
195
|
+
if (this.network.proxy?.noProxy) {
|
|
196
|
+
processedOptions.noProxyHost = this.network.proxy.noProxy;
|
|
197
|
+
}
|
|
198
|
+
processedOptions.rejectUnauthorized = this.network.rejectUnauthorized;
|
|
199
|
+
}
|
|
167
200
|
let httpResponse;
|
|
168
201
|
try {
|
|
169
202
|
httpResponse = await (this.limiter
|
|
@@ -44,12 +44,46 @@ const createMockIntegrationContext = (createMockFn = noOp) => {
|
|
|
44
44
|
fatal: createMockFn()
|
|
45
45
|
};
|
|
46
46
|
// Make child() return the logger for method chaining
|
|
47
|
-
if (typeof childFn.mockReturnValue ===
|
|
47
|
+
if (typeof childFn.mockReturnValue ===
|
|
48
|
+
'function') {
|
|
48
49
|
childFn.mockReturnValue(logger);
|
|
49
50
|
}
|
|
50
51
|
else {
|
|
51
52
|
logger.child = () => logger;
|
|
52
53
|
}
|
|
54
|
+
// Create a limiter schedule passthrough that executes the provided callback
|
|
55
|
+
// (matching Bottleneck behavior) while remaining spyable.
|
|
56
|
+
// Supports both schedule(fn, ...args) and schedule(options, fn, ...args) overloads.
|
|
57
|
+
const scheduleImpl = async (fnOrOptions, ...rest) => {
|
|
58
|
+
const fn = (typeof fnOrOptions === 'function' ? fnOrOptions : rest[0]);
|
|
59
|
+
const args = typeof fnOrOptions === 'function' ? rest : rest.slice(1);
|
|
60
|
+
return fn(...args);
|
|
61
|
+
};
|
|
62
|
+
const countsImpl = () => ({
|
|
63
|
+
EXECUTING: 0,
|
|
64
|
+
QUEUED: 0,
|
|
65
|
+
RUNNING: 0,
|
|
66
|
+
DONE: 0,
|
|
67
|
+
RECEIVED: 0
|
|
68
|
+
});
|
|
69
|
+
const settingsImpl = () => ({});
|
|
70
|
+
const scheduleMock = createMockFn();
|
|
71
|
+
const countsMock = createMockFn();
|
|
72
|
+
const settingsMock = createMockFn();
|
|
73
|
+
const scopeMock = createMockFn();
|
|
74
|
+
const updateSettingsMock = createMockFn();
|
|
75
|
+
const hasMockImplementation = typeof scheduleMock
|
|
76
|
+
.mockImplementation === 'function';
|
|
77
|
+
if (hasMockImplementation) {
|
|
78
|
+
const setImpl = (mock, impl) => mock.mockImplementation(impl);
|
|
79
|
+
setImpl(scheduleMock, scheduleImpl);
|
|
80
|
+
setImpl(countsMock, countsImpl);
|
|
81
|
+
setImpl(settingsMock, settingsImpl);
|
|
82
|
+
setImpl(updateSettingsMock, async () => undefined);
|
|
83
|
+
setImpl(scopeMock, () => ({
|
|
84
|
+
schedule: scheduleMock
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
53
87
|
return {
|
|
54
88
|
cache: {
|
|
55
89
|
global: createCacheScope(),
|
|
@@ -59,8 +93,28 @@ const createMockIntegrationContext = (createMockFn = noOp) => {
|
|
|
59
93
|
integrationId: 'test-integration',
|
|
60
94
|
userId: 1,
|
|
61
95
|
logger,
|
|
96
|
+
limiter: {
|
|
97
|
+
schedule: (hasMockImplementation
|
|
98
|
+
? scheduleMock
|
|
99
|
+
: scheduleImpl),
|
|
100
|
+
updateSettings: (hasMockImplementation
|
|
101
|
+
? updateSettingsMock
|
|
102
|
+
: async () => undefined),
|
|
103
|
+
counts: (hasMockImplementation
|
|
104
|
+
? countsMock
|
|
105
|
+
: countsImpl),
|
|
106
|
+
settings: (hasMockImplementation
|
|
107
|
+
? settingsMock
|
|
108
|
+
: settingsImpl),
|
|
109
|
+
scope: (hasMockImplementation
|
|
110
|
+
? scopeMock
|
|
111
|
+
: () => ({ schedule: scheduleImpl }))
|
|
112
|
+
},
|
|
62
113
|
startPolling: createMockFn(),
|
|
63
|
-
stopPolling: createMockFn()
|
|
114
|
+
stopPolling: createMockFn(),
|
|
115
|
+
network: {
|
|
116
|
+
rejectUnauthorized: true
|
|
117
|
+
}
|
|
64
118
|
};
|
|
65
119
|
};
|
|
66
120
|
exports.createMockIntegrationContext = createMockIntegrationContext;
|
|
@@ -78,6 +78,21 @@ class PolarityRequest {
|
|
|
78
78
|
* integration context.
|
|
79
79
|
*/
|
|
80
80
|
limiter = null;
|
|
81
|
+
/**
|
|
82
|
+
* Per-integration network configuration from `context.network`.
|
|
83
|
+
*
|
|
84
|
+
* When set, proxy and TLS settings are applied dynamically on each
|
|
85
|
+
* {@link PolarityRequest.run} call, overriding any static values from
|
|
86
|
+
* the constructor `defaults`. Set this before each `run()` call to
|
|
87
|
+
* reflect the latest runtime config.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* polarityRequest.network = context.network;
|
|
92
|
+
* const response = await polarityRequest.run(requestOptions);
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
network = null;
|
|
81
96
|
/**
|
|
82
97
|
* Lifecycle hooks for customizing request behavior. Hooks are configured via the
|
|
83
98
|
* {@link PolarityRequestOptions.hooks} property when creating a new instance of the
|
|
@@ -133,6 +148,9 @@ class PolarityRequest {
|
|
|
133
148
|
if (options.limiter) {
|
|
134
149
|
this.limiter = options.limiter;
|
|
135
150
|
}
|
|
151
|
+
if (options.network) {
|
|
152
|
+
this.network = options.network;
|
|
153
|
+
}
|
|
136
154
|
const defaultRequestOptions = {
|
|
137
155
|
...(this.configFieldIsValid(ca) && { ca: fs_1.default.readFileSync(ca) }),
|
|
138
156
|
...(this.configFieldIsValid(cert) && { cert: fs_1.default.readFileSync(cert) }),
|
|
@@ -164,6 +182,21 @@ class PolarityRequest {
|
|
|
164
182
|
}
|
|
165
183
|
processedOptions = hookResult;
|
|
166
184
|
}
|
|
185
|
+
// Apply runtime network settings (overrides defaults and hook mutations)
|
|
186
|
+
if (this.network) {
|
|
187
|
+
const url = (processedOptions.url || '').trim().toLowerCase();
|
|
188
|
+
const isHttps = url.startsWith('https://') || !url.startsWith('http://');
|
|
189
|
+
const proxy = isHttps
|
|
190
|
+
? (this.network.proxy?.https ?? this.network.proxy?.http)
|
|
191
|
+
: (this.network.proxy?.http ?? this.network.proxy?.https);
|
|
192
|
+
if (proxy) {
|
|
193
|
+
processedOptions.proxy = proxy;
|
|
194
|
+
}
|
|
195
|
+
if (this.network.proxy?.noProxy) {
|
|
196
|
+
processedOptions.noProxyHost = this.network.proxy.noProxy;
|
|
197
|
+
}
|
|
198
|
+
processedOptions.rejectUnauthorized = this.network.rejectUnauthorized;
|
|
199
|
+
}
|
|
167
200
|
let httpResponse;
|
|
168
201
|
try {
|
|
169
202
|
httpResponse = await (this.limiter
|
|
@@ -44,12 +44,46 @@ const createMockIntegrationContext = (createMockFn = noOp) => {
|
|
|
44
44
|
fatal: createMockFn()
|
|
45
45
|
};
|
|
46
46
|
// Make child() return the logger for method chaining
|
|
47
|
-
if (typeof childFn.mockReturnValue ===
|
|
47
|
+
if (typeof childFn.mockReturnValue ===
|
|
48
|
+
'function') {
|
|
48
49
|
childFn.mockReturnValue(logger);
|
|
49
50
|
}
|
|
50
51
|
else {
|
|
51
52
|
logger.child = () => logger;
|
|
52
53
|
}
|
|
54
|
+
// Create a limiter schedule passthrough that executes the provided callback
|
|
55
|
+
// (matching Bottleneck behavior) while remaining spyable.
|
|
56
|
+
// Supports both schedule(fn, ...args) and schedule(options, fn, ...args) overloads.
|
|
57
|
+
const scheduleImpl = async (fnOrOptions, ...rest) => {
|
|
58
|
+
const fn = (typeof fnOrOptions === 'function' ? fnOrOptions : rest[0]);
|
|
59
|
+
const args = typeof fnOrOptions === 'function' ? rest : rest.slice(1);
|
|
60
|
+
return fn(...args);
|
|
61
|
+
};
|
|
62
|
+
const countsImpl = () => ({
|
|
63
|
+
EXECUTING: 0,
|
|
64
|
+
QUEUED: 0,
|
|
65
|
+
RUNNING: 0,
|
|
66
|
+
DONE: 0,
|
|
67
|
+
RECEIVED: 0
|
|
68
|
+
});
|
|
69
|
+
const settingsImpl = () => ({});
|
|
70
|
+
const scheduleMock = createMockFn();
|
|
71
|
+
const countsMock = createMockFn();
|
|
72
|
+
const settingsMock = createMockFn();
|
|
73
|
+
const scopeMock = createMockFn();
|
|
74
|
+
const updateSettingsMock = createMockFn();
|
|
75
|
+
const hasMockImplementation = typeof scheduleMock
|
|
76
|
+
.mockImplementation === 'function';
|
|
77
|
+
if (hasMockImplementation) {
|
|
78
|
+
const setImpl = (mock, impl) => mock.mockImplementation(impl);
|
|
79
|
+
setImpl(scheduleMock, scheduleImpl);
|
|
80
|
+
setImpl(countsMock, countsImpl);
|
|
81
|
+
setImpl(settingsMock, settingsImpl);
|
|
82
|
+
setImpl(updateSettingsMock, async () => undefined);
|
|
83
|
+
setImpl(scopeMock, () => ({
|
|
84
|
+
schedule: scheduleMock
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
53
87
|
return {
|
|
54
88
|
cache: {
|
|
55
89
|
global: createCacheScope(),
|
|
@@ -59,8 +93,28 @@ const createMockIntegrationContext = (createMockFn = noOp) => {
|
|
|
59
93
|
integrationId: 'test-integration',
|
|
60
94
|
userId: 1,
|
|
61
95
|
logger,
|
|
96
|
+
limiter: {
|
|
97
|
+
schedule: (hasMockImplementation
|
|
98
|
+
? scheduleMock
|
|
99
|
+
: scheduleImpl),
|
|
100
|
+
updateSettings: (hasMockImplementation
|
|
101
|
+
? updateSettingsMock
|
|
102
|
+
: async () => undefined),
|
|
103
|
+
counts: (hasMockImplementation
|
|
104
|
+
? countsMock
|
|
105
|
+
: countsImpl),
|
|
106
|
+
settings: (hasMockImplementation
|
|
107
|
+
? settingsMock
|
|
108
|
+
: settingsImpl),
|
|
109
|
+
scope: (hasMockImplementation
|
|
110
|
+
? scopeMock
|
|
111
|
+
: () => ({ schedule: scheduleImpl }))
|
|
112
|
+
},
|
|
62
113
|
startPolling: createMockFn(),
|
|
63
|
-
stopPolling: createMockFn()
|
|
114
|
+
stopPolling: createMockFn(),
|
|
115
|
+
network: {
|
|
116
|
+
rejectUnauthorized: true
|
|
117
|
+
}
|
|
64
118
|
};
|
|
65
119
|
};
|
|
66
120
|
exports.createMockIntegrationContext = createMockIntegrationContext;
|
|
@@ -3,6 +3,8 @@ import type { Entity } from '@polarityio/integration-types';
|
|
|
3
3
|
import type { EntityType } from '@polarityio/integration-types';
|
|
4
4
|
import type { IntegrationContext } from '@polarityio/integration-types';
|
|
5
5
|
import type { Logger } from '@polarityio/integration-types';
|
|
6
|
+
import type { NetworkContext } from '@polarityio/integration-types';
|
|
7
|
+
import type { NetworkProxy } from '@polarityio/integration-types';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Hook that runs after a successful HTTP response. Each hook receives the output
|
|
@@ -519,14 +521,10 @@ export declare type ConfigRequestProxyOptions = {
|
|
|
519
521
|
}
|
|
520
522
|
|
|
521
523
|
/**
|
|
522
|
-
*
|
|
523
|
-
* The Polarity server provides a Bottleneck instance that satisfies this interface.
|
|
524
|
-
*
|
|
524
|
+
* @deprecated Use {@link PolarityRequestLimiter} instead. This alias will be removed in a future major version.
|
|
525
525
|
* @public
|
|
526
526
|
*/
|
|
527
|
-
export declare
|
|
528
|
-
schedule<T>(fn: (...args: unknown[]) => PromiseLike<T>, ...args: unknown[]): Promise<T>;
|
|
529
|
-
}
|
|
527
|
+
export declare type Limiter = PolarityRequestLimiter;
|
|
530
528
|
|
|
531
529
|
/**
|
|
532
530
|
* @public
|
|
@@ -555,6 +553,8 @@ export declare type ConfigRequestProxyOptions = {
|
|
|
555
553
|
*/
|
|
556
554
|
export declare type MockFnFactory = () => (...args: any[]) => any;
|
|
557
555
|
|
|
556
|
+
export { NetworkContext }
|
|
557
|
+
|
|
558
558
|
/**
|
|
559
559
|
* Generic network error for REST requests.
|
|
560
560
|
* https://betterstack.com/community/guides/scaling-nodejs/nodejs-errors/#4-econnrefused
|
|
@@ -565,6 +565,8 @@ export declare type ConfigRequestProxyOptions = {
|
|
|
565
565
|
constructor(message: string, properties?: IntegrationErrorProperties);
|
|
566
566
|
}
|
|
567
567
|
|
|
568
|
+
export { NetworkProxy }
|
|
569
|
+
|
|
568
570
|
/**
|
|
569
571
|
* Hook that runs when an API error is detected (non-success status code or error
|
|
570
572
|
* properties found in the response body). Receives the full HTTP response so the
|
|
@@ -660,7 +662,22 @@ export declare type ConfigRequestProxyOptions = {
|
|
|
660
662
|
* through this limiter. Typically provided by the Polarity server via the
|
|
661
663
|
* integration context.
|
|
662
664
|
*/
|
|
663
|
-
limiter:
|
|
665
|
+
limiter: PolarityRequestLimiter | null;
|
|
666
|
+
/**
|
|
667
|
+
* Per-integration network configuration from `context.network`.
|
|
668
|
+
*
|
|
669
|
+
* When set, proxy and TLS settings are applied dynamically on each
|
|
670
|
+
* {@link PolarityRequest.run} call, overriding any static values from
|
|
671
|
+
* the constructor `defaults`. Set this before each `run()` call to
|
|
672
|
+
* reflect the latest runtime config.
|
|
673
|
+
*
|
|
674
|
+
* @example
|
|
675
|
+
* ```typescript
|
|
676
|
+
* polarityRequest.network = context.network;
|
|
677
|
+
* const response = await polarityRequest.run(requestOptions);
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
680
|
+
network: NetworkContext | null;
|
|
664
681
|
/**
|
|
665
682
|
* Lifecycle hooks for customizing request behavior. Hooks are configured via the
|
|
666
683
|
* {@link PolarityRequestOptions.hooks} property when creating a new instance of the
|
|
@@ -784,18 +801,39 @@ export declare type ConfigRequestProxyOptions = {
|
|
|
784
801
|
onNetworkError?: OnNetworkErrorHook[];
|
|
785
802
|
}
|
|
786
803
|
|
|
804
|
+
/**
|
|
805
|
+
* Minimal interface for a rate limiter compatible with PolarityRequest.
|
|
806
|
+
* Only requires the `schedule` method, which is the sole method used internally.
|
|
807
|
+
* The Polarity server provides a full `Limiter` instance (from
|
|
808
|
+
* `@polarityio/integration-types`) that satisfies this interface, but consumers
|
|
809
|
+
* may also pass simpler objects or mocks that only implement `schedule`.
|
|
810
|
+
*
|
|
811
|
+
* @public
|
|
812
|
+
*/
|
|
813
|
+
export declare interface PolarityRequestLimiter {
|
|
814
|
+
schedule<T>(fn: (...args: unknown[]) => PromiseLike<T>, ...args: unknown[]): Promise<T>;
|
|
815
|
+
}
|
|
816
|
+
|
|
787
817
|
/**
|
|
788
818
|
* @public
|
|
789
819
|
*/
|
|
790
820
|
export declare interface PolarityRequestOptions {
|
|
791
821
|
defaults?: ConfigRequestProxyOptions;
|
|
822
|
+
/**
|
|
823
|
+
* Per-integration network configuration from `context.network`. When provided,
|
|
824
|
+
* proxy and TLS settings from this object override values from `defaults`.
|
|
825
|
+
*
|
|
826
|
+
* Can also be set (or updated) after construction via the {@link PolarityRequest.network}
|
|
827
|
+
* property to reflect per-request runtime config.
|
|
828
|
+
*/
|
|
829
|
+
network?: NetworkContext;
|
|
792
830
|
isApiError?: IsApiErrorFunction;
|
|
793
831
|
roundedSuccessStatusCodes?: number[];
|
|
794
832
|
httpResponseErrorProperties?: string[];
|
|
795
833
|
httpResponseErrorMessageProperties?: string[];
|
|
796
834
|
requestOptionsToSanitize?: string[];
|
|
797
835
|
hooks?: PolarityRequestHooks;
|
|
798
|
-
limiter?:
|
|
836
|
+
limiter?: PolarityRequestLimiter;
|
|
799
837
|
}
|
|
800
838
|
|
|
801
839
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polarity-integration-utils",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "A utility library for building Polarity Integrations",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"@eslint/js": "^9.39.4",
|
|
88
88
|
"@microsoft/api-extractor": "^7.57.7",
|
|
89
|
-
"@polarityio/integration-types": "^
|
|
89
|
+
"@polarityio/integration-types": "^2.0.0",
|
|
90
90
|
"@types/jest": "^30.0.0",
|
|
91
91
|
"@types/lodash": "^4.17.24",
|
|
92
92
|
"@types/node": "^20.14.8",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"postman-request": "^2.88.1-postman.48"
|
|
109
109
|
},
|
|
110
110
|
"peerDependencies": {
|
|
111
|
-
"@polarityio/integration-types": "
|
|
111
|
+
"@polarityio/integration-types": "^2.0.0"
|
|
112
112
|
},
|
|
113
113
|
"sideEffects": false,
|
|
114
114
|
"overrides": {
|