@tramvai/module-common 2.145.1 → 2.146.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,6 +1,6 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import { createToken, Module, provide, Scope, ACTIONS_LIST_TOKEN, DI_TOKEN, optional } from '@tramvai/core';
3
- import { COMBINE_REDUCERS, ACTION_REGISTRY_TOKEN, ACTION_EXECUTION_TOKEN, ACTION_CONDITIONALS, CONTEXT_TOKEN, STORE_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN, ACTION_PAGE_RUNNER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, LOGGER_TOKEN, DEFERRED_ACTIONS_MAP_TOKEN } from '@tramvai/tokens-common';
3
+ import { COMBINE_REDUCERS, ACTION_REGISTRY_TOKEN, ACTION_EXECUTION_TOKEN, ACTION_CONDITIONALS, CONTEXT_TOKEN, STORE_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN, DEFERRED_ACTIONS_MAP_TOKEN, ACTION_PAGE_RUNNER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, LOGGER_TOKEN } from '@tramvai/tokens-common';
4
4
  import { SERVER_RESPONSE_TASK_MANAGER, SERVER_RESPONSE_STREAM } from '@tramvai/tokens-server-private';
5
5
  import { actionTramvaiReducer } from './actionTramvaiReducer.browser.js';
6
6
  import { ActionExecution } from './actionExecution.browser.js';
@@ -16,6 +16,8 @@ import { pageServer } from './conditions/pageServer.browser.js';
16
16
  export { pageServer } from './conditions/pageServer.browser.js';
17
17
  import { pageBrowser } from './conditions/pageBrowser.browser.js';
18
18
  export { pageBrowser } from './conditions/pageBrowser.browser.js';
19
+ import { dynamicCondition } from './conditions/dynamic.browser.js';
20
+ export { dynamicCondition } from './conditions/dynamic.browser.js';
19
21
  import { providers } from './deferred/providers.browser.browser.js';
20
22
 
21
23
  const LIMIT_ACTION_GLOBAL_TIME_RUN = createToken('limitActionGlobalTimeRun');
@@ -50,6 +52,7 @@ ActionModule = __decorate([
50
52
  token: 'actionTransformAction',
51
53
  optional: true,
52
54
  },
55
+ deferredActionsMap: DEFERRED_ACTIONS_MAP_TOKEN,
53
56
  },
54
57
  }),
