@tramvai/module-common 5.16.2 → 5.17.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.
@@ -4,8 +4,7 @@ import { Module, provide, Scope, BUNDLE_LIST_TOKEN, DI_TOKEN } from '@tramvai/co
4
4
  import { EnvironmentModule } from '@tramvai/module-environment';
5
5
  import { CookieModule } from '@tramvai/module-cookie';
6
6
  import { LogModule } from '@tramvai/module-log';
7
- import { Hooks } from '@tinkoff/hook-runner';
8
- import { HOOK_TOKEN, COMPONENT_REGISTRY_TOKEN, BUNDLE_MANAGER_TOKEN, ADDITIONAL_BUNDLE_TOKEN, LOGGER_TOKEN, CONTEXT_TOKEN, PUBSUB_TOKEN, DISPATCHER_CONTEXT_TOKEN, STORE_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN } from '@tramvai/tokens-common';
7
+ import { COMPONENT_REGISTRY_TOKEN, BUNDLE_MANAGER_TOKEN, ADDITIONAL_BUNDLE_TOKEN, LOGGER_TOKEN, CONTEXT_TOKEN, PUBSUB_TOKEN, DISPATCHER_CONTEXT_TOKEN, STORE_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN } from '@tramvai/tokens-common';
9
8
  import { BundleManager } from './bundleManager/bundleManager.browser.js';
10
9
  import { ComponentRegistry } from './componentRegistry/componentRegistry.browser.js';
11
10
  import { RequestManagerModule } from './requestManager/RequestManagerModule.browser.browser.js';
@@ -19,12 +18,14 @@ import { ActionModule } from './actions/ActionModule.browser.js';
19
18
  import { StateModule } from './state/StateModule.browser.js';
20
19
  import { CacheModule } from './cache/CacheModule.browser.js';
21
20
  import { ExecutionContextManager } from './executionContext/executionContextManager.browser.js';
21
+ import { TramvaiHookModule } from './hook/HookModule.browser.js';
22
22
 
23
23
  let CommonModule = class CommonModule {
24
24
  };
