@tramvai/module-http-client 2.70.1 → 2.72.3

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.
@@ -1,237 +1,8 @@
1
- import { createToken } from '@tinkoff/dippy';
2
- import { Module, provide, Scope, APP_INFO_TOKEN } from '@tramvai/core';
3
- import { PAPI_SERVICE, HTTP_CLIENT_FACTORY, API_CLIENT_PASS_HEADERS, HTTP_CLIENT_AGENT, DISABLE_CIRCUIT_BREAKER, DEFAULT_HTTP_CLIENT_FACTORY_OPTIONS, HTTP_CLIENT } from '@tramvai/tokens-http-client';
1
+ export { HttpClientModule } from './httpClientModule.browser.js';
2
+ export { httpClientFactory } from './httpClient/httpClientFactory.browser.js';
3
+ import '@tramvai/core';
4
+ import '@tramvai/tokens-server';
4
5
  export * from '@tramvai/tokens-http-client';
5
- import { LOGGER_TOKEN, ENV_MANAGER_TOKEN, REQUEST_MANAGER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, ENV_USED_TOKEN, CREATE_CACHE_TOKEN } from '@tramvai/tokens-common';
6
- import isNil from '@tinkoff/utils/is/nil';
7
- import compose from '@tinkoff/utils/function/compose';
8
- import { mergeOptions, createTinkoffRequest, HttpClientAdapter } from '@tramvai/tinkoff-request-http-client-adapter';
9
- import { SERVER_MODULE_PAPI_PUBLIC_URL } from '@tramvai/tokens-server';
10
- import prop from '@tinkoff/utils/object/prop';
11
- import { ApiService } from '@tramvai/http-client';
6
+ export { PapiService } from './papi/papiService.browser.browser.js';
7
+ export { fillHeaderIp, fillHeaders } from './utils/fillHeaders.browser.browser.js';
12
8
  export * from '@tramvai/http-client';