55
58
  provide({
@@ -75,7 +78,14 @@ ActionModule = __decorate([
75
78
  provide({
76
79
  provide: ACTION_CONDITIONALS,
77
80
  multi: true,
78
- useValue: [alwaysCondition, onlyServer, onlyBrowser, pageServer, pageBrowser],
81
+ useValue: [
82
+ alwaysCondition,
83
+ onlyServer,
84
+ onlyBrowser,
85
+ pageServer,
86
+ pageBrowser,
87
+ dynamicCondition,
88
+ ],
79
89
  }),
80
90
  ...providers,
81
91
  ],
@@ -3,7 +3,8 @@ import { onlyServer } from './conditions/onlyServer';
3
3
  import { onlyBrowser } from './conditions/onlyBrowser';
4
4
  import { pageServer } from './conditions/pageServer';
5
5
  import { pageBrowser } from './conditions/pageBrowser';
6
- export { alwaysCondition, onlyServer, onlyBrowser, pageServer, pageBrowser };
6
+ import { dynamicCondition } from './conditions/dynamic';
7
+ export { alwaysCondition, onlyServer, onlyBrowser, pageServer, pageBrowser, dynamicCondition };
7
8
  declare class ActionModule {
8
9
  }
9
10
  export { ActionModule };
@@ -1,6 +1,6 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import { createToken, Module, provide, Scope, ACTIONS_LIST_TOKEN, DI_TOKEN, optional } from '@tramvai/core';
3
- import { COMBINE_REDUCERS, ACTION_REGISTRY_TOKEN, ACTION_EXECUTION_TOKEN, ACTION_CONDITIONALS, CONTEXT_TOKEN, STORE_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN, ACTION_PAGE_RUNNER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, LOGGER_TOKEN, DEFERRED_ACTIONS_MAP_TOKEN } from '@tramvai/tokens-common';
3
+ import { COMBINE_REDUCERS, ACTION_REGISTRY_TOKEN, ACTION_EXECUTION_TOKEN, ACTION_CONDITIONALS, CONTEXT_TOKEN, STORE_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN, DEFERRED_ACTIONS_MAP_TOKEN, ACTION_PAGE_RUNNER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, LOGGER_TOKEN } from '@tramvai/tokens-common';
4
4
  import { SERVER_RESPONSE_TASK_MANAGER, SERVER_RESPONSE_STREAM } from '@tramvai/tokens-server-private';
5
5
  import { actionTramvaiReducer } from './actionTramvaiReducer.es.js';
6
6
  import { ActionExecution } from './actionExecution.es.js';
@@ -16,6 +16,8 @@ import { pageServer } from './conditions/pageServer.es.js';
16
16
  export { pageServer } from './conditions/pageServer.es.js';
17
17
  import { pageBrowser } from './conditions/pageBrowser.es.js';
18
18
  export { pageBrowser } from './conditions/pageBrowser.es.js';
19
+ import { dynamicCondition } from './conditions/dynamic.es.js';
20
+ export { dynamicCondition } from './conditions/dynamic.es.js';
19
21
  import { providers } from './deferred/providers.es.js';
20
22
 
21
23
  const LIMIT_ACTION_GLOBAL_TIME_RUN = createToken('limitActionGlobalTimeRun');
@@ -50,6 +52,7 @@ ActionModule = __decorate([
50
52
  token: 'actionTransformAction',
51
53
  optional: true,
52
54
  },
55
+ deferredActionsMap: DEFERRED_ACTIONS_MAP_TOKEN,
53
56
  },
54
57
  }),
55
58
  provide({
@@ -75,7 +78,14 @@ ActionModule = __decorate([
75
78
  provide({
76
79
  provide: ACTION_CONDITIONALS,
77
80
  multi: true,
78
- useValue: [alwaysCondition, onlyServer, onlyBrowser, pageServer, pageBrowser],
81
+ useValue: [
82
+ alwaysCondition,
83
+ onlyServer,
84
+ onlyBrowser,
85
+ pageServer,
86
+ pageBrowser,
87
+ dynamicCondition,
88
+ ],
79
89
  }),
80
90
  ...providers,
81
91
  ],
@@ -15,6 +15,7 @@ var onlyServer = require('./conditions/onlyServer.js');
15
15
  var onlyBrowser = require('./conditions/onlyBrowser.js');
16
16
  var pageServer = require('./conditions/pageServer.js');
17
17
  var pageBrowser = require('./conditions/pageBrowser.js');
18
+ var dynamic = require('./conditions/dynamic.js');
18
19
  var providers = require('./deferred/providers.js');
19
20
 
20
21
  const LIMIT_ACTION_GLOBAL_TIME_RUN = core.createToken('limitActionGlobalTimeRun');
@@ -49,6 +50,7 @@ exports.ActionModule = tslib.__decorate([
49
50
  token: 'actionTransformAction',
50
51
  optional: true,
51
52
  },
53
+ deferredActionsMap: tokensCommon.DEFERRED_ACTIONS_MAP_TOKEN,
52
54
  },
53
55
  }),
54
56
  core.provide({
@@ -74,7 +76,14 @@ exports.ActionModule = tslib.__decorate([
74
76
  core.provide({
75
77
  provide: tokensCommon.ACTION_CONDITIONALS,
76
78
  multi: true,
77
- useValue: [always.alwaysCondition, onlyServer.onlyServer, onlyBrowser.onlyBrowser, pageServer.pageServer, pageBrowser.pageBrowser],
79
+ useValue: [
80
+ always.alwaysCondition,
81
+ onlyServer.onlyServer,
82
+ onlyBrowser.onlyBrowser,
83
+ pageServer.pageServer,
84
+ pageBrowser.pageBrowser,
85
+ dynamic.dynamicCondition,
86
+ ],
78
87
  }),
79
88
  ...providers.providers,
80
89
  ],
@@ -86,3 +95,4 @@ exports.onlyServer = onlyServer.onlyServer;
86
95
  exports.onlyBrowser = onlyBrowser.onlyBrowser;
87
96
  exports.pageServer = pageServer.pageServer;
88
97
  exports.pageBrowser = pageBrowser.pageBrowser;
98
+ exports.dynamicCondition = dynamic.dynamicCondition;
@@ -1,19 +1,22 @@
1
1
  import flatten from '@tinkoff/utils/array/flatten';
2
2
  import identity from '@tinkoff/utils/function/identity';
3
+ import isPromise from '@tinkoff/utils/is/promise';
3
4
  import { isTramvaiAction, ACTION_PARAMETERS } from '@tramvai/core';
4
5
  import objectMap from '@tinkoff/utils/object/map';
5
6
  import { ConditionFailError } from '@tinkoff/errors';
6
7
  import { ActionChecker } from './actionChecker.browser.js';
7
8
  import { actionType } from './constants.browser.js';
8
9
  import { actionTramvaiReducer } from './actionTramvaiReducer.browser.js';
10
+ import { Deferred } from './deferred/deferred.browser.js';
9
11
 
10
12
  const EMPTY_DEPS = {};
11
13
  const getParameters = (action) => action[ACTION_PARAMETERS];
12
14
  class ActionExecution {
13
- constructor({ store, context, di, executionContextManager, actionConditionals, transformAction, }) {
15
+ constructor({ store, context, deferredActionsMap, di, executionContextManager, actionConditionals, transformAction, }) {
14
16
  this.actionConditionals = flatten(actionConditionals !== null && actionConditionals !== void 0 ? actionConditionals : []);
15
17
  this.context = context;
16
18
  this.store = store;
19
+ this.deferredActionsMap = deferredActionsMap;
17
20
  this.di = di;
18
21
  this.executionContextManager = executionContextManager;
19
22
  this.execution = new Map();
@@ -27,18 +30,10 @@ class ActionExecution {
27
30
  }
28
31
  async runInContext(executionContext, action, ...params) {
29
32
  var _a;
30
- let parameters;
31
33
  const payload = params[0];
32
34
  // TODO: replace type with pure context usage
33
35
  const type = (executionContext === null || executionContext === void 0 ? void 0 : executionContext.values.pageActions) === true ? actionType.global : actionType.local;
34
- // TODO: remove else branch after migration to new declareAction
35
- if (isTramvaiAction(action)) {
36
- parameters = action;
37
- }
38
- else {
39
- this.transformAction(action);
40
- parameters = getParameters(action);
41
- }
36
+ const parameters = this.getActionParameters(action);
42
37
  if (!parameters) {
43
38
  throw new Error('Cannot resolve internal data for action. Make sure you are using the result of `declareAction` call as an action');
44
39
  }
@@ -54,6 +49,14 @@ class ActionExecution {
54
49
  return Promise.resolve();
55
50
  }
56
51
  }
52
+ const isDeferredAction = parameters.deferred;
53
+ // will be created when spa run actions mode is "before"
54
+ if (isDeferredAction && !this.deferredActionsMap.get(action.name)) {
55
+ const deferred = new Deferred();
56
+ // avoid unhandled promise rejection
57
+ deferred.promise.catch(() => { });
58
+ this.deferredActionsMap.set(action.name, deferred);
59
+ }
57
60
  executionState.status = 'pending';
58
61
  return this.executionContextManager.withContext(executionContext, {
59
62
  name: parameters.name,
@@ -63,7 +66,20 @@ class ActionExecution {
63
66
  return Promise.resolve()
64
67
  .then(() => {
65
68
  if (isTramvaiAction(action)) {
66
- return action.fn.apply(context, params);
69
+ const result = action.fn.apply(context, params);
70
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
71
+ const deferred = this.deferredActionsMap.get(action.name);
72
+ if (isDeferredAction) {
73
+ if (!deferred.isResolved() && !deferred.isRejected() && isPromise(result)) {
74
+ // eslint-disable-next-line promise/no-nesting
75
+ result.then(deferred.resolve).catch(deferred.reject);
76
+ }
77
+ // pass success state for deferred actions to the client
78
+ // it will be helpful to detect deferred executions for SPA-transitions only
79
+ // eslint-disable-next-line promise/no-return-wrap
80
+ return Promise.resolve();
81
+ }
82
+ return result;
67
83
  }
68
84
  return action(this.context, payload, context.deps);
69
85
  })
@@ -80,9 +96,26 @@ class ActionExecution {
80
96
  async run(action, ...params) {
81
97
  return this.runInContext(null, action, ...params);
82
98
  }
99
+ canExecute(action) {
100
+ const parameters = this.getActionParameters(action);
101
+ const executionState = this.getExecutionState(parameters.name);
102
+ // @todo any cases when hadcoded global action type and payload could cause problems?
103
+ return this.canExecuteAction(undefined, parameters, executionState, actionType.global);
104
+ }
105
+ getActionParameters(action) {
106
+ let parameters;
107
+ // TODO: remove else branch after migration to new declareAction
108
+ if (isTramvaiAction(action)) {
109
+ parameters = action;
110
+ }
111
+ else {
112
+ this.transformAction(action);
113
+ parameters = getParameters(action);
114
+ }
115
+ return parameters;
116
+ }
83
117
  getExecutionState(name) {
84
118
  let executionState = this.execution.get(name);
85
- // TODO: probably do not need to create executionState on client as it is not used
86
119
  if (!executionState) {
87
120
  executionState = { status: 'pending', state: {} };
88
121
  this.execution.set(name, executionState);
@@ -1,5 +1,5 @@
1
1
  import type { Action, ActionParameters, DI_TOKEN } from '@tramvai/core';
2
- import type { CONTEXT_TOKEN, ActionCondition, STORE_TOKEN, ActionExecution as Interface, EXECUTION_CONTEXT_MANAGER_TOKEN, ExecutionContext } from '@tramvai/tokens-common';
2
+ import type { CONTEXT_TOKEN, ActionCondition, STORE_TOKEN, ActionExecution as Interface, EXECUTION_CONTEXT_MANAGER_TOKEN, ExecutionContext, DEFERRED_ACTIONS_MAP_TOKEN } from '@tramvai/tokens-common';
3
3
  import type { TramvaiAction } from '@tramvai/types-actions-state-context';
4
4
  import type { ExtractDependencyType } from '@tinkoff/dippy';
5
5
  export declare const getParameters: (action: Action) => ActionParameters<any, any>;
@@ -14,20 +14,24 @@ export declare class ActionExecution implements Interface {
14
14
  execution: Map<string, ExecutionState>;
15
15
  private actionConditionals;
16
16
  private context;
17
+ private deferredActionsMap;
17
18
  private store;
18
19
  private executionContextManager;
19
20
  private di;
20
21
  private transformAction;
21
- constructor({ store, context, di, executionContextManager, actionConditionals, transformAction, }: {
22
+ constructor({ store, context, deferredActionsMap, di, executionContextManager, actionConditionals, transformAction, }: {
22
23
  actionConditionals: (ActionCondition | ActionCondition[])[] | null;
23
24
  store: ExtractDependencyType<typeof STORE_TOKEN>;
24
25
  context: ExtractDependencyType<typeof CONTEXT_TOKEN>;
26
+ deferredActionsMap: ExtractDependencyType<typeof DEFERRED_ACTIONS_MAP_TOKEN>;
25
27
  di: ExtractDependencyType<typeof DI_TOKEN>;
26
28
  executionContextManager: ExtractDependencyType<typeof EXECUTION_CONTEXT_MANAGER_TOKEN>;
27
29
  transformAction?: TransformAction;
28
30
  });
29
31
  runInContext(executionContext: ExecutionContext | null, action: Action | TramvaiAction<any, any, any>, ...params: any[]): Promise<any>;
30
32
  run(action: Action | TramvaiAction<any, any, any>, ...params: any[]): Promise<any>;
33
+ canExecute(action: Action | TramvaiAction<any, any, any>): boolean;
34
+ private getActionParameters;
31
35
  private getExecutionState;
32
36
  private canExecuteAction;
33
37
  private createActionContext;
@@ -1,19 +1,22 @@
1
1
  import flatten from '@tinkoff/utils/array/flatten';
2
2
  import identity from '@tinkoff/utils/function/identity';
3
+ import isPromise from '@tinkoff/utils/is/promise';
3
4
  import { isTramvaiAction, ACTION_PARAMETERS } from '@tramvai/core';
4
5
  import objectMap from '@tinkoff/utils/object/map';
5
6
  import { ConditionFailError } from '@tinkoff/errors';
6
7
  import { ActionChecker } from './actionChecker.es.js';
7
8
  import { actionType } from './constants.es.js';
8
9
  import { actionTramvaiReducer } from './actionTramvaiReducer.es.js';
10
+ import { Deferred } from './deferred/deferred.es.js';
9
11
 
10
12
  const EMPTY_DEPS = {};
11
13
  const getParameters = (action) => action[ACTION_PARAMETERS];
12
14
  class ActionExecution {
13
- constructor({ store, context, di, executionContextManager, actionConditionals, transformAction, }) {
15
+ constructor({ store, context, deferredActionsMap, di, executionContextManager, actionConditionals, transformAction, }) {
14
16
  this.actionConditionals = flatten(actionConditionals !== null && actionConditionals !== void 0 ? actionConditionals : []);
15
17
  this.context = context;
16
18
  this.store = store;
19
+ this.deferredActionsMap = deferredActionsMap;
17
20
  this.di = di;
18
21
  this.executionContextManager = executionContextManager;
19
22
  this.execution = new Map();
@@ -27,18 +30,10 @@ class ActionExecution {
27
30
  }
28
31
  async runInContext(executionContext, action, ...params) {
29
32
  var _a;
30
- let parameters;
31
33
  const payload = params[0];
32
34
  // TODO: replace type with pure context usage
33
35
  const type = (executionContext === null || executionContext === void 0 ? void 0 : executionContext.values.pageActions) === true ? actionType.global : actionType.local;
34
- // TODO: remove else branch after migration to new declareAction
35
- if (isTramvaiAction(action)) {
36
- parameters = action;
37
- }
38
- else {
39
- this.transformAction(action);
40
- parameters = getParameters(action);
41
- }
36
+ const parameters = this.getActionParameters(action);
42
37
  if (!parameters) {
43
38
  throw new Error('Cannot resolve internal data for action. Make sure you are using the result of `declareAction` call as an action');
44
39
  }
@@ -54,6 +49,14 @@ class ActionExecution {
54
49
  return Promise.resolve();
55
50
  }
56
51
  }
52
+ const isDeferredAction = parameters.deferred;
53
+ // will be created when spa run actions mode is "before"
54
+ if (isDeferredAction && !this.deferredActionsMap.get(action.name)) {
55
+ const deferred = new Deferred();
56
+ // avoid unhandled promise rejection
57
+ deferred.promise.catch(() => { });
58
+ this.deferredActionsMap.set(action.name, deferred);
59
+ }
57
60
  executionState.status = 'pending';
58
61
  return this.executionContextManager.withContext(executionContext, {
59
62
  name: parameters.name,
@@ -63,7 +66,20 @@ class ActionExecution {
63
66
  return Promise.resolve()
64
67
  .then(() => {
65
68
  if (isTramvaiAction(action)) {
66
- return action.fn.apply(context, params);
69
+ const result = action.fn.apply(context, params);
70
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
71
+ const deferred = this.deferredActionsMap.get(action.name);
72
+ if (isDeferredAction) {
73
+ if (!deferred.isResolved() && !deferred.isRejected() && isPromise(result)) {
74
+ // eslint-disable-next-line promise/no-nesting
75
+ result.then(deferred.resolve).catch(deferred.reject);
76
+ }
77
+ // pass success state for deferred actions to the client
78
+ // it will be helpful to detect deferred executions for SPA-transitions only
79
+ // eslint-disable-next-line promise/no-return-wrap
80
+ return Promise.resolve();
81
+ }
82
+ return result;
67
83
  }
68
84
  return action(this.context, payload, context.deps);
69
85
  })
@@ -80,9 +96,26 @@ class ActionExecution {
80
96
  async run(action, ...params) {
81
97
  return this.runInContext(null, action, ...params);
82
98
  }
99
+ canExecute(action) {
100
+ const parameters = this.getActionParameters(action);
101
+ const executionState = this.getExecutionState(parameters.name);
102
+ // @todo any cases when hadcoded global action type and payload could cause problems?
103
+ return this.canExecuteAction(undefined, parameters, executionState, actionType.global);
104
+ }
105
+ getActionParameters(action) {
106
+ let parameters;
107
+ // TODO: remove else branch after migration to new declareAction
108
+ if (isTramvaiAction(action)) {
109
+ parameters = action;
110
+ }
111
+ else {
112
+ this.transformAction(action);
113
+ parameters = getParameters(action);
114
+ }
115
+ return parameters;
116
+ }
83
117
  getExecutionState(name) {
84
118
  let executionState = this.execution.get(name);
85
- // TODO: probably do not need to create executionState on client as it is not used
86
119
  if (!executionState) {
87
120
  executionState = { status: 'pending', state: {} };
88
121
  this.execution.set(name, executionState);
@@ -4,26 +4,30 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var flatten = require('@tinkoff/utils/array/flatten');
6
6
  var identity = require('@tinkoff/utils/function/identity');
7
+ var isPromise = require('@tinkoff/utils/is/promise');
7
8
  var core = require('@tramvai/core');
8
9
  var objectMap = require('@tinkoff/utils/object/map');
9
10
  var errors = require('@tinkoff/errors');
10
11
  var actionChecker = require('./actionChecker.js');
11
12
  var constants = require('./constants.js');
12
13
  var actionTramvaiReducer = require('./actionTramvaiReducer.js');
14
+ var deferred = require('./deferred/deferred.js');
13
15
 
14
16
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
15
17
 
16
18
  var flatten__default = /*#__PURE__*/_interopDefaultLegacy(flatten);
17
19
  var identity__default = /*#__PURE__*/_interopDefaultLegacy(identity);
20
+ var isPromise__default = /*#__PURE__*/_interopDefaultLegacy(isPromise);
18
21
  var objectMap__default = /*#__PURE__*/_interopDefaultLegacy(objectMap);
19
22
 
20
23
  const EMPTY_DEPS = {};
21
24
  const getParameters = (action) => action[core.ACTION_PARAMETERS];
22
25
  class ActionExecution {
23
- constructor({ store, context, di, executionContextManager, actionConditionals, transformAction, }) {
26
+ constructor({ store, context, deferredActionsMap, di, executionContextManager, actionConditionals, transformAction, }) {
24
27
  this.actionConditionals = flatten__default["default"](actionConditionals !== null && actionConditionals !== void 0 ? actionConditionals : []);
25
28
  this.context = context;
26
29
  this.store = store;
30
+ this.deferredActionsMap = deferredActionsMap;
27
31
  this.di = di;
28
32
  this.executionContextManager = executionContextManager;
29
33
  this.execution = new Map();
@@ -37,18 +41,10 @@ class ActionExecution {
37
41
  }
38
42
  async runInContext(executionContext, action, ...params) {
39
43
  var _a;
40
- let parameters;
41
44
  const payload = params[0];
42
45
  // TODO: replace type with pure context usage
43
46
  const type = (executionContext === null || executionContext === void 0 ? void 0 : executionContext.values.pageActions) === true ? constants.actionType.global : constants.actionType.local;
44
- // TODO: remove else branch after migration to new declareAction
45
- if (core.isTramvaiAction(action)) {
46
- parameters = action;
47
- }
48
- else {
49
- this.transformAction(action);
50
- parameters = getParameters(action);
51
- }
47
+ const parameters = this.getActionParameters(action);
52
48
  if (!parameters) {
53
49
  throw new Error('Cannot resolve internal data for action. Make sure you are using the result of `declareAction` call as an action');
54
50
  }
@@ -64,6 +60,14 @@ class ActionExecution {
64
60
  return Promise.resolve();
65
61
  }
66
62
  }
63
+ const isDeferredAction = parameters.deferred;
64
+ // will be created when spa run actions mode is "before"
65
+ if (isDeferredAction && !this.deferredActionsMap.get(action.name)) {
66
+ const deferred$1 = new deferred.Deferred();
67
+ // avoid unhandled promise rejection
68
+ deferred$1.promise.catch(() => { });
69
+ this.deferredActionsMap.set(action.name, deferred$1);
70
+ }
67
71
  executionState.status = 'pending';
68
72
  return this.executionContextManager.withContext(executionContext, {
69
73
  name: parameters.name,
@@ -73,7 +77,20 @@ class ActionExecution {
73
77
  return Promise.resolve()
74
78
  .then(() => {
75
79
  if (core.isTramvaiAction(action)) {
76
- return action.fn.apply(context, params);
80
+ const result = action.fn.apply(context, params);
81
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
82
+ const deferred = this.deferredActionsMap.get(action.name);
83
+ if (isDeferredAction) {
84
+ if (!deferred.isResolved() && !deferred.isRejected() && isPromise__default["default"](result)) {
85
+ // eslint-disable-next-line promise/no-nesting
86
+ result.then(deferred.resolve).catch(deferred.reject);
87
+ }
88
+ // pass success state for deferred actions to the client
89
+ // it will be helpful to detect deferred executions for SPA-transitions only
90
+ // eslint-disable-next-line promise/no-return-wrap
91
+ return Promise.resolve();
92
+ }
93
+ return result;
77
94
  }
78
95
  return action(this.context, payload, context.deps);
79
96
  })
@@ -90,9 +107,26 @@ class ActionExecution {
90
107
  async run(action, ...params) {
91
108
  return this.runInContext(null, action, ...params);
92
109
  }
110
+ canExecute(action) {
111
+ const parameters = this.getActionParameters(action);
112
+ const executionState = this.getExecutionState(parameters.name);
113
+ // @todo any cases when hadcoded global action type and payload could cause problems?
114
+ return this.canExecuteAction(undefined, parameters, executionState, constants.actionType.global);
115
+ }
116
+ getActionParameters(action) {
117
+ let parameters;
118
+ // TODO: remove else branch after migration to new declareAction
119
+ if (core.isTramvaiAction(action)) {
120
+ parameters = action;
121
+ }
122
+ else {
123
+ this.transformAction(action);
124
+ parameters = getParameters(action);
125
+ }
126
+ return parameters;
127
+ }
93
128
  getExecutionState(name) {
94
129
  let executionState = this.execution.get(name);
95
- // TODO: probably do not need to create executionState on client as it is not used
96
130
  if (!executionState) {
97
131
  executionState = { status: 'pending', state: {} };
98
132
  this.execution.set(name, executionState);
@@ -1,6 +1,5 @@
1
1
  import { isSilentError } from '@tinkoff/errors';
2
- import { ACTION_PARAMETERS, isTramvaiAction } from '@tramvai/core';
3
- import { Deferred } from './deferred/deferred.browser.js';
2
+ import { isTramvaiAction, ACTION_PARAMETERS } from '@tramvai/core';
4
3
 
5
4
  const DEFAULT_PAYLOAD = {};
6
5
  class ActionPageRunner {
@@ -11,27 +10,9 @@ class ActionPageRunner {
11
10
  runActions(actions, stopRunAtError = () => false) {
12
11
  return this.deps.executionContextManager.withContext(this.deps.commandLineExecutionContext(), { name: 'pageActions', values: { pageActions: true } }, async (executionContext) => {
13
12
  const actionMapper = (action) => {
14
- const isDeferredAction = ACTION_PARAMETERS in action && action[ACTION_PARAMETERS].deferred;
15
- if (isDeferredAction && !this.deps.deferredMap.get(action.name)) {
16
- const deferred = new Deferred();
17
- // avoid unhandled promise rejection
18
- deferred.promise.catch(() => { });
19
- this.deps.deferredMap.set(action.name, deferred);
20
- }
21
13
  return Promise.resolve()
22
14
  .then(() => {
23
15
  const promise = this.deps.actionExecution.runInContext(executionContext, action, DEFAULT_PAYLOAD);
24
- if (isDeferredAction) {
25
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
26
- const deferred = this.deps.deferredMap.get(action.name);
27
- // @todo check always: true actions
28
- if (!deferred.isResolved() && !deferred.isRejected()) {
29
- // eslint-disable-next-line promise/no-nesting
30
- promise.then(deferred.resolve).catch(deferred.reject);
31
- }
32
- // eslint-disable-next-line promise/no-return-wrap
33
- return Promise.resolve();
34
- }
35
16
  return promise;
36
17
  })
37
18
  .catch((error) => {
@@ -1,6 +1,6 @@
1
1
  import type { Action, ExtractDependencyType, TramvaiAction } from '@tramvai/core';
2
2
  import type { LOGGER_TOKEN } from '@tramvai/module-log';
3
- import type { COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN, DEFERRED_ACTIONS_MAP_TOKEN } from '@tramvai/tokens-common';
3
+ import type { COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN } from '@tramvai/tokens-common';
4
4
  import type { ActionExecution } from './actionExecution';
5
5
  export declare class ActionPageRunner {
6
6
  private deps;
@@ -10,7 +10,6 @@ export declare class ActionPageRunner {
10
10
  executionContextManager: ExtractDependencyType<typeof EXECUTION_CONTEXT_MANAGER_TOKEN>;
11
11
  commandLineExecutionContext: ExtractDependencyType<typeof COMMAND_LINE_EXECUTION_CONTEXT_TOKEN>;
12
12
  logger: ExtractDependencyType<typeof LOGGER_TOKEN>;
13
- deferredMap: ExtractDependencyType<typeof DEFERRED_ACTIONS_MAP_TOKEN>;
14
13
  });
15
14
  runActions(actions: Array<Action | TramvaiAction<any[], any, any>>, stopRunAtError?: (error: Error) => boolean): Promise<void>;
16
15
  }
@@ -1,7 +1,6 @@
1
1
  import { ACTION_PARAMETERS, isTramvaiAction } from '@tramvai/core';
2
2
  import { isSilentError } from '@tinkoff/errors';
3
3
  import { actionServerStateEvent } from './actionTramvaiReducer.es.js';
4
- import { Deferred } from './deferred/deferred.es.js';
5
4
  import { generateDeferredResolve, generateDeferredReject } from './deferred/clientScriptsUtils.es.js';
6
5
 
7
6
  const DEFAULT_PAYLOAD = {};
@@ -38,22 +37,14 @@ class ActionPageRunner {
38
37
  };
39
38
  const actionMapper = (action) => {
40
39
  const isDeferredAction = ACTION_PARAMETERS in action && action[ACTION_PARAMETERS].deferred;
41
- if (isDeferredAction && !this.deferredMap.get(action.name)) {
42
- const deferred = new Deferred();
43
- // avoid unhandled promise rejection
44
- deferred.promise.catch(() => { });
45
- this.deferredMap.set(action.name, deferred);
46
- }
47
40
  return Promise.resolve()
48
41
  .then(() => {
49
42
  var _a;
50
43
  const promise = this.deps.actionExecution.runInContext(executionContext, action, DEFAULT_PAYLOAD);
51
44
  if (isDeferredAction) {
52
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
53
- const deferred = this.deferredMap.get(action.name);
54
- // eslint-disable-next-line promise/no-nesting
55
- promise.then(deferred.resolve).catch(deferred.reject);
56
45
  (_a = this.responseTaskManager) === null || _a === void 0 ? void 0 : _a.push(async () => {
46
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
47
+ const deferred = this.deferredMap.get(action.name);
57
48
  // scripts will already be present on the page HTML
58
49
  if (deferred.isResolved() || deferred.isRejected()) {
59
50
  return;
@@ -75,8 +66,6 @@ class ActionPageRunner {
75
66
  })}</script>`);
76
67
  });
77
68
  });
78
- // eslint-disable-next-line promise/no-return-wrap
79
- return Promise.resolve();
80
69
  }
81
70
  return promise;
82
71
  })
@@ -5,7 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var core = require('@tramvai/core');
6
6
  var errors = require('@tinkoff/errors');
7
7
  var actionTramvaiReducer = require('./actionTramvaiReducer.js');
8
- var deferred = require('./deferred/deferred.js');
9
8
  var clientScriptsUtils = require('./deferred/clientScriptsUtils.js');
10
9
 
11
10
  const DEFAULT_PAYLOAD = {};
@@ -42,22 +41,14 @@ class ActionPageRunner {
42
41
  };
43
42
  const actionMapper = (action) => {
44
43
  const isDeferredAction = core.ACTION_PARAMETERS in action && action[core.ACTION_PARAMETERS].deferred;
45
- if (isDeferredAction && !this.deferredMap.get(action.name)) {
46
- const deferred$1 = new deferred.Deferred();
47
- // avoid unhandled promise rejection
48
- deferred$1.promise.catch(() => { });
49
- this.deferredMap.set(action.name, deferred$1);
50
- }
51
44
  return Promise.resolve()
52
45
  .then(() => {
53
46
  var _a;
54
47
  const promise = this.deps.actionExecution.runInContext(executionContext, action, DEFAULT_PAYLOAD);
55
48
  if (isDeferredAction) {
56
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
57
- const deferred = this.deferredMap.get(action.name);
58
- // eslint-disable-next-line promise/no-nesting
59
- promise.then(deferred.resolve).catch(deferred.reject);
60
49
  (_a = this.responseTaskManager) === null || _a === void 0 ? void 0 : _a.push(async () => {
50
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
51
+ const deferred = this.deferredMap.get(action.name);
61
52
  // scripts will already be present on the page HTML
62
53
  if (deferred.isResolved() || deferred.isRejected()) {
63
54
  return;
@@ -79,8 +70,6 @@ class ActionPageRunner {
79
70
  })}</script>`);
80
71
  });
81
72
  });
82
- // eslint-disable-next-line promise/no-return-wrap
83
- return Promise.resolve();
84
73
  }
85
74
  return promise;
86
75
  })
@@ -0,0 +1,21 @@
1
+ import { isServer, isBrowser } from './helpers.browser.js';
2
+
3
+ // @todo make this behaviour default in next tramvai major version?
4
+ const dynamicCondition = {
5
+ key: 'dynamicCondition',
6
+ fn: (checker) => {
7
+ if (checker.conditions.dynamic) {
8
+ if (isServer) {
9
+ checker.setState({ environment: 'server' });
10
+ }
11
+ if (!checker.getState() || checker.getState().environment === 'browser') {
12
+ checker.allow();
13
+ }
14
+ if (isBrowser) {
15
+ checker.setState({ environment: 'browser' });
16
+ }
17
+ }
18
+ },
19
+ };
20
+
21
+ export { dynamicCondition };
@@ -0,0 +1,2 @@
1
+ import type { ActionCondition } from '@tramvai/tokens-common';
2
+ export declare const dynamicCondition: ActionCondition;
@@ -0,0 +1,21 @@
1
+ import { isServer, isBrowser } from './helpers.es.js';
2
+
3
+ // @todo make this behaviour default in next tramvai major version?
4
+ const dynamicCondition = {
5
+ key: 'dynamicCondition',
6
+ fn: (checker) => {
7
+ if (checker.conditions.dynamic) {
8
+ if (isServer) {
9
+ checker.setState({ environment: 'server' });
10
+ }
11
+ if (!checker.getState() || checker.getState().environment === 'browser') {
12
+ checker.allow();
13
+ }
14
+ if (isBrowser) {
15
+ checker.setState({ environment: 'browser' });
16
+ }
17
+ }
18
+ },
19
+ };
20
+
21
+ export { dynamicCondition };
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var helpers = require('./helpers.js');
6
+
7
+ // @todo make this behaviour default in next tramvai major version?
8
+ const dynamicCondition = {
9
+ key: 'dynamicCondition',
10
+ fn: (checker) => {
11
+ if (checker.conditions.dynamic) {
12
+ if (helpers.isServer) {
13
+ checker.setState({ environment: 'server' });
14
+ }
15
+ if (!checker.getState() || checker.getState().environment === 'browser') {
16
+ checker.allow();
17
+ }
18
+ if (helpers.isBrowser) {
19
+ checker.setState({ environment: 'browser' });
20
+ }
21
+ }
22
+ },
23
+ };
24
+
25
+ exports.dynamicCondition = dynamicCondition;
@@ -10,7 +10,7 @@ function Await({ action, children, error, }) {
10
10
  if (!action.deferred) {
11
11
  throw new Error(`Action ${action.name} is not deferred`);
12
12
  }
13
- // for SPA-transitions, when actions will be called after rendering
13
+ // will be created when spa run actions mode is "after"
14
14
  if (!deferred) {
15
15
  deferred = new Deferred();
16
16
  // avoid unhandled promise rejection
@@ -10,7 +10,7 @@ function Await({ action, children, error, }) {
10
10
  if (!action.deferred) {
11
11
  throw new Error(`Action ${action.name} is not deferred`);
12
12
  }
13
- // for SPA-transitions, when actions will be called after rendering
13
+ // will be created when spa run actions mode is "after"
14
14
  if (!deferred) {
15
15
  deferred = new Deferred();
16
16
  // avoid unhandled promise rejection
@@ -14,7 +14,7 @@ function Await({ action, children, error, }) {
14
14
  if (!action.deferred) {
15
15
  throw new Error(`Action ${action.name} is not deferred`);
16
16
  }
17
- // for SPA-transitions, when actions will be called after rendering
17
+ // will be created when spa run actions mode is "after"
18
18
  if (!deferred$1) {
19
19
  deferred$1 = new deferred.Deferred();
20
20
  // avoid unhandled promise rejection
@@ -1,6 +1,20 @@
1
1
  class Deferred {
2
2
  constructor() {
3
- this.promise = new Promise((resolve, reject) => {
3
+ this.promise = this.initPromise();
4
+ }
5
+ isResolved() {
6
+ return typeof this.resolveData !== 'undefined';
7
+ }
8
+ isRejected() {
9
+ return typeof this.rejectReason !== 'undefined';
10
+ }
11
+ reset() {
12
+ this.resolveData = undefined;
13
+ this.rejectReason = undefined;
14
+ this.promise = this.initPromise();
15
+ }
16
+ initPromise() {
17
+ return new Promise((resolve, reject) => {
4
18
  this.resolve = (value) => {
5
19
  this.resolveData = value;
6
20
  resolve(value);
@@ -11,12 +25,6 @@ class Deferred {
11
25
  };
12
26
  });
13
27
  }
14
- isResolved() {
15
- return typeof this.resolveData !== 'undefined';
16
- }
17
- isRejected() {
18
- return typeof this.rejectReason !== 'undefined';
19
- }
20
28
  }
21
29
 
22
30
  export { Deferred };
@@ -8,4 +8,6 @@ export declare class Deferred implements IDeferred {
8
8
  constructor();
9
9
  isResolved(): boolean;
10
10
  isRejected(): boolean;
11
+ reset(): void;
12
+ private initPromise;
11
13
  }
@@ -1,6 +1,20 @@
1
1
  class Deferred {
2
2
  constructor() {
3
- this.promise = new Promise((resolve, reject) => {
3
+ this.promise = this.initPromise();
4
+ }
5
+ isResolved() {
6
+ return typeof this.resolveData !== 'undefined';
7
+ }
8
+ isRejected() {
9
+ return typeof this.rejectReason !== 'undefined';
10
+ }
11
+ reset() {
12
+ this.resolveData = undefined;
13
+ this.rejectReason = undefined;
14
+ this.promise = this.initPromise();
15
+ }
16
+ initPromise() {
17
+ return new Promise((resolve, reject) => {
4
18
  this.resolve = (value) => {
5
19
  this.resolveData = value;
6
20
  resolve(value);
@@ -11,12 +25,6 @@ class Deferred {
11
25
  };
12
26
  });
13
27
  }
14
- isResolved() {
15
- return typeof this.resolveData !== 'undefined';
16
- }
17
- isRejected() {
18
- return typeof this.rejectReason !== 'undefined';
19
- }
20
28
  }
21
29
 
22
30
  export { Deferred };
@@ -1,2 +1,2 @@
1
1
  import type { Deferred } from '@tramvai/tokens-common';
2
- export declare function __Deferred(this: Deferred): void;
2
+ export declare function __Deferred(this: Deferred & Record<string, any>): void;
@@ -1,20 +1,28 @@
1
1
  function __Deferred() {
2
- this.promise = new Promise((resolve, reject) => {
3
- this.resolve = (value) => {
4
- this.resolveData = value;
5
- resolve(value);
6
- };
7
- this.reject = (reason) => {
8
- this.rejectReason = reason;
9
- reject(reason);
10
- };
11
- });
2
+ this.initPromise = () => {
3
+ return new Promise((resolve, reject) => {
4
+ this.resolve = (value) => {
5
+ this.resolveData = value;
6
+ resolve(value);
7
+ };
8
+ this.reject = (reason) => {
9
+ this.rejectReason = reason;
10
+ reject(reason);
11
+ };
12
+ });
13
+ };
14
+ this.promise = this.initPromise();
12
15
  this.isResolved = () => {
13
16
  return typeof this.resolveData !== 'undefined';
14
17
  };
15
18
  this.isRejected = () => {
16
19
  return typeof this.rejectReason !== 'undefined';
17
20
  };
21
+ this.reset = () => {
22
+ this.resolveData = undefined;
23
+ this.rejectReason = undefined;
24
+ this.promise = this.initPromise();
25
+ };
18
26
  }
19
27
 
20
28
  export { __Deferred };
@@ -3,22 +3,30 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  function __Deferred() {
6
- this.promise = new Promise((resolve, reject) => {
7
- this.resolve = (value) => {
8
- this.resolveData = value;
9
- resolve(value);
10
- };
11
- this.reject = (reason) => {
12
- this.rejectReason = reason;
13
- reject(reason);
14
- };
15
- });
6
+ this.initPromise = () => {
7
+ return new Promise((resolve, reject) => {
8
+ this.resolve = (value) => {
9
+ this.resolveData = value;
10
+ resolve(value);
11
+ };
12
+ this.reject = (reason) => {
13
+ this.rejectReason = reason;
14
+ reject(reason);
15
+ };
16
+ });
17
+ };
18
+ this.promise = this.initPromise();
16
19
  this.isResolved = () => {
17
20
  return typeof this.resolveData !== 'undefined';
18
21
  };
19
22
  this.isRejected = () => {
20
23
  return typeof this.rejectReason !== 'undefined';
21
24
  };
25
+ this.reset = () => {
26
+ this.resolveData = undefined;
27
+ this.rejectReason = undefined;
28
+ this.promise = this.initPromise();
29
+ };
22
30
  }
23
31
 
24
32
  exports.__Deferred = __Deferred;
@@ -4,7 +4,21 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  class Deferred {
6
6
  constructor() {
7
- this.promise = new Promise((resolve, reject) => {
7
+ this.promise = this.initPromise();
8
+ }
9
+ isResolved() {
10
+ return typeof this.resolveData !== 'undefined';
11
+ }
12
+ isRejected() {
13
+ return typeof this.rejectReason !== 'undefined';
14
+ }
15
+ reset() {
16
+ this.resolveData = undefined;
17
+ this.rejectReason = undefined;
18
+ this.promise = this.initPromise();
19
+ }
20
+ initPromise() {
21
+ return new Promise((resolve, reject) => {
8
22
  this.resolve = (value) => {
9
23
  this.resolveData = value;
10
24
  resolve(value);
@@ -15,12 +29,6 @@ class Deferred {
15
29
  };
16
30
  });
17
31
  }
18
- isResolved() {
19
- return typeof this.resolveData !== 'undefined';
20
- }
21
- isRejected() {
22
- return typeof this.rejectReason !== 'undefined';
23
- }
24
32
  }
25
33
 
26
34
  exports.Deferred = Deferred;
@@ -5,6 +5,9 @@ const providers = [
5
5
  provide({
6
6
  provide: DEFERRED_ACTIONS_MAP_TOKEN,
7
7
  useFactory: () => {
8
+ if (!window.__TRAMVAI_DEFERRED_ACTIONS) {
9
+ window.__TRAMVAI_DEFERRED_ACTIONS = {};
10
+ }
8
11
  return {
9
12
  get(key) {
10
13
  return window.__TRAMVAI_DEFERRED_ACTIONS[key];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-common",
3
- "version": "2.145.1",
3
+ "version": "2.146.1",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -36,28 +36,28 @@
36
36
  "@tinkoff/pubsub": "0.5.7",
37
37
  "@tinkoff/url": "0.8.6",
38
38
  "@tramvai/safe-strings": "0.5.12",
39
- "@tramvai/experiments": "2.145.1",
40
- "@tramvai/module-cookie": "2.145.1",
41
- "@tramvai/module-environment": "2.145.1",
42
- "@tramvai/module-log": "2.145.1",
43
- "@tramvai/tokens-child-app": "2.145.1",
44
- "@tramvai/tokens-core-private": "2.145.1",
45
- "@tramvai/tokens-common": "2.145.1",
46
- "@tramvai/tokens-render": "2.145.1",
47
- "@tramvai/tokens-server-private": "2.145.1",
48
- "@tramvai/types-actions-state-context": "2.145.1",
39
+ "@tramvai/experiments": "2.146.1",
40
+ "@tramvai/module-cookie": "2.146.1",
41
+ "@tramvai/module-environment": "2.146.1",
42
+ "@tramvai/module-log": "2.146.1",
43
+ "@tramvai/tokens-child-app": "2.146.1",
44
+ "@tramvai/tokens-core-private": "2.146.1",
45
+ "@tramvai/tokens-common": "2.146.1",
46
+ "@tramvai/tokens-render": "2.146.1",
47
+ "@tramvai/tokens-server-private": "2.146.1",
48
+ "@tramvai/types-actions-state-context": "2.146.1",
49
49
  "hoist-non-react-statics": "^3.3.1",
50
50
  "node-abort-controller": "^3.0.1"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "@tinkoff/dippy": "0.8.15",
54
54
  "@tinkoff/utils": "^2.1.2",
55
- "@tramvai/cli": "2.145.1",
56
- "@tramvai/core": "2.145.1",
57
- "@tramvai/papi": "2.145.1",
58
- "@tramvai/react": "2.145.1",
59
- "@tramvai/state": "2.145.1",
60
- "@tramvai/tokens-server": "2.145.1",
55
+ "@tramvai/cli": "2.146.1",
56
+ "@tramvai/core": "2.146.1",
57
+ "@tramvai/papi": "2.146.1",
58
+ "@tramvai/react": "2.146.1",
59
+ "@tramvai/state": "2.146.1",
60
+ "@tramvai/tokens-server": "2.146.1",
61
61
  "react": ">=16.14.0",
62
62
  "tslib": "^2.4.0"
63
63
  },