@tramvai/module-server 2.21.1 → 2.24.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.
@@ -1,9 +1,10 @@
1
+ import type { FastifyInstance } from 'fastify';
1
2
  import type { Papi } from '@tramvai/papi';
2
- import type express from 'express';
3
3
  import type { DI_TOKEN } from '@tramvai/core';
4
4
  import type { LOGGER_TOKEN } from '@tramvai/tokens-common';
5
5
  export interface CreateOptions {
6
+ baseUrl: string;
6
7
  di: typeof DI_TOKEN;
7
8
  logger: typeof LOGGER_TOKEN;
8
9
  }
9
- export declare function createApi(papiList: Papi[], { di, logger }: CreateOptions): express.RequestHandler;
10
+ export declare function createApi(rootApp: FastifyInstance, papiList: Papi[], { baseUrl, di, logger }: CreateOptions): void;
@@ -0,0 +1,11 @@
1
+ /// <reference types="node" />
2
+ export declare const papiExecutorProvider: import("@tinkoff/dippy").Provider<{
3
+ di: import("@tinkoff/dippy").BaseTokenInterface<import("@tinkoff/dippy").Container>;
4
+ logger: import("@tinkoff/dippy").BaseTokenInterface<import("@tramvai/tokens-common").LoggerFactory>;
5
+ fastifyRequest: import("@tinkoff/dippy").BaseTokenInterface<import("fastify").FastifyRequest<import("fastify/types/route").RouteGenericInterface, import("fastify").RawServerDefault, import("http").IncomingMessage, unknown, import("fastify").FastifyLoggerInstance> & {
6
+ cookies: Record<string, string>;
7
+ query: Record<string, string>;
8
+ }>;
9
+ requestManager: import("@tinkoff/dippy").BaseTokenInterface<import("@tramvai/tokens-common").RequestManager>;
10
+ responseManager: import("@tinkoff/dippy").BaseTokenInterface<import("@tramvai/tokens-common").ResponseManager>;
11
+ }, import("@tinkoff/dippy").BaseTokenInterface<import("@tramvai/tokens-server-private").PapiExecutor>>;
@@ -0,0 +1,2 @@
1
+ import type { Provider } from '@tramvai/core';
2
+ export declare const fileApiProvider: Provider;
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Fork of https://github.com/fastify/fastify-express
3
3
  */
4
- /// <reference types="node" />
5
4
  import express from 'express';
6
5
  import type { FastifyPluginCallback } from 'fastify';
7
6
  declare module 'fastify' {
@@ -21,5 +20,5 @@ interface Options {
21
20
  instance: express.Application;
22
21
  };
23
22
  }
24
- export declare const fastifyExpressCompatibility: FastifyPluginCallback<Options, import("http").Server>;
23
+ export declare const fastifyExpressCompatibility: FastifyPluginCallback<Options, import("fastify").RawServerDefault>;
25
24
  export {};
@@ -3,7 +3,7 @@ import http from 'http';
3
3
  import type { SERVER_TOKEN } from '@tramvai/tokens-server';
4
4
  import type { LOGGER_TOKEN } from '@tramvai/tokens-common';
5
5
  import type { ENV_MANAGER_TOKEN } from '@tramvai/module-environment';