13
-
14
- const fillHeaders = (deps) => (params) => {
15
- return params;
16
- };
17
- const fillHeaderIp = fillHeaders;
18
-
19
- const createUserAgent = ({ appInfo, envManager, }) => {
20
- const { appName } = appInfo;
21
- const appVersion = envManager.get('APP_VERSION');
22
- const appRelease = envManager.get('APP_RELEASE');
23
- const userAgent = [
24
- 'tramvai',
25
- appName,
26
- appVersion && `version ${appVersion}`,
27
- appRelease && `release ${appRelease}`,
28
- ]
29
- .filter(Boolean)
30
- .join(' ');
31
- return userAgent;
32
- };
33
-
34
- const environmentDependentOptions = typeof window === 'undefined'
35
- ? {
36
- defaultTimeout: 2000,
37
- }
38
- : {
39
- defaultTimeout: 30000,
40
- };
41
- const httpClientFactory = ({ logger, envManager, appInfo, requestManager, headersList, createCache, makeRequestRegistry, agent, querySerializer, disableCircuitBreaker = false, defaultOptions, commandLineExecutionContext, }) => {
42
- return (options) => {
43
- var _a;
44
- if (!options.name) {
45
- throw Error(`You need to pass a unique field "name" for the HTTP client instance`);
46
- }
47
- const forceDisableCache = envManager.get('HTTP_CLIENT_CACHE_DISABLED');
48
- const forceDisabledCircuitBreaker = envManager.get('HTTP_CLIENT_CIRCUIT_BREAKER_DISABLED');
49
- const adapterOptions = mergeOptions(mergeOptions({
50
- logger,
51
- agent,
52
- querySerializer,
53
- method: 'GET',
54
- createCache: createCache
55
- ? (cacheOptions) => createCache('memory', cacheOptions)
56
- : undefined,
57
- modifyRequest: compose(fillHeaderIp(), fillHeaders()),
58
- circuitBreakerOptions: {
59
- failureThreshold: 75,
60
- minimumFailureCount: 10,
61
- },
62
- // TODO: remove any after [resolving](https://github.com/southpolesteve/node-abort-controller/issues/31)
63
- signal: (_a = commandLineExecutionContext === null || commandLineExecutionContext === void 0 ? void 0 : commandLineExecutionContext()) === null || _a === void 0 ? void 0 : _a.abortSignal,
64
- ...environmentDependentOptions,
65
- }, defaultOptions !== null && defaultOptions !== void 0 ? defaultOptions : {}), options);
66
- // по умолчанию, на сервере, библиотека https://github.com/node-fetch/node-fetch
67
- // отправляет заголовок "User-Agent" вида "node-fetch".
68
- // для улучшения логов сервисов, в которые делают запросы tramvai приложения,
69
- // заменяем "User-Agent" на кастомный, содержащий название и версию приложения
70
- if (typeof window === 'undefined') {
71
- adapterOptions.headers = {
72
- 'User-Agent': createUserAgent({ appInfo, envManager }),
73
- ...adapterOptions.headers,
74
- };
75
- }
76
- if (!isNil(forceDisableCache)) {
77
- adapterOptions.disableCache = !!forceDisableCache;
78
- }
79
- if (disableCircuitBreaker) {
80
- adapterOptions.enableCircuitBreaker = false;
81
- }
82
- // environment variable in priority over disableCircuitBreaker
83
- if (!isNil(forceDisabledCircuitBreaker)) {
84
- adapterOptions.enableCircuitBreaker = !forceDisabledCircuitBreaker;
85
- }
86
- // кэшируем инстанс @tinkoff/request
87
- if (!makeRequestRegistry.has(adapterOptions.name)) {
88
- makeRequestRegistry.set(adapterOptions.name, createTinkoffRequest(adapterOptions));
89
- }
90
- const makeRequest = makeRequestRegistry.get(adapterOptions.name);
91
- const httpClientAdapter = new HttpClientAdapter({
92
- options: adapterOptions,
93
- makeRequest,
94
- });
95
- return httpClientAdapter;
96
- };
97
- };
98
-
99
- class PapiService extends ApiService {
100
- constructor({ httpClientFactory, baseUrl }) {
101
- const httpClient = httpClientFactory({
102
- name: 'papi',
103
- baseUrl,
104
- validator: ({ response }) => {
105
- if (response.resultCode && response.payload) {
106
- return;
107
- }
108
- return response.error || new Error(JSON.stringify(response));
109
- },
110
- modifyResponse(response) {
111
- return {
112
- ...response,
113
- payload: prop('payload', response.payload),
114
- };
115
- },
116
- });
117
- super(httpClient);
118
- }
119
- }
120
-
121
- const PapiClientModule = /* @__PURE__ */ Module({
122
- providers: [
123
- provide({
124
- provide: PAPI_SERVICE,
125
- scope: Scope.SINGLETON,
126
- useClass: PapiService,
127
- deps: {
128
- httpClientFactory: HTTP_CLIENT_FACTORY,
129
- baseUrl: SERVER_MODULE_PAPI_PUBLIC_URL,
130
- },
131
- }),
132
- ],
133
- })(class PapiClientModule {
134
- });
135
-
136
- const createCacheToken = createToken('httpClient createCache');
137
- const HttpClientModule = /* @__PURE__ */ Module({
138
- imports: [PapiClientModule],
139
- providers: [
140
- provide({
141
- provide: HTTP_CLIENT_FACTORY,
142
- useFactory: httpClientFactory,
143
- deps: {
144
- logger: LOGGER_TOKEN,
145
- envManager: ENV_MANAGER_TOKEN,
146
- appInfo: APP_INFO_TOKEN,
147
- createCache: createCacheToken,
148
- makeRequestRegistry: 'makeRequestRegistry',
149
- requestManager: {
150
- token: REQUEST_MANAGER_TOKEN,
151
- optional: true,
152
- },
153
- headersList: {
154
- token: API_CLIENT_PASS_HEADERS,
155
- optional: true,
156
- },
157
- agent: {
158
- token: HTTP_CLIENT_AGENT,
159
- optional: true,
160
- },
161
- disableCircuitBreaker: {
162
- token: DISABLE_CIRCUIT_BREAKER,
163
- optional: true,
164
- },
165
- defaultOptions: {
166
- token: DEFAULT_HTTP_CLIENT_FACTORY_OPTIONS,
167
- optional: true,
168
- },
169
- commandLineExecutionContext: {
170
- token: COMMAND_LINE_EXECUTION_CONTEXT_TOKEN,
171
- optional: true,
172
- },
173
- },
174
- }),
175
- provide({
176
- provide: HTTP_CLIENT,
177
- useFactory: ({ factory }) => {
178
- return factory({
179
- name: 'http-client',
180
- disableCache: true,
181
- });
182
- },
183
- deps: {
184
- factory: HTTP_CLIENT_FACTORY,
185
- },
186
- }),
187
- provide({
188
- provide: ENV_USED_TOKEN,
189
- useValue: [
190
- { key: 'HTTP_CLIENT_CACHE_DISABLED', optional: true, dehydrate: false },
191
- { key: 'HTTP_CLIENT_CIRCUIT_BREAKER_DISABLED', optional: true, dehydrate: false },
192
- ],
193
- }),
194
- /**
195
- * хранилище для экземпляров @tinkoff/request
196
- *
197
- * требуется хранить экземпляры в единственном виде,
198
- * т.к. многие плагины @tinkoff/request после инициализации имеют состояние
199
- * (cache, circuit breaker), и не будут корректно работать на сервере,
200
- * если создавать новые экземпляры на Scope.REQUEST
201
- */
202
- provide({
203
- provide: 'makeRequestRegistry',
204
- scope: Scope.SINGLETON,
205
- useFactory: () => new Map(),
206
- }),
207
- /**
208
- * `CREATE_CACHE_TOKEN` имеет проверку, если токен используется провайдером,
209
- * который имеет Scope.SINGLETON, то инстанс кэша сохраняется в общее хранилище,
210
- * и доступен для очистки через `/papi/clear-cache`.
211
- * Scope.REQUEST игнорируется, т.к. это верная утечка памяти,
212
- * инстансов кэши было бы неограниченное количество.
213
- *
214
- * HTTP клиенты создаются со Scope.REQUEST, но инстансы @tinkoff/request
215
- * (и соответственно кэшей) создаются только один раз, благодаря `makeRequestRegistry`.
216
- * это гарантирует отсутствие утечек памяти, поэтому мы обходим проверку
217
- * на Scope.SINGLETON c помощью обертки `createCacheToken`
218
- */
219
- provide({
220
- provide: createCacheToken,
221
- scope: Scope.SINGLETON,
222
- useFactory: ({ createCache }) => {
223
- return createCache;
224
- },
225
- deps: {
226
- createCache: CREATE_CACHE_TOKEN,
227
- },
228
- }),
229
- provide({
230
- provide: API_CLIENT_PASS_HEADERS,
231
- useValue: ['x-request-id'],
232
- }),
233
- ],
234
- })(class HttpClientModule {
235
- });
236
-
237
- export { HttpClientModule, PapiService, fillHeaderIp, fillHeaders, httpClientFactory };
package/lib/index.es.js CHANGED
@@ -1,275 +1,8 @@
1
- import { createChildContainer, createToken } from '@tinkoff/dippy';
2
- import { Module, provide, Scope, DI_TOKEN, APP_INFO_TOKEN } from '@tramvai/core';
3
- import { PAPI_SERVICE, HTTP_CLIENT_FACTORY, API_CLIENT_PASS_HEADERS, HTTP_CLIENT_AGENT, DISABLE_CIRCUIT_BREAKER, DEFAULT_HTTP_CLIENT_FACTORY_OPTIONS, HTTP_CLIENT } from '@tramvai/tokens-http-client';
1
+ export { HttpClientModule } from './httpClientModule.es.js';
2
+ export { httpClientFactory } from './httpClient/httpClientFactory.es.js';
3
+ import '@tramvai/core';
4
+ import '@tramvai/tokens-server';
4
5
  export * from '@tramvai/tokens-http-client';
