@chainlink/external-adapter-framework 0.24.2 → 0.25.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.
- package/background-executor.js +20 -9
- package/background-executor.js.map +1 -1
- package/cache/index.js +9 -3
- package/cache/index.js.map +1 -1
- package/config/index.d.ts +0 -4
- package/config/index.js +0 -4
- package/config/index.js.map +1 -1
- package/index.d.ts +4 -0
- package/index.js +25 -56
- package/index.js.map +1 -1
- package/metrics/index.d.ts +5 -3
- package/metrics/index.js +8 -6
- package/metrics/index.js.map +1 -1
- package/package.json +5 -4
- package/rate-limiting/index.js +7 -2
- package/rate-limiting/index.js.map +1 -1
- package/transports/http.js +40 -22
- package/transports/http.js.map +1 -1
- package/transports/meta/routing.d.ts +2 -5
- package/transports/meta/routing.js +4 -10
- package/transports/meta/routing.js.map +1 -1
- package/transports/sse.d.ts +2 -0
- package/transports/sse.js +7 -12
- package/transports/sse.js.map +1 -1
- package/transports/websocket.js +14 -2
- package/transports/websocket.js.map +1 -1
- package/util/logger.d.ts +5 -1
- package/util/logger.js +3 -1
- package/util/logger.js.map +1 -1
- package/util/requester.js +18 -9
- package/util/requester.js.map +1 -1
- package/validation/error.d.ts +4 -1
- package/validation/error.js +1 -0
- package/validation/error.js.map +1 -1
- package/validation/index.js +6 -2
- package/validation/index.js.map +1 -1
- package/config/provider-limits.d.ts +0 -27
- package/config/provider-limits.js +0 -75
- package/config/provider-limits.js.map +0 -1
- package/util/test-payload-loader.d.ts +0 -26
- package/util/test-payload-loader.js +0 -85
- package/util/test-payload-loader.js.map +0 -1
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getWSLimits = exports.getRateLimit = exports.getHTTPLimit = exports.DEFAULT_WS_SUBSCRIPTIONS = exports.DEFAULT_WS_CONNECTIONS = exports.BURST_UNDEFINED_QUOTA_MULTIPLE = exports.DEFAULT_MINUTE_RATE_LIMIT = void 0;
|
|
4
|
-
const util_1 = require("../util");
|
|
5
|
-
exports.DEFAULT_MINUTE_RATE_LIMIT = 60;
|
|
6
|
-
exports.BURST_UNDEFINED_QUOTA_MULTIPLE = 2;
|
|
7
|
-
exports.DEFAULT_WS_CONNECTIONS = 2;
|
|
8
|
-
exports.DEFAULT_WS_SUBSCRIPTIONS = 10;
|
|
9
|
-
const logger = (0, util_1.makeLogger)('ProviderLimits');
|
|
10
|
-
const getHTTPLimit = (provider, limits, tier, timeframe) => {
|
|
11
|
-
const providerLimit = getProviderLimits(provider, limits, tier, 'http');
|
|
12
|
-
return providerLimit?.[timeframe] || 0;
|
|
13
|
-
};
|
|
14
|
-
exports.getHTTPLimit = getHTTPLimit;
|
|
15
|
-
const getRateLimit = (provider, limits, tier) => {
|
|
16
|
-
const providerLimit = getProviderLimits(provider, limits, tier, 'http');
|
|
17
|
-
return calculateRateLimit(providerLimit);
|
|
18
|
-
};
|
|
19
|
-
exports.getRateLimit = getRateLimit;
|
|
20
|
-
const getWSLimits = (provider, limits, tier) => {
|
|
21
|
-
const providerLimit = getProviderLimits(provider, limits, tier, 'ws');
|
|
22
|
-
return calculateWSLimits(providerLimit);
|
|
23
|
-
};
|
|
24
|
-
exports.getWSLimits = getWSLimits;
|
|
25
|
-
const getProviderLimits = (provider, limits, tier, protocol) => {
|
|
26
|
-
const providerConfig = parseLimits(limits);
|
|
27
|
-
if (!providerConfig) {
|
|
28
|
-
throw new Error(`Rate Limit: Provider: "${provider}" doesn't match any provider spec in the adapter rate limiting configurations`);
|
|
29
|
-
}
|
|
30
|
-
const protocolConfig = providerConfig[protocol];
|
|
31
|
-
if (!protocolConfig) {
|
|
32
|
-
throw new Error(`Rate Limit: "${provider}" doesn't have any configuration for ${protocol} in the adapter rate limiting configurations`);
|
|
33
|
-
}
|
|
34
|
-
let limitsConfig = protocolConfig[tier.toLowerCase()];
|
|
35
|
-
if (!limitsConfig) {
|
|
36
|
-
logger.debug(`Rate Limit: "${provider} does not have tier ${tier} defined. Falling back to lowest tier"`);
|
|
37
|
-
limitsConfig = Object.values(protocolConfig)?.[0];
|
|
38
|
-
}
|
|
39
|
-
if (!limitsConfig) {
|
|
40
|
-
throw new Error(`Rate Limit: Provider: "${provider}" has no tiers defined for ${protocol} in the adapter rate limiting configurations`);
|
|
41
|
-
}
|
|
42
|
-
return limitsConfig;
|
|
43
|
-
};
|
|
44
|
-
const objectKeysToLowercase = (obj) => {
|
|
45
|
-
const lower = {};
|
|
46
|
-
for (const key in obj) {
|
|
47
|
-
lower[key.toLowerCase()] = obj[key];
|
|
48
|
-
}
|
|
49
|
-
return lower;
|
|
50
|
-
};
|
|
51
|
-
const parseLimits = (limits) => {
|
|
52
|
-
limits.http = objectKeysToLowercase(limits.http);
|
|
53
|
-
limits.ws = objectKeysToLowercase(limits.ws);
|
|
54
|
-
return limits;
|
|
55
|
-
};
|
|
56
|
-
const calculateWSLimits = (providerLimit) => {
|
|
57
|
-
return {
|
|
58
|
-
connections: providerLimit.connections,
|
|
59
|
-
subscriptions: providerLimit.subscriptions,
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
const calculateRateLimit = (providerLimit) => {
|
|
63
|
-
let quota = providerLimit.rateLimit1m;
|
|
64
|
-
if (!quota && providerLimit?.rateLimit1h) {
|
|
65
|
-
quota = providerLimit?.rateLimit1h / 60;
|
|
66
|
-
}
|
|
67
|
-
else if (!quota && providerLimit?.rateLimit1s) {
|
|
68
|
-
quota = providerLimit?.rateLimit1s * 60;
|
|
69
|
-
}
|
|
70
|
-
return {
|
|
71
|
-
second: providerLimit?.rateLimit1s || (quota / 60) * exports.BURST_UNDEFINED_QUOTA_MULTIPLE,
|
|
72
|
-
minute: quota,
|
|
73
|
-
};
|
|
74
|
-
};
|
|
75
|
-
//# sourceMappingURL=provider-limits.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provider-limits.js","sourceRoot":"","sources":["../../../src/config/provider-limits.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { AdapterRequestData } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* The test payload read in from filesystem
|
|
4
|
-
*/
|
|
5
|
-
export interface Payload {
|
|
6
|
-
requests: Array<AdapterRequestData>;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Test payload with discriminated union so we can tell when we should just do
|
|
10
|
-
* a simple liveness check rather than a sample request
|
|
11
|
-
*/
|
|
12
|
-
type TestPayload = (Payload & {
|
|
13
|
-
isDefault: false;
|
|
14
|
-
}) | {
|
|
15
|
-
isDefault: true;
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* Load in a JSON file containing a test payload for the current adapter,
|
|
19
|
-
* used in healthchecks to make sample requests
|
|
20
|
-
*
|
|
21
|
-
* @param fileName - name of file that contains the test payload data for the smoke endpoint
|
|
22
|
-
* @returns the parsed payload with individual requests
|
|
23
|
-
*/
|
|
24
|
-
export declare function loadTestPayload(fileName?: string): TestPayload;
|
|
25
|
-
export declare function resolvePayload(fileName?: string): Payload | null;
|
|
26
|
-
export {};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolvePayload = exports.loadTestPayload = void 0;
|
|
7
|
-
const ajv_1 = __importDefault(require("ajv"));
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const logger_1 = require("./logger");
|
|
11
|
-
const logger = (0, logger_1.makeLogger)('TestPayloadLoader');
|
|
12
|
-
/**
|
|
13
|
-
* Load in a JSON file containing a test payload for the current adapter,
|
|
14
|
-
* used in healthchecks to make sample requests
|
|
15
|
-
*
|
|
16
|
-
* @param fileName - name of file that contains the test payload data for the smoke endpoint
|
|
17
|
-
* @returns the parsed payload with individual requests
|
|
18
|
-
*/
|
|
19
|
-
function loadTestPayload(fileName) {
|
|
20
|
-
const ajv = new ajv_1.default();
|
|
21
|
-
const schema = {
|
|
22
|
-
type: 'object',
|
|
23
|
-
required: ['requests'],
|
|
24
|
-
properties: {
|
|
25
|
-
requests: {
|
|
26
|
-
type: 'array',
|
|
27
|
-
items: {
|
|
28
|
-
type: 'object',
|
|
29
|
-
required: [],
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
const validate = ajv.compile(schema);
|
|
35
|
-
try {
|
|
36
|
-
const payload = resolvePayload(fileName);
|
|
37
|
-
if (!validate(payload) || !payload?.requests) {
|
|
38
|
-
throw Error(JSON.stringify(validate?.errors || 'Could not validate schema for test payload'));
|
|
39
|
-
}
|
|
40
|
-
return { ...payload, isDefault: false };
|
|
41
|
-
}
|
|
42
|
-
catch (e) {
|
|
43
|
-
logger.warn(`Could not load payload: ${e.message}`);
|
|
44
|
-
logger.warn('Falling back to default empty payload');
|
|
45
|
-
return { isDefault: true };
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
exports.loadTestPayload = loadTestPayload;
|
|
49
|
-
function resolvePayload(fileName) {
|
|
50
|
-
try {
|
|
51
|
-
let payload = null;
|
|
52
|
-
// Find test payload by specified name, fallback to default names if not found
|
|
53
|
-
if (fileName && fs_1.default.existsSync(path_1.default.join(process.cwd(), fileName))) {
|
|
54
|
-
payload = require(path_1.default.join(process.cwd(), fileName));
|
|
55
|
-
}
|
|
56
|
-
else if (fileName && fs_1.default.existsSync(`./${fileName}`)) {
|
|
57
|
-
payload = require(`./${fileName}`);
|
|
58
|
-
}
|
|
59
|
-
// Search for test payload js first if no filename specified or found
|
|
60
|
-
else if (fs_1.default.existsSync(path_1.default.resolve('.', 'test-payload.js'))) {
|
|
61
|
-
payload = require(path_1.default.join(process.cwd(), 'test-payload.js'));
|
|
62
|
-
}
|
|
63
|
-
else if (fs_1.default.existsSync(`./test-payload.js`)) {
|
|
64
|
-
payload = require(`./test-payload.js`);
|
|
65
|
-
}
|
|
66
|
-
// Search for test payload json second if no filename specified or found
|
|
67
|
-
else if (fs_1.default.existsSync(path_1.default.join(process.cwd(), 'test-payload.json'))) {
|
|
68
|
-
payload = require(path_1.default.join(process.cwd(), 'test-payload.json'));
|
|
69
|
-
}
|
|
70
|
-
else if (fs_1.default.existsSync(`./test-payload.json`)) {
|
|
71
|
-
payload = require(`./test-payload.json`);
|
|
72
|
-
}
|
|
73
|
-
if (typeof payload === 'string') {
|
|
74
|
-
return JSON.parse(payload);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
return payload;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
exports.resolvePayload = resolvePayload;
|
|
85
|
-
//# sourceMappingURL=test-payload-loader.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-payload-loader.js","sourceRoot":"","sources":["../../../src/util/test-payload-loader.ts"],"names":[],"mappings":"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"}
|