25
25
  CommonModule = __decorate([
26
26
  Module({
27
27
  imports: [
28
+ TramvaiHookModule,
28
29
  CommandModule,
29
30
  EnvironmentModule,
30
31
  PubSubModule,
@@ -39,13 +40,6 @@ CommonModule = __decorate([
39
40
  ],
40
41
  providers: [
41
42
  provide({
42
- // Инстанс хук системы
43
- provide: HOOK_TOKEN,
44
- scope: Scope.SINGLETON,
45
- useClass: Hooks,
46
- }),
47
- provide({
48
- // Регистр ui компонентов
49
43
  provide: COMPONENT_REGISTRY_TOKEN,
50
44
  scope: Scope.SINGLETON,
51
45
  useClass: ComponentRegistry,
@@ -54,7 +48,6 @@ CommonModule = __decorate([
54
48
  },
55
49
  }),
56
50
  provide({
57
- // Управление бандлами, хранение и получение
58
51
  provide: BUNDLE_MANAGER_TOKEN,
59
52
  scope: Scope.SINGLETON,
60
53
  useFactory: ({ additionalBundleList, ...bundleManagerDeps }) => {
@@ -77,7 +70,6 @@ CommonModule = __decorate([
77
70
  },
78
71
  }),
79
72
  provide({
80
- // Клиентский контекст исполнения
81
73
  provide: CONTEXT_TOKEN,
82
74
  scope: Scope.REQUEST,
83
75
  useFactory: createConsumerContext,
@@ -4,8 +4,7 @@ import { Module, provide, Scope, BUNDLE_LIST_TOKEN, DI_TOKEN } from '@tramvai/co
4
4
  import { EnvironmentModule } from '@tramvai/module-environment';
5
5
  import { CookieModule } from '@tramvai/module-cookie';
6
6
  import { LogModule } from '@tramvai/module-log';
7
- import { Hooks } from '@tinkoff/hook-runner';
8
- import { HOOK_TOKEN, COMPONENT_REGISTRY_TOKEN, BUNDLE_MANAGER_TOKEN, ADDITIONAL_BUNDLE_TOKEN, LOGGER_TOKEN, CONTEXT_TOKEN, PUBSUB_TOKEN, DISPATCHER_CONTEXT_TOKEN, STORE_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN } from '@tramvai/tokens-common';
7
+ import { COMPONENT_REGISTRY_TOKEN, BUNDLE_MANAGER_TOKEN, ADDITIONAL_BUNDLE_TOKEN, LOGGER_TOKEN, CONTEXT_TOKEN, PUBSUB_TOKEN, DISPATCHER_CONTEXT_TOKEN, STORE_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN } from '@tramvai/tokens-common';
9
8
  import { BundleManager } from './bundleManager/bundleManager.es.js';
10
9
  import { ComponentRegistry } from './componentRegistry/componentRegistry.es.js';
11
10
  import { RequestManagerModule } from './requestManager/RequestManagerModule.es.js';
@@ -19,12 +18,14 @@ import { ActionModule } from './actions/ActionModule.es.js';
19
18
  import { StateModule } from './state/StateModule.es.js';
20
19
  import { CacheModule } from './cache/CacheModule.es.js';
21
20
  import { ExecutionContextManager } from './executionContext/executionContextManager.es.js';
21
+ import { TramvaiHookModule } from './hook/HookModule.es.js';
22
22
 
23
23
  let CommonModule = class CommonModule {
24
24
  };
25
25
  CommonModule = __decorate([
26
26
  Module({
27
27
  imports: [
28
+ TramvaiHookModule,
28
29
  CommandModule,
29
30
  EnvironmentModule,
30
31
  PubSubModule,
@@ -39,13 +40,6 @@ CommonModule = __decorate([
39
40
  ],
40
41
  providers: [
41
42
  provide({
42
- // Инстанс хук системы
43
- provide: HOOK_TOKEN,
44
- scope: Scope.SINGLETON,
45
- useClass: Hooks,
46
- }),
47
- provide({
48
- // Регистр ui компонентов
49
43
  provide: COMPONENT_REGISTRY_TOKEN,
50
44
  scope: Scope.SINGLETON,
51
45
  useClass: ComponentRegistry,
@@ -54,7 +48,6 @@ CommonModule = __decorate([
54
48
  },
55
49
  }),
56
50
  provide({
57
- // Управление бандлами, хранение и получение
58
51
  provide: BUNDLE_MANAGER_TOKEN,
59
52
  scope: Scope.SINGLETON,
60
53
  useFactory: ({ additionalBundleList, ...bundleManagerDeps }) => {
@@ -77,7 +70,6 @@ CommonModule = __decorate([
77
70
  },
78
71
  }),
79
72
  provide({
80
- // Клиентский контекст исполнения
81
73
  provide: CONTEXT_TOKEN,
82
74
  scope: Scope.REQUEST,
83
75
  useFactory: createConsumerContext,
@@ -8,7 +8,6 @@ var core = require('@tramvai/core');
8
8
  var moduleEnvironment = require('@tramvai/module-environment');
9
9
  var moduleCookie = require('@tramvai/module-cookie');
10
10
  var moduleLog = require('@tramvai/module-log');
11
- var hookRunner = require('@tinkoff/hook-runner');
12
11
  var tokensCommon = require('@tramvai/tokens-common');
13
12
  var bundleManager = require('./bundleManager/bundleManager.js');
14
13
  var componentRegistry = require('./componentRegistry/componentRegistry.js');
@@ -23,6 +22,7 @@ var ActionModule = require('./actions/ActionModule.js');
23
22
  var StateModule = require('./state/StateModule.js');
24
23
  var CacheModule = require('./cache/CacheModule.js');
25
24
  var executionContextManager = require('./executionContext/executionContextManager.js');
25
+ var HookModule = require('./hook/HookModule.js');
26
26
 
27
27
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
28
28
 
@@ -33,6 +33,7 @@ exports.CommonModule = class CommonModule {
33
33
  exports.CommonModule = tslib.__decorate([
34
34
  core.Module({
35
35
  imports: [
36
+ HookModule.TramvaiHookModule,
36
37
  CommandModule.CommandModule,
37
38
  moduleEnvironment.EnvironmentModule,
38
39
  PubSubModule.PubSubModule,
@@ -47,13 +48,6 @@ exports.CommonModule = tslib.__decorate([
47
48
  ],
48
49
  providers: [
49
50
  core.provide({
50
- // Инстанс хук системы
51
- provide: tokensCommon.HOOK_TOKEN,
52
- scope: core.Scope.SINGLETON,
53
- useClass: hookRunner.Hooks,
54
- }),
55
- core.provide({
56
- // Регистр ui компонентов
57
51
  provide: tokensCommon.COMPONENT_REGISTRY_TOKEN,
58
52
  scope: core.Scope.SINGLETON,
59
53
  useClass: componentRegistry.ComponentRegistry,
@@ -62,7 +56,6 @@ exports.CommonModule = tslib.__decorate([
62
56
  },
63
57
  }),
64
58
  core.provide({
65
- // Управление бандлами, хранение и получение
66
59
  provide: tokensCommon.BUNDLE_MANAGER_TOKEN,
67
60
  scope: core.Scope.SINGLETON,
68
61
  useFactory: ({ additionalBundleList, ...bundleManagerDeps }) => {
@@ -85,7 +78,6 @@ exports.CommonModule = tslib.__decorate([
85
78
  },
86
79
  }),
87
80
  core.provide({
88
- // Клиентский контекст исполнения
89
81
  provide: tokensCommon.CONTEXT_TOKEN,
90
82
  scope: core.Scope.REQUEST,
91
83
  useFactory: createConsumerContext.createConsumerContext,
@@ -1,25 +1,28 @@
1
1
  import { __decorate } from 'tslib';
2
- import { Module, COMMAND_LINE_RUNNER_TOKEN, COMMAND_LINES_TOKEN, DI_TOKEN } from '@tramvai/core';
3
- import { COMMAND_LINE_EXECUTION_END_TOKEN } from '@tramvai/tokens-core-private';
2
+ import { Module, COMMAND_LINE_RUNNER_TOKEN, COMMAND_LINES_TOKEN, TAPABLE_HOOK_FACTORY_TOKEN, COMMAND_LINE_RUNNER_PLUGIN, DI_TOKEN } from '@tramvai/core';
4
3
  import { LOGGER_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN } from '@tramvai/tokens-common';
5
- import { provide, Scope } from '@tinkoff/dippy';
6
- import { CommandLineRunner } from './commandLineRunner.browser.js';
4
+ import { provide, Scope, optional } from '@tinkoff/dippy';
5
+ import { COMMAND_LINE_EXECUTION_END_TOKEN } from '@tramvai/tokens-core-private';
6
+ import { TramvaiHookModule } from '../hook/HookModule.browser.js';
7
7
  import { lines } from './defaultLines.browser.js';
8
+ import { CommandLineRunner } from './commandLineRunner.new.browser.js';
8
9
 
9
10
  let CommandModule = class CommandModule {
10
11
  };
11
12
  CommandModule = __decorate([
12
13
  Module({
14
+ imports: [TramvaiHookModule],
13
15
  providers: [
14
16
  provide({
15
- // Раннер процессов
16
17
  provide: COMMAND_LINE_RUNNER_TOKEN,
17
18
  scope: Scope.SINGLETON,
18
19
  useClass: CommandLineRunner,
19
20
  deps: {
21
+ logger: LOGGER_TOKEN,
20
22
  lines: COMMAND_LINES_TOKEN,
23
+ hookFactory: TAPABLE_HOOK_FACTORY_TOKEN,
24
+ plugins: optional(COMMAND_LINE_RUNNER_PLUGIN),
21
25
  rootDi: DI_TOKEN,
22
- logger: LOGGER_TOKEN,
23
26
  executionContextManager: EXECUTION_CONTEXT_MANAGER_TOKEN,
24
27
  executionEndHandlers: {
25
28
  token: COMMAND_LINE_EXECUTION_END_TOKEN,
@@ -40,7 +43,6 @@ CommandModule = __decorate([
40
43
  },
41
44
  }),
42
45
  provide({
43
- // Дефолтный список команл
44
46
  provide: COMMAND_LINES_TOKEN,
45
47
  scope: Scope.SINGLETON,
46
48
  useValue: lines,
@@ -1,25 +1,28 @@
1
1
  import { __decorate } from 'tslib';
2
- import { Module, COMMAND_LINE_RUNNER_TOKEN, COMMAND_LINES_TOKEN, DI_TOKEN } from '@tramvai/core';
3
- import { COMMAND_LINE_EXECUTION_END_TOKEN } from '@tramvai/tokens-core-private';
2
+ import { Module, COMMAND_LINE_RUNNER_TOKEN, COMMAND_LINES_TOKEN, TAPABLE_HOOK_FACTORY_TOKEN, COMMAND_LINE_RUNNER_PLUGIN, DI_TOKEN } from '@tramvai/core';
4
3
  import { LOGGER_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN } from '@tramvai/tokens-common';
5
- import { provide, Scope } from '@tinkoff/dippy';
6
- import { CommandLineRunner } from './commandLineRunner.es.js';
4
+ import { provide, Scope, optional } from '@tinkoff/dippy';
5
+ import { COMMAND_LINE_EXECUTION_END_TOKEN } from '@tramvai/tokens-core-private';
6
+ import { TramvaiHookModule } from '../hook/HookModule.es.js';
7
7
  import { lines } from './defaultLines.es.js';
8
+ import { CommandLineRunner } from './commandLineRunner.new.es.js';
8
9
 
9
10
  let CommandModule = class CommandModule {
10
11
  };
11
12
  CommandModule = __decorate([
12
13
  Module({
14
+ imports: [TramvaiHookModule],
13
15
  providers: [
14
16
  provide({
15
- // Раннер процессов
16
17
  provide: COMMAND_LINE_RUNNER_TOKEN,
17
18
  scope: Scope.SINGLETON,
18
19
  useClass: CommandLineRunner,
19
20
  deps: {
21
+ logger: LOGGER_TOKEN,
20
22
  lines: COMMAND_LINES_TOKEN,
23
+ hookFactory: TAPABLE_HOOK_FACTORY_TOKEN,
24
+ plugins: optional(COMMAND_LINE_RUNNER_PLUGIN),
21
25
  rootDi: DI_TOKEN,
22
- logger: LOGGER_TOKEN,
23
26
  executionContextManager: EXECUTION_CONTEXT_MANAGER_TOKEN,
24
27
  executionEndHandlers: {
25
28
  token: COMMAND_LINE_EXECUTION_END_TOKEN,
@@ -40,7 +43,6 @@ CommandModule = __decorate([
40
43
  },
41
44
  }),
42
45
  provide({
43
- // Дефолтный список команл
44
46
  provide: COMMAND_LINES_TOKEN,
45
47
  scope: Scope.SINGLETON,
46
48
  useValue: lines,
@@ -4,26 +4,29 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var tslib = require('tslib');
6
6
  var core = require('@tramvai/core');
7
- var tokensCorePrivate = require('@tramvai/tokens-core-private');
8
7
  var tokensCommon = require('@tramvai/tokens-common');
9
8
  var dippy = require('@tinkoff/dippy');
10
- var commandLineRunner = require('./commandLineRunner.js');
9
+ var tokensCorePrivate = require('@tramvai/tokens-core-private');
10
+ var HookModule = require('../hook/HookModule.js');
11
11
  var defaultLines = require('./defaultLines.js');
12
+ var commandLineRunner_new = require('./commandLineRunner.new.js');
12
13
 
13
14
  exports.CommandModule = class CommandModule {
14
15
  };
15
16
  exports.CommandModule = tslib.__decorate([
16
17
  core.Module({
18
+ imports: [HookModule.TramvaiHookModule],
17
19
  providers: [
18
20
  dippy.provide({
19
- // Раннер процессов
20
21
  provide: core.COMMAND_LINE_RUNNER_TOKEN,
21
22
  scope: dippy.Scope.SINGLETON,
22
- useClass: commandLineRunner.CommandLineRunner,
23
+ useClass: commandLineRunner_new.CommandLineRunner,
23
24
  deps: {
25
+ logger: tokensCommon.LOGGER_TOKEN,
24
26
  lines: core.COMMAND_LINES_TOKEN,
27
+ hookFactory: core.TAPABLE_HOOK_FACTORY_TOKEN,
28
+ plugins: dippy.optional(core.COMMAND_LINE_RUNNER_PLUGIN),
25
29
  rootDi: core.DI_TOKEN,
26
- logger: tokensCommon.LOGGER_TOKEN,
27
30
  executionContextManager: tokensCommon.EXECUTION_CONTEXT_MANAGER_TOKEN,
28
31
  executionEndHandlers: {
29
32
  token: tokensCorePrivate.COMMAND_LINE_EXECUTION_END_TOKEN,
@@ -44,7 +47,6 @@ exports.CommandModule = tslib.__decorate([
44
47
  },
45
48
  }),
46
49
  dippy.provide({
47
- // Дефолтный список команл
48
50
  provide: core.COMMAND_LINES_TOKEN,
49
51
  scope: dippy.Scope.SINGLETON,
50
52
  useValue: defaultLines.lines,
@@ -0,0 +1,159 @@
1
+ import { createChildContainer } from '@tinkoff/dippy';
2
+ import { isSilentError } from '@tinkoff/errors';
3
+ import { ROOT_EXECUTION_CONTEXT_TOKEN } from '@tramvai/tokens-common';
4
+ import { COMMAND_LINE_TIMING_INFO_TOKEN } from '@tramvai/tokens-core-private';
5
+
6
+ class CommandLineRunner {
7
+ constructor({ rootDi, lines, logger, plugins, hookFactory, executionContextManager, executionEndHandlers, }) {
8
+ this.executionContextByDi = new WeakMap();
9
+ this.abortControllerByDi = new WeakMap();
10
+ this.rootDi = rootDi;
11
+ this.lines = lines;
12
+ this.hookFactory = hookFactory;
13
+ this.plugins = plugins;
14
+ this.executionContextManager = executionContextManager;
15
+ this.executionEndHandlers = executionEndHandlers;
16
+ this.log = logger('command:command-line-runner');
17
+ this.runLineHook = this.hookFactory.createAsync('runLine');
18
+ this.runCommandHook = this.hookFactory.createAsync('runCommand');
19
+ this.runCommandFnHook = this.hookFactory.createAsync('runCommandFn');
20
+ this.runLineHook.tapPromise('commandLineRunner', async (_, { env, line, di, key }) => {
21
+ const commands = this.lines[env][line];
22
+ const timingInfo = {};
23
+ di.register({ provide: COMMAND_LINE_TIMING_INFO_TOKEN, useValue: timingInfo });
24
+ this.log.debug({
25
+ event: 'command-run',
26
+ type: env,
27
+ status: line,
28
+ });
29
+ try {
30
+ for (const command of commands) {
31
+ // emulate old `CommandLineRunner` behavior, important for race conditions at client-side,
32
+ // when new line executes in the middle of current line, and we need cleanup current line before
33
+ await Promise.resolve();
34
+ await this.runCommandHook.callPromise({
35
+ env,
36
+ line,
37
+ di,
38
+ command,
39
+ });
40
+ }
41
+ }
42
+ finally {
43
+ this.executionContextByDi.delete(di);
44
+ this.abortControllerByDi.delete(di);
45
+ for (const executionEndHandler of this.executionEndHandlers) {
46
+ executionEndHandler(di, env, line, timingInfo, key);
47
+ }
48
+ }
49
+ });
50
+ this.runCommandHook.tapPromise('commandLineRunner', async (_, { env, line, di, command }) => {
51
+ const commands = di.get({ token: command, optional: true });
52
+ if (!commands) {
53
+ return;
54
+ }
55
+ const rootExecutionContext = di.get({ token: ROOT_EXECUTION_CONTEXT_TOKEN, optional: true });
56
+ const timingInfo = di.get(COMMAND_LINE_TIMING_INFO_TOKEN);
57
+ const commandName = command.toString();
58
+ timingInfo[commandName] = { start: performance.now() };
59
+ return this.executionContextManager
60
+ .withContext(rootExecutionContext, `command-line:${commandName}`, async (executionContext, abortController) => {
61
+ this.executionContextByDi.set(di, executionContext);
62
+ this.abortControllerByDi.set(di, abortController);
63
+ if (!Array.isArray(commands)) {
64
+ await this.runCommandFnHook.callPromise({ fn: commands, line, command, di });
65
+ }
66
+ else {
67
+ await Promise.all(commands.map((fn) => {
68
+ return this.runCommandFnHook.callPromise({ fn, line, command, di });
69
+ }));
70
+ }
71
+ })
72
+ .finally(() => {
73
+ timingInfo[commandName].end = performance.now();
74
+ });
75
+ });
76
+ this.runCommandFnHook.tapPromise('commandLineRunner', async (_, { fn, command, di }) => {
77
+ try {
78
+ await this.executeCommand(fn, command, di);
79
+ }
80
+ catch (error) {
81
+ // in case if any error happens during line execution results from other line handlers will not be used anyway
82
+ this.abortControllerByDi
83
+ .get(di)
84
+ ?.abort('Execution context were aborted because of one of the commands failed');
85
+ throw error;
86
+ }
87
+ });
88
+ this.plugins?.forEach((plugin) => {
89
+ plugin.apply(this);
90
+ });
91
+ }
92
+ async run(env, line, providers, customDi, key) {
93
+ const di = customDi ?? this.resolveDi(env, line, this.rootDi, providers);
94
+ await this.runLineHook.callPromise({ env, line, di, key });
95
+ return di;
96
+ }
97
+ resolveDi(env, line, rootDi, providers) {
98
+ let di = rootDi;
99
+ if (env === 'server' && line === 'customer') {
100
+ di = createChildContainer(di);
101
+ }
102
+ if (providers) {
103
+ providers.forEach((item) => {
104
+ return di.register(item);
105
+ });
106
+ }
107
+ return di;
108
+ }
109
+ resolveExecutionContextFromDi(di) {
110
+ return this.executionContextByDi.get(di) ?? null;
111
+ }
112
+ async executeCommand(command, commandToken, di) {
113
+ const commandName = commandToken.toString();
114
+ if (!(command instanceof Function)) {
115
+ const error = new TypeError(`Expected function in line processing "commandLineListTokens.${commandName}", received "${command}".
116
+ Check that all commandLineListTokens providers return functions`);
117
+ if (process.env.NODE_ENV !== 'production') {
118
+ const commands = di.get(commandToken);
119
+ const record = di.getRecord(commandToken.name);
120
+ // need to find stack trace from this specific token provider
121
+ for (let i = 0; i < commands.length; i++) {
122
+ if (commands[i] === command) {
123
+ // @ts-expect-error
124
+ error.stack = `${error.stack}\n---- caused by: ----\n${record.multi[i].stack || ''}`;
125
+ }
126
+ }
127
+ }
128
+ this.log.error({
129
+ event: 'line-error',
130
+ error,
131
+ line: commandName,
132
+ });
133
+ return;
134
+ }
135
+ const { name = '' } = command;
136
+ this.log.debug({
137
+ event: 'line-run',
138
+ line: commandName,
139
+ command: name,
140
+ });
141
+ try {
142
+ await command();
143
+ }
144
+ catch (error) {
145
+ this.log[isSilentError(error) ? 'debug' : 'error']({
146
+ event: 'line-error',
147
+ error,
148
+ line: commandName,
149
+ command: name,
150
+ });
151
+ if (typeof error === 'object') {
152
+ error.di = di;
153
+ }
154
+ throw error;
155
+ }
156
+ }
157
+ }
158
+
159
+ export { CommandLineRunner };
@@ -0,0 +1,57 @@
1
+ import type { MultiTokenInterface, Provider } from '@tinkoff/dippy';
2
+ import type { AsyncTapableHookInstance, CommandLineDescription, CommandLineRunnerPlugin, CommandLines } from '@tramvai/core';
3
+ import type { COMMAND_LINES_TOKEN, Command, DI_TOKEN, ExtractDependencyType, CommandLineRunner as Interface, TAPABLE_HOOK_FACTORY_TOKEN } from '@tramvai/core';
4
+ import { type EXECUTION_CONTEXT_MANAGER_TOKEN, type ExecutionContext, type LOGGER_TOKEN } from '@tramvai/tokens-common';
5
+ import { type COMMAND_LINE_EXECUTION_END_TOKEN } from '@tramvai/tokens-core-private';
6
+ type Container = ExtractDependencyType<typeof DI_TOKEN>;
7
+ type Lines = ExtractDependencyType<typeof COMMAND_LINES_TOKEN>;
8
+ export type CommandEnvironment = 'server' | 'client';
9
+ export type CommandLine = 'init' | 'customer' | 'spa' | 'afterSpa' | 'close';
10
+ type LoggerFactory = ExtractDependencyType<typeof LOGGER_TOKEN>;
11
+ type HookFactory = ExtractDependencyType<typeof TAPABLE_HOOK_FACTORY_TOKEN>;
12
+ type ExecutionContextManager = ExtractDependencyType<typeof EXECUTION_CONTEXT_MANAGER_TOKEN>;
13
+ type ExecutionEndHandlers = ExtractDependencyType<typeof COMMAND_LINE_EXECUTION_END_TOKEN> | null;
14
+ export declare class CommandLineRunner implements Interface {
15
+ private rootDi;
16
+ private log;
17
+ private hookFactory;
18
+ private plugins;
19
+ private executionContextManager;
20
+ private executionEndHandlers;
21
+ private executionContextByDi;
22
+ private abortControllerByDi;
23
+ lines: Lines;
24
+ runLineHook: AsyncTapableHookInstance<{
25
+ env: keyof CommandLines;
26
+ line: keyof CommandLineDescription;
27
+ di: Container;
28
+ key?: string | number;
29
+ }>;
30
+ runCommandHook: AsyncTapableHookInstance<{
31
+ env: keyof CommandLines;
32
+ line: keyof CommandLineDescription;
33
+ di: Container;
34
+ command: MultiTokenInterface<Command>;
35
+ }>;
36
+ runCommandFnHook: AsyncTapableHookInstance<{
37
+ fn: Command;
38
+ line: keyof CommandLineDescription;
39
+ command: MultiTokenInterface<Command>;
40
+ di: Container;
41
+ }>;
42
+ constructor({ rootDi, lines, logger, plugins, hookFactory, executionContextManager, executionEndHandlers, }: {
43
+ rootDi: Container;
44
+ lines: Lines;
45
+ logger: LoggerFactory;
46
+ plugins: CommandLineRunnerPlugin[] | null;
47
+ hookFactory: HookFactory;
48
+ executionContextManager: ExecutionContextManager;
49
+ executionEndHandlers: ExecutionEndHandlers;
50
+ });
51
+ run(env: CommandEnvironment, line: 'init' | 'customer' | 'spa' | 'afterSpa' | 'close', providers?: Provider[], customDi?: Container, key?: string | number): Promise<import("@tinkoff/dippy").Container>;
52
+ resolveDi(env: CommandEnvironment, line: CommandLine, rootDi: Container, providers?: Provider[]): Container;
53
+ resolveExecutionContextFromDi(di: Container): ExecutionContext | null;
54
+ private executeCommand;
55
+ }
56
+ export {};
57
+ //# sourceMappingURL=commandLineRunner.new.d.ts.map
@@ -0,0 +1,159 @@
1
+ import { createChildContainer } from '@tinkoff/dippy';
2
+ import { isSilentError } from '@tinkoff/errors';
3
+ import { ROOT_EXECUTION_CONTEXT_TOKEN } from '@tramvai/tokens-common';
4
+ import { COMMAND_LINE_TIMING_INFO_TOKEN } from '@tramvai/tokens-core-private';
5
+
6
+ class CommandLineRunner {
7
+ constructor({ rootDi, lines, logger, plugins, hookFactory, executionContextManager, executionEndHandlers, }) {
8
+ this.executionContextByDi = new WeakMap();
9
+ this.abortControllerByDi = new WeakMap();
10
+ this.rootDi = rootDi;
11
+ this.lines = lines;
12
+ this.hookFactory = hookFactory;
13
+ this.plugins = plugins;
14
+ this.executionContextManager = executionContextManager;
15
+ this.executionEndHandlers = executionEndHandlers;
16
+ this.log = logger('command:command-line-runner');
17
+ this.runLineHook = this.hookFactory.createAsync('runLine');
18
+ this.runCommandHook = this.hookFactory.createAsync('runCommand');
19
+ this.runCommandFnHook = this.hookFactory.createAsync('runCommandFn');
20
+ this.runLineHook.tapPromise('commandLineRunner', async (_, { env, line, di, key }) => {
21
+ const commands = this.lines[env][line];
22
+ const timingInfo = {};
23
+ di.register({ provide: COMMAND_LINE_TIMING_INFO_TOKEN, useValue: timingInfo });
24
+ this.log.debug({
25
+ event: 'command-run',
26
+ type: env,
27
+ status: line,
28
+ });
29
+ try {
30
+ for (const command of commands) {
31
+ // emulate old `CommandLineRunner` behavior, important for race conditions at client-side,
32
+ // when new line executes in the middle of current line, and we need cleanup current line before
33
+ await Promise.resolve();
34
+ await this.runCommandHook.callPromise({
35
+ env,
36
+ line,
37
+ di,
38
+ command,
39
+ });
40
+ }
41
+ }
42
+ finally {
43
+ this.executionContextByDi.delete(di);
44
+ this.abortControllerByDi.delete(di);
45
+ for (const executionEndHandler of this.executionEndHandlers) {
46
+ executionEndHandler(di, env, line, timingInfo, key);
47
+ }
48
+ }
49
+ });
50
+ this.runCommandHook.tapPromise('commandLineRunner', async (_, { env, line, di, command }) => {
51
+ const commands = di.get({ token: command, optional: true });
52
+ if (!commands) {
53
+ return;
54
+ }
55
+ const rootExecutionContext = di.get({ token: ROOT_EXECUTION_CONTEXT_TOKEN, optional: true });
56
+ const timingInfo = di.get(COMMAND_LINE_TIMING_INFO_TOKEN);
57
+ const commandName = command.toString();
58
+ timingInfo[commandName] = { start: performance.now() };
59
+ return this.executionContextManager
60
+ .withContext(rootExecutionContext, `command-line:${commandName}`, async (executionContext, abortController) => {
61
+ this.executionContextByDi.set(di, executionContext);
62
+ this.abortControllerByDi.set(di, abortController);
63
+ if (!Array.isArray(commands)) {
64
+ await this.runCommandFnHook.callPromise({ fn: commands, line, command, di });
65
+ }
66
+ else {
67
+ await Promise.all(commands.map((fn) => {
68
+ return this.runCommandFnHook.callPromise({ fn, line, command, di });
69
+ }));
70
+ }
71
+ })
72
+ .finally(() => {
73
+ timingInfo[commandName].end = performance.now();
74
+ });
75
+ });
76
+ this.runCommandFnHook.tapPromise('commandLineRunner', async (_, { fn, command, di }) => {
77
+ try {
78
+ await this.executeCommand(fn, command, di);
79
+ }
80
+ catch (error) {
81
+ // in case if any error happens during line execution results from other line handlers will not be used anyway
82
+ this.abortControllerByDi
83
+ .get(di)
84
+ ?.abort('Execution context were aborted because of one of the commands failed');
85
+ throw error;
86
+ }
87
+ });
88
+ this.plugins?.forEach((plugin) => {
89
+ plugin.apply(this);
90
+ });
91
+ }
92
+ async run(env, line, providers, customDi, key) {
93
+ const di = customDi ?? this.resolveDi(env, line, this.rootDi, providers);
94
+ await this.runLineHook.callPromise({ env, line, di, key });
95
+ return di;
96
+ }
97
+ resolveDi(env, line, rootDi, providers) {
98
+ let di = rootDi;
99
+ if (env === 'server' && line === 'customer') {
100
+ di = createChildContainer(di);
101
+ }
102
+ if (providers) {
103
+ providers.forEach((item) => {
104
+ return di.register(item);
105
+ });
106
+ }
107
+ return di;
108
+ }
109
+ resolveExecutionContextFromDi(di) {
110
+ return this.executionContextByDi.get(di) ?? null;
111
+ }
112
+ async executeCommand(command, commandToken, di) {
113
+ const commandName = commandToken.toString();
114
+ if (!(command instanceof Function)) {
115
+ const error = new TypeError(`Expected function in line processing "commandLineListTokens.${commandName}", received "${command}".
116
+ Check that all commandLineListTokens providers return functions`);
117
+ if (process.env.NODE_ENV !== 'production') {
118
+ const commands = di.get(commandToken);
119
+ const record = di.getRecord(commandToken.name);
120
+ // need to find stack trace from this specific token provider
121
+ for (let i = 0; i < commands.length; i++) {
122
+ if (commands[i] === command) {
123
+ // @ts-expect-error
124
+ error.stack = `${error.stack}\n---- caused by: ----\n${record.multi[i].stack || ''}`;
125
+ }
126
+ }
127
+ }
128
+ this.log.error({
129
+ event: 'line-error',
130
+ error,
131
+ line: commandName,
132
+ });
133
+ return;
134
+ }
135
+ const { name = '' } = command;
136
+ this.log.debug({
137
+ event: 'line-run',
138
+ line: commandName,
139
+ command: name,
140
+ });
141
+ try {
142
+ await command();
143
+ }
144
+ catch (error) {
145
+ this.log[isSilentError(error) ? 'debug' : 'error']({
146
+ event: 'line-error',
147
+ error,
148
+ line: commandName,
149
+ command: name,
150
+ });
151
+ if (typeof error === 'object') {
152
+ error.di = di;
153
+ }
154
+ throw error;
155
+ }
156
+ }
157
+ }
158
+
159
+ export { CommandLineRunner };