5
- import { LOGGER_TOKEN, ENV_MANAGER_TOKEN, REQUEST_MANAGER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, ENV_USED_TOKEN, CREATE_CACHE_TOKEN } from '@tramvai/tokens-common';
6
- import isNil from '@tinkoff/utils/is/nil';
7
- import compose from '@tinkoff/utils/function/compose';
8
- import { mergeOptions, createTinkoffRequest, HttpClientAdapter } from '@tramvai/tinkoff-request-http-client-adapter';
9
- import identity from '@tinkoff/utils/function/identity';
10
- import flatten from '@tinkoff/utils/array/flatten';
11
- import pick from '@tinkoff/utils/object/pick';
12
- import { SERVER_MODULE_PAPI_PUBLIC_ROUTE } from '@tramvai/tokens-server';
13
- import find from '@tinkoff/utils/array/find';
14
- import { BaseHttpClient } from '@tramvai/http-client';
6
+ export { PapiService } from './papi/papiService.es.js';
7
+ export { fillHeaderIp, fillHeaders } from './utils/fillHeaders.es.js';
15
8
  export * from '@tramvai/http-client';
16
- import { getPapiParameters } from '@tramvai/papi';
17
- import { FASTIFY_REQUEST, FASTIFY_RESPONSE, PAPI_EXECUTOR } from '@tramvai/tokens-server-private';
18
-
19
- const fillHeaderIp = ({ requestManager, }) => {
20
- if (!requestManager) {
21
- return identity;
22
- }
23
- return (params) => {
24
- return {
25
- ...params,
26
- headers: {
27
- ...params.headers,
28
- 'X-real-ip': requestManager.getClientIp(),
29
- },
30
- };
31
- };
32
- };
33
- const fillHeaders = ({ requestManager, headersList, }) => {
34
- if (!requestManager) {
35
- return identity;
36
- }
37
- const headerNames = flatten(headersList !== null && headersList !== void 0 ? headersList : []);
38
- return (params) => {
39
- return {
40
- ...params,
41
- headers: {
42
- ...params.headers,
43
- ...pick(headerNames, requestManager.getHeaders()),
44
- },
45
- };
46
- };
47
- };
48
-
49
- const createUserAgent = ({ appInfo, envManager, }) => {
50
- const { appName } = appInfo;
51
- const appVersion = envManager.get('APP_VERSION');
52
- const appRelease = envManager.get('APP_RELEASE');
53
- const userAgent = [
54
- 'tramvai',
55
- appName,
56
- appVersion && `version ${appVersion}`,
57
- appRelease && `release ${appRelease}`,
58
- ]
59
- .filter(Boolean)
60
- .join(' ');
61
- return userAgent;
62
- };
63
-
64
- const environmentDependentOptions = typeof window === 'undefined'
65
- ? {
66
- defaultTimeout: 2000,
67
- }
68
- : {
69
- defaultTimeout: 30000,
70
- };
71
- const httpClientFactory = ({ logger, envManager, appInfo, requestManager, headersList, createCache, makeRequestRegistry, agent, querySerializer, disableCircuitBreaker = false, defaultOptions, commandLineExecutionContext, }) => {
72
- return (options) => {
73
- var _a;
74
- if (!options.name) {
75
- throw Error(`You need to pass a unique field "name" for the HTTP client instance`);
76
- }
77
- const forceDisableCache = envManager.get('HTTP_CLIENT_CACHE_DISABLED');
78
- const forceDisabledCircuitBreaker = envManager.get('HTTP_CLIENT_CIRCUIT_BREAKER_DISABLED');
79
- const adapterOptions = mergeOptions(mergeOptions({
80
- logger,
81
- agent,
82
- querySerializer,
83
- method: 'GET',
84
- createCache: createCache
85
- ? (cacheOptions) => createCache('memory', cacheOptions)
86
- : undefined,
87
- modifyRequest: compose(fillHeaderIp({ requestManager }), fillHeaders({ requestManager, headersList })),
88
- circuitBreakerOptions: {
89
- failureThreshold: 75,
90
- minimumFailureCount: 10,
91
- },
92
- // TODO: remove any after [resolving](https://github.com/southpolesteve/node-abort-controller/issues/31)
93
- signal: (_a = commandLineExecutionContext === null || commandLineExecutionContext === void 0 ? void 0 : commandLineExecutionContext()) === null || _a === void 0 ? void 0 : _a.abortSignal,
94
- ...environmentDependentOptions,
95
- }, defaultOptions !== null && defaultOptions !== void 0 ? defaultOptions : {}), options);
96
- // по умолчанию, на сервере, библиотека https://github.com/node-fetch/node-fetch
97
- // отправляет заголовок "User-Agent" вида "node-fetch".
98
- // для улучшения логов сервисов, в которые делают запросы tramvai приложения,
99
- // заменяем "User-Agent" на кастомный, содержащий название и версию приложения
100
- if (typeof window === 'undefined') {
101
- adapterOptions.headers = {
102
- 'User-Agent': createUserAgent({ appInfo, envManager }),
103
- ...adapterOptions.headers,
104
- };
105
- }
106
- if (!isNil(forceDisableCache)) {
107
- adapterOptions.disableCache = !!forceDisableCache;
108
- }
109
- if (disableCircuitBreaker) {
110
- adapterOptions.enableCircuitBreaker = false;
111
- }
112
- // environment variable in priority over disableCircuitBreaker
113
- if (!isNil(forceDisabledCircuitBreaker)) {
114
- adapterOptions.enableCircuitBreaker = !forceDisabledCircuitBreaker;
115
- }
116
- // кэшируем инстанс @tinkoff/request
117
- if (!makeRequestRegistry.has(adapterOptions.name)) {
118
- makeRequestRegistry.set(adapterOptions.name, createTinkoffRequest(adapterOptions));
119
- }
120
- const makeRequest = makeRequestRegistry.get(adapterOptions.name);
121
- const httpClientAdapter = new HttpClientAdapter({
122
- options: adapterOptions,
123
- makeRequest,
124
- });
125
- return httpClientAdapter;
126
- };
127
- };
128
-
129
- class PapiService extends BaseHttpClient {
130
- constructor({ papi, di }) {
131
- super();
132
- this.papi = flatten(papi || []);
133
- this.di = di;
134
- }
135
- async request({ path, query, body }) {
136
- var _a;
137
- const papiRoute = find((papi) => getPapiParameters(papi).path === `/${path}`, (_a = this.papi) !== null && _a !== void 0 ? _a : []);
138
- if (!papiRoute) {
139
- throw new Error(`papi handler '${path}' not found`);
140
- }
141
- const req = { headers: { host: 'localhost' }, cookies: {}, query, body };
142
- const res = {};
143
- const childDi = createChildContainer(this.di, [
144
- {
145
- provide: FASTIFY_REQUEST,
146
- useValue: req,
147
- },
148
- {
149
- provide: FASTIFY_RESPONSE,
150
- useValue: res,
151
- },
152
- ]);
153
- const papiExecutor = childDi.get(PAPI_EXECUTOR);
154
- const payload = await papiExecutor(papiRoute);
155
- return { payload, status: 200, headers: {} };
156
- }
157
- }
158
-
159
- const PapiClientModule = /* @__PURE__ */ Module({
160
- providers: [
161
- provide({
162
- provide: PAPI_SERVICE,
163
- scope: Scope.SINGLETON,
164
- useClass: PapiService,
165
- deps: {
166
- di: DI_TOKEN,
167
- papi: { token: SERVER_MODULE_PAPI_PUBLIC_ROUTE, optional: true },
168
- },
169
- }),
170
- ],
171
- })(class PapiClientModule {
172
- });
173
-
174
- const createCacheToken = createToken('httpClient createCache');
175
- const HttpClientModule = /* @__PURE__ */ Module({
176
- imports: [PapiClientModule],
177
- providers: [
178
- provide({
179
- provide: HTTP_CLIENT_FACTORY,
180
- useFactory: httpClientFactory,
181
- deps: {
182
- logger: LOGGER_TOKEN,
183
- envManager: ENV_MANAGER_TOKEN,
184
- appInfo: APP_INFO_TOKEN,
185
- createCache: createCacheToken,
186
- makeRequestRegistry: 'makeRequestRegistry',
187
- requestManager: {
188
- token: REQUEST_MANAGER_TOKEN,
189
- optional: true,
190
- },
191
- headersList: {
192
- token: API_CLIENT_PASS_HEADERS,
193
- optional: true,
194
- },
195
- agent: {
196
- token: HTTP_CLIENT_AGENT,
197
- optional: true,
198
- },
199
- disableCircuitBreaker: {
200
- token: DISABLE_CIRCUIT_BREAKER,
201
- optional: true,
202
- },
203
- defaultOptions: {
204
- token: DEFAULT_HTTP_CLIENT_FACTORY_OPTIONS,
205
- optional: true,
206
- },
207
- commandLineExecutionContext: {
208
- token: COMMAND_LINE_EXECUTION_CONTEXT_TOKEN,
209
- optional: true,
210
- },
211
- },
212
- }),
213
- provide({
214
- provide: HTTP_CLIENT,
215
- useFactory: ({ factory }) => {
216
- return factory({
217
- name: 'http-client',
218
- disableCache: true,
219
- });
220
- },
221
- deps: {
222
- factory: HTTP_CLIENT_FACTORY,
223
- },
224
- }),
225
- provide({
226
- provide: ENV_USED_TOKEN,
227
- useValue: [
228
- { key: 'HTTP_CLIENT_CACHE_DISABLED', optional: true, dehydrate: false },
229
- { key: 'HTTP_CLIENT_CIRCUIT_BREAKER_DISABLED', optional: true, dehydrate: false },
230
- ],
231
- }),
232
- /**
233
- * хранилище для экземпляров @tinkoff/request
234
- *
235
- * требуется хранить экземпляры в единственном виде,
236
- * т.к. многие плагины @tinkoff/request после инициализации имеют состояние
237
- * (cache, circuit breaker), и не будут корректно работать на сервере,
238
- * если создавать новые экземпляры на Scope.REQUEST
239
- */
240
- provide({
241
- provide: 'makeRequestRegistry',
242
- scope: Scope.SINGLETON,
243
- useFactory: () => new Map(),
244
- }),
245
- /**
246
- * `CREATE_CACHE_TOKEN` имеет проверку, если токен используется провайдером,
247
- * который имеет Scope.SINGLETON, то инстанс кэша сохраняется в общее хранилище,
248
- * и доступен для очистки через `/papi/clear-cache`.
249
- * Scope.REQUEST игнорируется, т.к. это верная утечка памяти,
250
- * инстансов кэши было бы неограниченное количество.
251
- *
252
- * HTTP клиенты создаются со Scope.REQUEST, но инстансы @tinkoff/request
253
- * (и соответственно кэшей) создаются только один раз, благодаря `makeRequestRegistry`.
254
- * это гарантирует отсутствие утечек памяти, поэтому мы обходим проверку
255
- * на Scope.SINGLETON c помощью обертки `createCacheToken`
256
- */
257
- provide({
258
- provide: createCacheToken,
259
- scope: Scope.SINGLETON,
260
- useFactory: ({ createCache }) => {
261
- return createCache;
262
- },
263
- deps: {
264
- createCache: CREATE_CACHE_TOKEN,
265
- },
266
- }),
267
- provide({
268
- provide: API_CLIENT_PASS_HEADERS,
269
- useValue: ['x-request-id'],
270
- }),
271
- ],
272
- })(class HttpClientModule {
273
- });
274
-
275
- export { HttpClientModule, PapiService, fillHeaderIp, fillHeaders, httpClientFactory };