6
- export declare const serverFactory: () => http.Server;
6
+ export declare const serverFactory: () => http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
7
7
  export declare const serverListenCommand: ({ server, logger, envManager, }: {
8
8
  server: typeof SERVER_TOKEN;
9
9
  logger: typeof LOGGER_TOKEN;
@@ -6,7 +6,7 @@ import type { WEB_FASTIFY_APP_TOKEN, WEB_FASTIFY_APP_AFTER_INIT_TOKEN, WEB_FASTI
6
6
  import type { ExtractDependencyType } from '@tinkoff/dippy';
7
7
  export declare const webAppFactory: ({ server }: {
8
8
  server: typeof SERVER_TOKEN;
9
- }) => import("fastify").FastifyInstance<import("http").Server, import("http").IncomingMessage, import("http").ServerResponse, import("fastify").FastifyLoggerInstance> & PromiseLike<import("fastify").FastifyInstance<import("http").Server, import("http").IncomingMessage, import("http").ServerResponse, import("fastify").FastifyLoggerInstance>>;
9
+ }) => import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyLoggerInstance> & PromiseLike<import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyLoggerInstance>>;
10
10
  export declare const webAppExpressFactory: ({ webApp }: {
11
11
  webApp: typeof WEB_FASTIFY_APP_TOKEN;
12
12
  }) => import("express-serve-static-core").Express;
package/lib/server.es.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import { setDefaultResultOrder } from 'dns';
3
3
  import EventEmitter$1, { EventEmitter } from 'events';
4
- import { Scope, APP_INFO_TOKEN, Module, provide as provide$1, DI_TOKEN, COMMAND_LINE_RUNNER_TOKEN, commandLineListTokens, createToken } from '@tramvai/core';
5
- import { SERVER_MODULE_PAPI_PUBLIC_ROUTE, SERVER_MODULE_PAPI_PUBLIC_URL, SERVER_MODULE_PAPI_PRIVATE_URL, WEB_APP_BEFORE_INIT_TOKEN, WEB_APP_TOKEN, SERVER_MODULE_PAPI_PRIVATE_ROUTE, SERVER_MODULE_STATICS_OPTIONS, SERVER_TOKEN, READINESS_PROBE_TOKEN, LIVENESS_PROBE_TOKEN, UTILITY_SERVER_PATHS, PROXY_CONFIG_TOKEN, DEPENDENCIES_VERSION_FILTER_TOKEN, UTILITY_SERVER_PORT_TOKEN, WEB_APP_INIT_TOKEN, WEB_APP_AFTER_INIT_TOKEN, WEB_APP_LIMITER_TOKEN } from '@tramvai/tokens-server';
4
+ import { Scope, APP_INFO_TOKEN, Module, provide as provide$1, DI_TOKEN as DI_TOKEN$1, COMMAND_LINE_RUNNER_TOKEN, commandLineListTokens, createToken } from '@tramvai/core';
5
+ import { SERVER_MODULE_PAPI_PUBLIC_ROUTE, SERVER_MODULE_PAPI_PUBLIC_URL, SERVER_MODULE_PAPI_PRIVATE_URL, SERVER_MODULE_PAPI_PRIVATE_ROUTE, SERVER_MODULE_STATICS_OPTIONS, SERVER_TOKEN, READINESS_PROBE_TOKEN, LIVENESS_PROBE_TOKEN, UTILITY_SERVER_PATHS, WEB_APP_BEFORE_INIT_TOKEN, WEB_APP_TOKEN, PROXY_CONFIG_TOKEN, DEPENDENCIES_VERSION_FILTER_TOKEN, UTILITY_SERVER_PORT_TOKEN, WEB_APP_INIT_TOKEN, WEB_APP_AFTER_INIT_TOKEN, WEB_APP_LIMITER_TOKEN } from '@tramvai/tokens-server';
6
6
  export * from '@tramvai/tokens-server';
7
- import { WEB_FASTIFY_APP_BEFORE_INIT_TOKEN, UTILITY_WEB_FASTIFY_APP_TOKEN, UTILITY_SERVER_TOKEN, SERVER_FACTORY_TOKEN, WEB_FASTIFY_APP_TOKEN, WEB_FASTIFY_APP_FACTORY_TOKEN, WEB_FASTIFY_APP_INIT_TOKEN, WEB_FASTIFY_APP_AFTER_INIT_TOKEN, WEB_FASTIFY_APP_METRICS_TOKEN, WEB_FASTIFY_APP_LIMITER_TOKEN, WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN, WEB_FASTIFY_APP_PROCESS_ERROR_TOKEN, WEB_FASTIFY_APP_AFTER_ERROR_TOKEN } from '@tramvai/tokens-server-private';
8
- import { ROOT_EXECUTION_CONTEXT_TOKEN, REQUEST, RESPONSE, FASTIFY_REQUEST, FASTIFY_RESPONSE, RESPONSE_MANAGER_TOKEN, LOGGER_TOKEN, ENV_MANAGER_TOKEN, ENV_USED_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN } from '@tramvai/tokens-common';
7
+ import { PAPI_EXECUTOR, WEB_FASTIFY_APP_BEFORE_INIT_TOKEN, UTILITY_WEB_FASTIFY_APP_TOKEN, UTILITY_SERVER_TOKEN, SERVER_FACTORY_TOKEN, WEB_FASTIFY_APP_TOKEN, WEB_FASTIFY_APP_FACTORY_TOKEN, WEB_FASTIFY_APP_INIT_TOKEN, WEB_FASTIFY_APP_AFTER_INIT_TOKEN, WEB_FASTIFY_APP_METRICS_TOKEN, WEB_FASTIFY_APP_LIMITER_TOKEN, WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN, WEB_FASTIFY_APP_PROCESS_ERROR_TOKEN, WEB_FASTIFY_APP_AFTER_ERROR_TOKEN } from '@tramvai/tokens-server-private';
8
+ import { ROOT_EXECUTION_CONTEXT_TOKEN, REQUEST, RESPONSE, FASTIFY_REQUEST, FASTIFY_RESPONSE, RESPONSE_MANAGER_TOKEN, LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, ENV_MANAGER_TOKEN, ENV_USED_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN } from '@tramvai/tokens-common';
9
9
  import { MetricsModule } from '@tramvai/module-metrics';
10
10
  import { CacheWarmupModule } from '@tramvai/module-cache-warmup';
11
11
  import http from 'http';
@@ -13,18 +13,18 @@ import fastify from 'fastify';
13
13
  import express from 'express';
14
14
  import { fastifyCookie } from '@fastify/cookie';
15
15
  import fastifyFormBody from '@fastify/formbody';
16
- import { provide, createChildContainer } from '@tinkoff/dippy';
16
+ import { provide, createChildContainer, Scope as Scope$1, DI_TOKEN } from '@tinkoff/dippy';
17
17
  import fp from 'fastify-plugin';
18
18
  import symbols from 'fastify/lib/symbols';
19
19
  import isNil from '@tinkoff/utils/is/nil';
20
- import { isRedirectFoundError, isNotFoundError, isHttpError } from '@tinkoff/errors';
20
+ import { isRedirectFoundError, isNotFoundError, isHttpError, HttpError } from '@tinkoff/errors';
21
21
  import zlib from 'zlib';
22
22
  import compression from 'compression';
23
23
  import os from 'os';
24
24
  import filterObj from '@tinkoff/utils/object/filter';
25
25
  import flatten from '@tinkoff/utils/array/flatten';
26
26
  import toArray from '@tinkoff/utils/array/toArray';
27
- import { create, middlewares, getPapiParameters, createPapiMethod } from '@tramvai/papi';
27
+ import { getPapiParameters, isPapiMethod, createPapiMethod } from '@tramvai/papi';
28
28
  import eachObj from '@tinkoff/utils/object/each';
29
29
  import { resolve } from 'path';
30
30
  import FastifyStatic from '@fastify/static';
@@ -188,6 +188,7 @@ const errorHandler = (app, { log, beforeError, processError, afterError, }) => {
188
188
 
189
189
  const webAppFactory = ({ server }) => {
190
190
  const app = fastify({
191
+ ignoreTrailingSlash: true,
191
192
  serverFactory: (handler) => {
192
193
  server.on('request', handler);
193
194
  return server;
@@ -347,51 +348,102 @@ const xHeadersFactory = ({ app, envManager, appInfo, }) => {
347
348
  };
348
349
  };
349
350
 
350
- function createApi(papiList, { di, logger }) {
351
- return create(papiList, [
352
- middlewares.timeout(),
353
- middlewares.cookie(),
354
- middlewares.validate(),
355
- middlewares.fillPapiState({
356
- fillState: (papi) => {
357
- let rootDeps = {};
358
- if (papi.rootDeps) {
359
- rootDeps = di.getOfDeps(papi.rootDeps);
360
- rootDeps = papi.mapRootDeps ? papi.mapRootDeps(rootDeps) : rootDeps;
361
- }
362
- return (req, res) => {
351
+ function createApi(rootApp, papiList, { baseUrl, di, logger }) {
352
+ const paths = new Set();
353
+ const papiLog = logger('papi');
354
+ rootApp.register(async (app) => {
355
+ await app.register(fastifyCookie);
356
+ await app.register(fastifyFormBody, { bodyLimit: 2097152 }); // 2mb
357
+ for (const papi of papiList) {
358
+ const papiParams = getPapiParameters(papi);
359
+ if (!papiParams) {
360
+ throw new Error(`papi should be created using createPapiMethod from @tramvai/papi,
361
+ got: ${JSON.stringify(papi)}`);
362
+ }
363
+ const { method, path, options } = papiParams;
364
+ const { timeout, schema } = options;
365
+ if (!path) {
366
+ throw new Error(`No path in papi handler, got: ${JSON.stringify(papi)}`);
367
+ }
368
+ const key = `${method} ${path}`;
369
+ if (paths.has(key)) {
370
+ throw new Error(`papi: route '${key}' already registered`);
371
+ }
372
+ paths.add(key);
373
+ const childLog = papiLog.child(`${papiParams.method}_${papiParams.path}`);
374
+ app[method](path, {
375
+ schema,
376
+ errorHandler: async (error, req, res) => {
363
377
  var _a;
364
- const childDI = createChildContainer(di);
365
- childDI.register({
366
- provide: REQUEST,
378
+ res.status(error.validation ? 400 : 503);
379
+ childLog.error(error);
380
+ return {
381
+ resultCode: 'INTERNAL_ERROR',
382
+ errorMessage: (_a = error.message) !== null && _a !== void 0 ? _a : 'internal error',
383
+ };
384
+ },
385
+ }, async (req, res) => {
386
+ const childDi = createChildContainer(di, [
387
+ {
388
+ provide: FASTIFY_REQUEST,
389
+ scope: Scope$1.REQUEST,
367
390
  useValue: req,
368
- });
369
- childDI.register({
370
- provide: RESPONSE,
391
+ },
392
+ {
393
+ provide: FASTIFY_RESPONSE,
394
+ scope: Scope$1.REQUEST,
371
395
  useValue: res,
372
- });
373
- return { ...rootDeps, ...childDI.getOfDeps((_a = papi.deps) !== null && _a !== void 0 ? _a : {}), req, res };
396
+ },
397
+ // TODO: remove deprecated after removing express compatibility layer
398
+ {
399
+ provide: REQUEST,
400
+ scope: Scope$1.REQUEST,
401
+ useValue: req.raw,
402
+ },
403
+ {
404
+ provide: RESPONSE,
405
+ scope: Scope$1.REQUEST,
406
+ useValue: res.raw,
407
+ },
408
+ ]);
409
+ const papiExecutor = childDi.get(PAPI_EXECUTOR);
410
+ // TODO: use abortSignal
411
+ const payload = await Promise.race([
412
+ papiExecutor(papi),
413
+ new Promise((resolve, reject) => setTimeout(() => reject(new HttpError({ httpStatus: 503, message: 'Execution timeout' })), timeout)),
414
+ ]);
415
+ const responseManager = childDi.get(RESPONSE_MANAGER_TOKEN);
416
+ res.headers(responseManager.getHeaders()).status(responseManager.getStatus());
417
+ if (res.sent) {
418
+ return;
419
+ }
420
+ if (!payload && responseManager.getBody()) {
421
+ res.send(responseManager.getBody());
422
+ return res;
423
+ }
424
+ return {
425
+ resultCode: 'OK',
426
+ payload,
374
427
  };
375
- },
376
- }),
377
- middlewares.handler(),
378
- middlewares.error({ logger }),
379
- ]);
428
+ });
429
+ }
430
+ }, { prefix: baseUrl });
380
431
  }
381
432
 
382
- let Api;
433
+ let papis;
383
434
  try {
384
435
  // eslint-disable-next-line import/no-extraneous-dependencies
385
- Api = require('@tramvai/cli/lib/external/api').default; // eslint-disable-line import/no-unresolved
436
+ papis = require('@tramvai/cli/lib/external/api').default; // eslint-disable-line import/no-unresolved
386
437
  }
387
438
  catch (e) { }
388
439
  const getFileApi = ({ logger }) => {
389
440
  const log = logger('papi:fileApi');
390
441
  const result = [];
391
- eachObj((v, k) => {
392
- const handler = (v.handler || v.default);
393
- if (!handler) {
442
+ eachObj(({ default: entry }, path) => {
443
+ if (!isPapiMethod(entry)) {
394
444
  log.error({
445
+ path,
446
+ entry,
395
447
  message: `Cannot resolve a papi handler.
396
448
  Check that you are using file based papi right way by docs https://tramvai.dev/docs/how-to/how-create-papi#automatic-handler-creation
397
449
  In case you have not added any file papi handler, consider renaming directory ./src/api (by default) to the other name to resolve conflicts with papi, or
@@ -399,14 +451,12 @@ change settings application.commands.build.options.serverApiDir in tramvai.json`
399
451
  });
400
452
  throw new Error('Not a papi');
401
453
  }
402
- const papiParameters = getPapiParameters(handler);
454
+ const papiParameters = getPapiParameters(entry);
403
455
  result.push(createPapiMethod({
404
- ...v,
405
- ...(papiParameters !== null && papiParameters !== void 0 ? papiParameters : {}),
406
- path: `/${k}`,
407
- handler,
456
+ ...papiParameters,
457
+ path: `/${path}`,
408
458
  }));
409
- }, Api);
459
+ }, papis);
410
460
  return result;
411
461
  };
412
462
  const fileApiProvider = {
@@ -439,16 +489,50 @@ const sharedProviders = [
439
489
  },
440
490
  ];
441
491
 
492
+ const papiExecutorProvider = provide({
493
+ provide: PAPI_EXECUTOR,
494
+ scope: Scope$1.REQUEST,
495
+ useFactory: ({ di, logger, fastifyRequest, requestManager, responseManager }) => {
496
+ var _a;
497
+ const papiLog = logger('papi');
498
+ const papiOptions = {
499
+ requestManager,
500
+ responseManager,
501
+ params: (_a = fastifyRequest.params) !== null && _a !== void 0 ? _a : {},
502
+ cookies: requestManager.getCookies(),
503
+ headers: requestManager.getHeaders(),
504
+ body: requestManager.getBody(),
505
+ parsedUrl: requestManager.getParsedUrl(),
506
+ };
507
+ return (papi) => {
508
+ const { handler, deps, method, path } = getPapiParameters(papi);
509
+ const papiContext = {
510
+ deps: di.getOfDeps(deps),
511
+ log: papiLog.child(`${method}_${path}`),
512
+ };
513
+ return handler.call(papiContext, papiOptions);
514
+ };
515
+ },
516
+ deps: {
517
+ di: DI_TOKEN,
518
+ logger: LOGGER_TOKEN,
519
+ fastifyRequest: FASTIFY_REQUEST,
520
+ requestManager: REQUEST_MANAGER_TOKEN,
521
+ responseManager: RESPONSE_MANAGER_TOKEN,
522
+ },
523
+ });
524
+
442
525
  let ServerPapiModule = class ServerPapiModule {
443
526
  };
444
527
  ServerPapiModule = __decorate([
445
528
  Module({
446
529
  providers: [
530
+ papiExecutorProvider,
447
531
  fileApiProvider,
448
532
  ...sharedProviders,
449
533
  provide$1({
450
- provide: WEB_APP_BEFORE_INIT_TOKEN,
451
- useFactory: ({ di, app, logger, privateRoutes, publicRoutes, publicBaseUrl, privateBaseUrl, }) => () => {
534
+ provide: WEB_FASTIFY_APP_BEFORE_INIT_TOKEN,
535
+ useFactory: ({ di, logger, privateRoutes, publicRoutes, publicBaseUrl, privateBaseUrl }) => (app) => {
452
536
  if (process.env.NODE_ENV === 'development') {
453
537
  const papiListRoute = createPapiMethod({
454
538
  method: 'get',
@@ -482,23 +566,22 @@ ServerPapiModule = __decorate([
482
566
  : [papiListRoute];
483
567
  }
484
568
  if (privateRoutes) {
485
- const apiPrivate = createApi(flatten(privateRoutes), {
569
+ createApi(app, flatten(privateRoutes), {
570
+ baseUrl: privateBaseUrl,
486
571
  di,
487
572
  logger,
488
573
  });
489
- app.use(privateBaseUrl, apiPrivate);
490
574
  }
491
575
  if (publicRoutes) {
492
- const apiPublic = createApi(flatten(publicRoutes), {
576
+ createApi(app, flatten(publicRoutes), {
577
+ baseUrl: publicBaseUrl,
493
578
  di,
494
579
  logger,
495
580
  });
496
- app.use(publicBaseUrl, apiPublic);
497
581
  }
498
582
  },
499
583
  deps: {
500
- app: WEB_APP_TOKEN,
501
- di: DI_TOKEN,
584
+ di: DI_TOKEN$1,
502
585
  logger: LOGGER_TOKEN,
503
586
  privateRoutes: {
504
587
  token: SERVER_MODULE_PAPI_PRIVATE_ROUTE,
@@ -522,14 +605,14 @@ ServerPapiModule = __decorate([
522
605
  useValue: createPapiMethod({
523
606
  method: 'get',
524
607
  path: '/version',
525
- deps: {
526
- envManager: ENV_MANAGER_TOKEN,
527
- },
528
- async handler(req, res, { envManager }) {
608
+ async handler() {
529
609
  return {
530
- version: envManager.get('APP_VERSION'),
610
+ version: this.deps.envManager.get('APP_VERSION'),
531
611
  };
532
612
  },
613
+ deps: {
614
+ envManager: ENV_MANAGER_TOKEN,
615
+ },
533
616
  }),
534
617
  }),
535
618
  ],
@@ -751,8 +834,8 @@ ${parsed.href || `${parsed.protocol}//${parsed.hostname}${parsed.path}`}
751
834
  return createPapiMethod({
752
835
  method: 'post',
753
836
  path: '/debug-http-request',
754
- async handler({ req }) {
755
- const { delay = 10000 } = req.body;
837
+ async handler({ body }) {
838
+ const { delay = 10000 } = body;
756
839
  if (delay) {
757
840
  interceptor.setDelay(delay);
758
841
  }
package/lib/server.js CHANGED
@@ -214,6 +214,7 @@ const errorHandler = (app, { log, beforeError, processError, afterError, }) => {
214
214
 
215
215
  const webAppFactory = ({ server }) => {
216
216
  const app = fastify__default["default"]({
217
+ ignoreTrailingSlash: true,
217
218
  serverFactory: (handler) => {
218
219
  server.on('request', handler);
219
220
  return server;
@@ -373,51 +374,102 @@ const xHeadersFactory = ({ app, envManager, appInfo, }) => {
373
374
  };
374
375
  };
375
376
 
376
- function createApi(papiList, { di, logger }) {
377
- return papi.create(papiList, [
378
- papi.middlewares.timeout(),
379
- papi.middlewares.cookie(),
380
- papi.middlewares.validate(),
381
- papi.middlewares.fillPapiState({
382
- fillState: (papi) => {
383
- let rootDeps = {};
384
- if (papi.rootDeps) {
385
- rootDeps = di.getOfDeps(papi.rootDeps);
386
- rootDeps = papi.mapRootDeps ? papi.mapRootDeps(rootDeps) : rootDeps;
387
- }
388
- return (req, res) => {
377
+ function createApi(rootApp, papiList, { baseUrl, di, logger }) {
378
+ const paths = new Set();
379
+ const papiLog = logger('papi');
380
+ rootApp.register(async (app) => {
381
+ await app.register(cookie.fastifyCookie);
382
+ await app.register(fastifyFormBody__default["default"], { bodyLimit: 2097152 }); // 2mb
383
+ for (const papi$1 of papiList) {
384
+ const papiParams = papi.getPapiParameters(papi$1);
385
+ if (!papiParams) {
386
+ throw new Error(`papi should be created using createPapiMethod from @tramvai/papi,
387
+ got: ${JSON.stringify(papi$1)}`);
388
+ }
389
+ const { method, path, options } = papiParams;
390
+ const { timeout, schema } = options;
391
+ if (!path) {
392
+ throw new Error(`No path in papi handler, got: ${JSON.stringify(papi$1)}`);
393
+ }
394
+ const key = `${method} ${path}`;
395
+ if (paths.has(key)) {
396
+ throw new Error(`papi: route '${key}' already registered`);
397
+ }
398
+ paths.add(key);
399
+ const childLog = papiLog.child(`${papiParams.method}_${papiParams.path}`);
400
+ app[method](path, {
401
+ schema,
402
+ errorHandler: async (error, req, res) => {
389
403
  var _a;
390
- const childDI = dippy.createChildContainer(di);
391
- childDI.register({
392
- provide: tokensCommon.REQUEST,
404
+ res.status(error.validation ? 400 : 503);
405
+ childLog.error(error);
406
+ return {
407
+ resultCode: 'INTERNAL_ERROR',
408
+ errorMessage: (_a = error.message) !== null && _a !== void 0 ? _a : 'internal error',
409
+ };
410
+ },
411
+ }, async (req, res) => {
412
+ const childDi = dippy.createChildContainer(di, [
413
+ {
414
+ provide: tokensCommon.FASTIFY_REQUEST,
415
+ scope: dippy.Scope.REQUEST,
393
416
  useValue: req,
394
- });
395
- childDI.register({
396
- provide: tokensCommon.RESPONSE,
417
+ },
418
+ {
419
+ provide: tokensCommon.FASTIFY_RESPONSE,
420
+ scope: dippy.Scope.REQUEST,
397
421
  useValue: res,
398
- });
399
- return { ...rootDeps, ...childDI.getOfDeps((_a = papi.deps) !== null && _a !== void 0 ? _a : {}), req, res };
422
+ },
423
+ // TODO: remove deprecated after removing express compatibility layer
424
+ {
425
+ provide: tokensCommon.REQUEST,
426
+ scope: dippy.Scope.REQUEST,
427
+ useValue: req.raw,
428
+ },
429
+ {
430
+ provide: tokensCommon.RESPONSE,
431
+ scope: dippy.Scope.REQUEST,
432
+ useValue: res.raw,
433
+ },
434
+ ]);
435
+ const papiExecutor = childDi.get(tokensServerPrivate.PAPI_EXECUTOR);
436
+ // TODO: use abortSignal
437
+ const payload = await Promise.race([
438
+ papiExecutor(papi$1),
439
+ new Promise((resolve, reject) => setTimeout(() => reject(new errors.HttpError({ httpStatus: 503, message: 'Execution timeout' })), timeout)),
440
+ ]);
441
+ const responseManager = childDi.get(tokensCommon.RESPONSE_MANAGER_TOKEN);
442
+ res.headers(responseManager.getHeaders()).status(responseManager.getStatus());
443
+ if (res.sent) {
444
+ return;
445
+ }
446
+ if (!payload && responseManager.getBody()) {
447
+ res.send(responseManager.getBody());
448
+ return res;
449
+ }
450
+ return {
451
+ resultCode: 'OK',
452
+ payload,
400
453
  };
401
- },
402
- }),
403
- papi.middlewares.handler(),
404
- papi.middlewares.error({ logger }),
405
- ]);
454
+ });
455
+ }
456
+ }, { prefix: baseUrl });
406
457
  }
407
458
 
408
- let Api;
459
+ let papis;
409
460
  try {
410
461
  // eslint-disable-next-line import/no-extraneous-dependencies
411
- Api = require('@tramvai/cli/lib/external/api').default; // eslint-disable-line import/no-unresolved
462
+ papis = require('@tramvai/cli/lib/external/api').default; // eslint-disable-line import/no-unresolved
412
463
  }
413
464
  catch (e) { }
414
465
  const getFileApi = ({ logger }) => {
415
466
  const log = logger('papi:fileApi');
416
467
  const result = [];
417
- eachObj__default["default"]((v, k) => {
418
- const handler = (v.handler || v.default);
419
- if (!handler) {
468
+ eachObj__default["default"](({ default: entry }, path) => {
469
+ if (!papi.isPapiMethod(entry)) {
420
470
  log.error({
471
+ path,
472
+ entry,
421
473
  message: `Cannot resolve a papi handler.
422
474
  Check that you are using file based papi right way by docs https://tramvai.dev/docs/how-to/how-create-papi#automatic-handler-creation
423
475
  In case you have not added any file papi handler, consider renaming directory ./src/api (by default) to the other name to resolve conflicts with papi, or
@@ -425,14 +477,12 @@ change settings application.commands.build.options.serverApiDir in tramvai.json`
425
477
  });
426
478
  throw new Error('Not a papi');
427
479
  }
428
- const papiParameters = papi.getPapiParameters(handler);
480
+ const papiParameters = papi.getPapiParameters(entry);
429
481
  result.push(papi.createPapiMethod({
430
- ...v,
431
- ...(papiParameters !== null && papiParameters !== void 0 ? papiParameters : {}),
432
- path: `/${k}`,
433
- handler,
482
+ ...papiParameters,
483
+ path: `/${path}`,
434
484
  }));
435
- }, Api);
485
+ }, papis);
436
486
  return result;
437
487
  };
438
488
  const fileApiProvider = {
@@ -465,16 +515,50 @@ const sharedProviders = [
465
515
  },
466
516
  ];
467
517
 
518
+ const papiExecutorProvider = dippy.provide({
519
+ provide: tokensServerPrivate.PAPI_EXECUTOR,
520
+ scope: dippy.Scope.REQUEST,
521
+ useFactory: ({ di, logger, fastifyRequest, requestManager, responseManager }) => {
522
+ var _a;
523
+ const papiLog = logger('papi');
524
+ const papiOptions = {
525
+ requestManager,
526
+ responseManager,
527
+ params: (_a = fastifyRequest.params) !== null && _a !== void 0 ? _a : {},
528
+ cookies: requestManager.getCookies(),
529
+ headers: requestManager.getHeaders(),
530
+ body: requestManager.getBody(),
531
+ parsedUrl: requestManager.getParsedUrl(),
532
+ };
533
+ return (papi$1) => {
534
+ const { handler, deps, method, path } = papi.getPapiParameters(papi$1);
535
+ const papiContext = {
536
+ deps: di.getOfDeps(deps),
537
+ log: papiLog.child(`${method}_${path}`),
538
+ };
539
+ return handler.call(papiContext, papiOptions);
540
+ };
541
+ },
542
+ deps: {
543
+ di: dippy.DI_TOKEN,
544
+ logger: tokensCommon.LOGGER_TOKEN,
545
+ fastifyRequest: tokensCommon.FASTIFY_REQUEST,
546
+ requestManager: tokensCommon.REQUEST_MANAGER_TOKEN,
547
+ responseManager: tokensCommon.RESPONSE_MANAGER_TOKEN,
548
+ },
549
+ });
550
+
468
551
  let ServerPapiModule = class ServerPapiModule {
469
552
  };
470
553
  ServerPapiModule = tslib.__decorate([
471
554
  core.Module({
472
555
  providers: [
556
+ papiExecutorProvider,
473
557
  fileApiProvider,
474
558
  ...sharedProviders,
475
559
  core.provide({
476
- provide: tokensServer.WEB_APP_BEFORE_INIT_TOKEN,
477
- useFactory: ({ di, app, logger, privateRoutes, publicRoutes, publicBaseUrl, privateBaseUrl, }) => () => {
560
+ provide: tokensServerPrivate.WEB_FASTIFY_APP_BEFORE_INIT_TOKEN,
561
+ useFactory: ({ di, logger, privateRoutes, publicRoutes, publicBaseUrl, privateBaseUrl }) => (app) => {
478
562
  if (process.env.NODE_ENV === 'development') {
479
563
  const papiListRoute = papi.createPapiMethod({
480
564
  method: 'get',
@@ -508,22 +592,21 @@ ServerPapiModule = tslib.__decorate([
508
592
  : [papiListRoute];
509
593
  }
510
594
  if (privateRoutes) {
511
- const apiPrivate = createApi(flatten__default["default"](privateRoutes), {
595
+ createApi(app, flatten__default["default"](privateRoutes), {
596
+ baseUrl: privateBaseUrl,
512
597
  di,
513
598
  logger,
514
599
  });
515
- app.use(privateBaseUrl, apiPrivate);
516
600
  }
517
601
  if (publicRoutes) {
518
- const apiPublic = createApi(flatten__default["default"](publicRoutes), {
602
+ createApi(app, flatten__default["default"](publicRoutes), {
603
+ baseUrl: publicBaseUrl,
519
604
  di,
520
605
  logger,
521
606
  });
522
- app.use(publicBaseUrl, apiPublic);
523
607
  }
524
608
  },
525
609
  deps: {
526
- app: tokensServer.WEB_APP_TOKEN,
527
610
  di: core.DI_TOKEN,
528
611
  logger: tokensCommon.LOGGER_TOKEN,
529
612
  privateRoutes: {
@@ -548,14 +631,14 @@ ServerPapiModule = tslib.__decorate([
548
631
  useValue: papi.createPapiMethod({
549
632
  method: 'get',
550
633
  path: '/version',
551
- deps: {
552
- envManager: tokensCommon.ENV_MANAGER_TOKEN,
553
- },
554
- async handler(req, res, { envManager }) {
634
+ async handler() {
555
635
  return {
556
- version: envManager.get('APP_VERSION'),
636
+ version: this.deps.envManager.get('APP_VERSION'),
557
637
  };
558
638
  },
639
+ deps: {
640
+ envManager: tokensCommon.ENV_MANAGER_TOKEN,
641
+ },
559
642
  }),
560
643
  }),
561
644
  ],
@@ -777,8 +860,8 @@ ${parsed.href || `${parsed.protocol}//${parsed.hostname}${parsed.path}`}
777
860
  return papi.createPapiMethod({
778
861
  method: 'post',
779
862
  path: '/debug-http-request',
780
- async handler({ req }) {
781
- const { delay = 10000 } = req.body;
863
+ async handler({ body }) {
864
+ const { delay = 10000 } = body;
782
865
  if (delay) {
783
866
  interceptor.setDelay(delay);
784
867
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-server",
3
- "version": "2.21.1",
3
+ "version": "2.24.1",
4
4
  "description": "",
5
5
  "browser": "lib/browser.js",
6
6
  "main": "lib/server.js",
@@ -22,15 +22,15 @@
22
22
  "build-for-publish": "true"
23
23
  },
24
24
  "dependencies": {
25
- "@tinkoff/errors": "0.3.2",
25
+ "@tinkoff/errors": "0.3.3",
26
26
  "@tinkoff/monkeypatch": "2.0.2",
27
27
  "@tinkoff/terminus": "0.1.2",
28
28
  "@tinkoff/url": "0.8.2",
29
- "@tramvai/module-cache-warmup": "2.21.1",
30
- "@tramvai/module-metrics": "2.21.1",
31
- "@tramvai/papi": "2.21.1",
32
- "@tramvai/tokens-server": "2.21.1",
33
- "@tramvai/tokens-server-private": "2.21.1",
29
+ "@tramvai/module-cache-warmup": "2.24.1",
30
+ "@tramvai/module-metrics": "2.24.1",
31
+ "@tramvai/papi": "2.24.1",
32
+ "@tramvai/tokens-server": "2.24.1",
33
+ "@tramvai/tokens-server-private": "2.24.1",
34
34
  "compression": "^1.7.4",
35
35
  "express": "^4.17.1",
36
36
  "fastify": "^3.29.0",
@@ -43,11 +43,11 @@
43
43
  "peerDependencies": {
44
44
  "@tinkoff/dippy": "0.8.2",
45
45
  "@tinkoff/utils": "^2.1.2",
46
- "@tramvai/cli": "2.21.1",
47
- "@tramvai/core": "2.21.1",
48
- "@tramvai/module-common": "2.21.1",
49
- "@tramvai/module-environment": "2.21.1",
50
- "@tramvai/tokens-common": "2.21.1",
46
+ "@tramvai/cli": "2.24.1",
47
+ "@tramvai/core": "2.24.1",
48
+ "@tramvai/module-common": "2.24.1",
49
+ "@tramvai/module-environment": "2.24.1",
50
+ "@tramvai/tokens-common": "2.24.1",
51
51
  "tslib": "^2.0.3"
52
52
  },
53
53
  "devDependencies": {
@@ -1,8 +0,0 @@
1
- import type { Provider } from '@tramvai/core';
2
- declare module '@tramvai/papi/lib/types' {
3
- interface PapiParameters<Deps, Result> {
4
- rootDeps?: Record<string, any>;
5
- mapRootDeps?: (rootDeps: Record<string, any>) => Record<string, any>;
6
- }
7
- }
8
- export declare const fileApiProvider: Provider;