@evergis/react 4.0.166 → 4.0.168
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Dashboard/actions/components/TaskExecutionHost/index.d.ts +2 -0
- package/dist/components/Dashboard/actions/conditions/constants.d.ts +4 -0
- package/dist/components/Dashboard/actions/conditions/evaluateActionCondition.d.ts +6 -0
- package/dist/components/Dashboard/actions/conditions/operators.d.ts +4 -0
- package/dist/components/Dashboard/actions/conditions/parseActionCondition.d.ts +6 -0
- package/dist/components/Dashboard/actions/conditions/tokenizeActionCondition.d.ts +2 -0
- package/dist/components/Dashboard/actions/conditions/types.d.ts +43 -0
- package/dist/components/Dashboard/actions/constants.d.ts +4 -0
- package/dist/components/Dashboard/actions/context/ActionContextProvider.d.ts +8 -0
- package/dist/components/Dashboard/actions/context/ActionNodeProvider.d.ts +3 -0
- package/dist/components/Dashboard/actions/context/ActionScopeProvider.d.ts +5 -0
- package/dist/components/Dashboard/actions/context/WidgetActionsProvider/hooks/useActionExecutors.d.ts +3 -0
- package/dist/components/Dashboard/actions/context/WidgetActionsProvider/hooks/useActionFilters.d.ts +6 -0
- package/dist/components/Dashboard/actions/context/WidgetActionsProvider/index.d.ts +2 -0
- package/dist/components/Dashboard/actions/context/WidgetActionsProvider/types.d.ts +14 -0
- package/dist/components/Dashboard/actions/context/bindingTypes.d.ts +46 -0
- package/dist/components/Dashboard/actions/executors/ActionNavigationError.d.ts +4 -0
- package/dist/components/Dashboard/actions/executors/createActionNavigation.d.ts +5 -0
- package/dist/components/Dashboard/actions/executors/needsNavigationWindow.d.ts +2 -0
- package/dist/components/Dashboard/actions/hooks/useActionBindings.d.ts +2 -0
- package/dist/components/Dashboard/actions/hooks/useChartActions.d.ts +10 -0
- package/dist/components/Dashboard/actions/index.d.ts +20 -0
- package/dist/components/Dashboard/actions/modals/WidgetModalHost/ModalWindow.d.ts +2 -0
- package/dist/components/Dashboard/actions/modals/WidgetModalHost/index.d.ts +2 -0
- package/dist/components/Dashboard/actions/modals/WidgetModalHost/types.d.ts +6 -0
- package/dist/components/Dashboard/actions/modals/WidgetModalsProvider/index.d.ts +2 -0
- package/dist/components/Dashboard/actions/modals/hooks/useAvailableModals.d.ts +2 -0
- package/dist/components/Dashboard/actions/modals/hooks/useModalAutoSync.d.ts +2 -0
- package/dist/components/Dashboard/actions/modals/hooks/useModalSources.d.ts +8 -0
- package/dist/components/Dashboard/actions/modals/hooks/useWidgetModals.d.ts +2 -0
- package/dist/components/Dashboard/actions/modals/types.d.ts +26 -0
- package/dist/components/Dashboard/actions/modals/utils/createModalStore.d.ts +16 -0
- package/dist/components/Dashboard/actions/modals/utils/modalRequestKey.d.ts +4 -0
- package/dist/components/Dashboard/actions/parameters/resolveActionParameters.d.ts +3 -0
- package/dist/components/Dashboard/actions/registry/mergeActionParameters.d.ts +1 -0
- package/dist/components/Dashboard/actions/registry/resolveActionInvocation.d.ts +2 -0
- package/dist/components/Dashboard/actions/runtime/awaitActionStep.d.ts +2 -0
- package/dist/components/Dashboard/actions/runtime/createActionRuntime.d.ts +3 -0
- package/dist/components/Dashboard/actions/runtime/executeActionSequence.d.ts +3 -0
- package/dist/components/Dashboard/actions/runtime/types.d.ts +36 -0
- package/dist/components/Dashboard/actions/types.d.ts +81 -0
- package/dist/components/Dashboard/actions/validation/actionShape.d.ts +2 -0
- package/dist/components/Dashboard/actions/validation/isActionSelectedFilter.d.ts +1 -0
- package/dist/components/Dashboard/actions/validation/types.d.ts +15 -0
- package/dist/components/Dashboard/actions/validation/validateActionTree.d.ts +5 -0
- package/dist/components/Dashboard/actions/validation/validateDashboardActions.d.ts +3 -0
- package/dist/components/Dashboard/componentTypes.d.ts +27 -9
- package/dist/components/Dashboard/components/Chart/components/BarChartView.d.ts +2 -0
- package/dist/components/Dashboard/components/Chart/components/LineChartView.d.ts +2 -0
- package/dist/components/Dashboard/components/Chart/components/PieChartView.d.ts +2 -0
- package/dist/components/Dashboard/components/Chart/components/StackChartView.d.ts +2 -0
- package/dist/components/Dashboard/components/Chart/constants.d.ts +2 -0
- package/dist/components/Dashboard/components/Chart/hooks/index.d.ts +2 -0
- package/dist/components/Dashboard/components/Chart/hooks/useChartLayout.d.ts +71 -0
- package/dist/components/Dashboard/components/Chart/hooks/useChartPresentation.d.ts +30 -0
- package/dist/components/Dashboard/components/Chart/types.d.ts +6 -1
- package/dist/components/Dashboard/components/ContainerButton/index.d.ts +4 -0
- package/dist/components/Dashboard/components/StackBar/index.d.ts +2 -1
- package/dist/components/Dashboard/components/SvgImage/index.d.ts +2 -2
- package/dist/components/Dashboard/constants.d.ts +2 -19
- package/dist/components/Dashboard/containers/DataSourceInnerContainer/styled.d.ts +0 -1
- package/dist/components/Dashboard/containers/StructuredDataContainer/types.d.ts +3 -1
- package/dist/components/Dashboard/containers/TaskContainer/components/LogDialog/types.d.ts +1 -0
- package/dist/components/Dashboard/containers/TaskContainer/components/TaskLogButton.d.ts +5 -0
- package/dist/components/Dashboard/containers/TaskContainer/hooks/useTaskButtonState.d.ts +1 -1
- package/dist/components/Dashboard/containers/VoteContainer/components/VoteScreen.d.ts +2 -2
- package/dist/components/Dashboard/containers/VoteContainer/types.d.ts +10 -3
- package/dist/components/Dashboard/containers/styled.d.ts +1 -1
- package/dist/components/Dashboard/elements/ElementIcon/components/AttachmentIcon/types.d.ts +2 -2
- package/dist/components/Dashboard/elements/ElementImage/useElementImage.d.ts +3 -2
- package/dist/components/Dashboard/elements/ElementModal/hooks/useModalOpen.d.ts +2 -7
- package/dist/components/Dashboard/elements/ElementSvg/index.d.ts +0 -1
- package/dist/components/Dashboard/elements/ElementTable/components/ActionTableRow.d.ts +6 -0
- package/dist/components/Dashboard/elements/registry.d.ts +1 -1
- package/dist/components/Dashboard/hooks/dataSourceTypes.d.ts +15 -0
- package/dist/components/Dashboard/hooks/useDataSourceRequests.d.ts +4 -0
- package/dist/components/Dashboard/hooks/useDataSources.d.ts +6 -13
- package/dist/components/Dashboard/hooks/useWidgetContext.d.ts +7 -1
- package/dist/components/Dashboard/index.d.ts +1 -0
- package/dist/components/Dashboard/types.d.ts +18 -6
- package/dist/components/Dashboard/utils/applyQueryFilters.d.ts +5 -4
- package/dist/components/Dashboard/utils/checkEqualOrIncludes.d.ts +1 -1
- package/dist/components/Dashboard/utils/configString/constants.d.ts +19 -0
- package/dist/components/Dashboard/utils/configString/resolveActionToken.d.ts +3 -0
- package/dist/components/Dashboard/utils/configString/types.d.ts +10 -1
- package/dist/components/Dashboard/utils/findDashboardNode.d.ts +3 -0
- package/dist/components/Dashboard/utils/formatDataSourceCondition.d.ts +2 -1
- package/dist/components/Dashboard/utils/getButtonSlot.d.ts +4 -0
- package/dist/components/Dashboard/utils/getDataFromAttributes.d.ts +2 -2
- package/dist/components/Dashboard/utils/getSelectedFilterValue.d.ts +1 -1
- package/dist/components/Dashboard/utils/getUpdatedDataSources.d.ts +2 -0
- package/dist/components/Dashboard/utils/index.d.ts +1 -0
- package/dist/contexts/ActionContext/index.d.ts +3 -0
- package/dist/contexts/ActionNodeContext/index.d.ts +2 -0
- package/dist/contexts/ActionScopeContext/index.d.ts +3 -0
- package/dist/contexts/ChartActionsContext/index.d.ts +2 -0
- package/dist/contexts/DashboardContext/types.d.ts +15 -6
- package/dist/contexts/FeatureCardContext/types.d.ts +14 -1
- package/dist/{components/Dashboard/grid → contexts}/GridEditContext/index.d.ts +1 -1
- package/dist/contexts/ModalDataContext/index.d.ts +2 -0
- package/dist/{components/Dashboard/containers/StructuredDataContainer/StructuredDataContext.d.ts → contexts/StructuredDataContext/index.d.ts} +1 -1
- package/dist/contexts/TaskExecutionContext/TaskExecutionProvider.d.ts +2 -0
- package/dist/contexts/TaskExecutionContext/context.d.ts +2 -0
- package/dist/contexts/TaskExecutionContext/hooks/useTaskExecution.d.ts +17 -0
- package/dist/contexts/TaskExecutionContext/index.d.ts +4 -0
- package/dist/contexts/TaskExecutionContext/types.d.ts +14 -0
- package/dist/contexts/TaskExecutionContext/utils/createTaskNotification.d.ts +4 -0
- package/dist/contexts/WidgetActionsContext/index.d.ts +4 -0
- package/dist/contexts/WidgetModalsContext/index.d.ts +3 -0
- package/dist/contexts/index.d.ts +12 -0
- package/dist/dashboardActions/index.js +577 -0
- package/dist/dashboardActions/index.js.map +1 -0
- package/dist/dashboardActions/package.json +1 -0
- package/dist/dashboardActions.d.ts +3 -0
- package/dist/hooks/task/index.d.ts +2 -0
- package/dist/hooks/task/runtime/PythonExecutionError.d.ts +9 -0
- package/dist/hooks/task/runtime/constants.d.ts +2 -0
- package/dist/hooks/task/runtime/resolvePythonTaskDefinition.d.ts +2 -0
- package/dist/hooks/task/runtime/startPythonExecution.d.ts +2 -0
- package/dist/hooks/task/runtime/types.d.ts +53 -0
- package/dist/hooks/task/types.d.ts +7 -1
- package/dist/hooks/task/usePythonTask.d.ts +12 -14
- package/dist/hooks/task/utils/pollSubtaskCompletion.d.ts +4 -1
- package/dist/hooks/task/utils/startRemoteTask.d.ts +2 -0
- package/dist/index.js +15164 -13214
- package/dist/index.js.map +1 -1
- package/dist/react.esm.js +14860 -12954
- package/dist/react.esm.js.map +1 -1
- package/dist/taskParameters/index.js +329 -0
- package/dist/taskParameters/index.js.map +1 -0
- package/dist/taskParameters/package.json +1 -0
- package/dist/taskParameters.d.ts +2 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/isObject.d.ts +1 -1
- package/dist/utils/taskParameters/SchemaResolver.d.ts +14 -0
- package/dist/utils/taskParameters/buildDefaultForSchema.d.ts +23 -0
- package/dist/utils/taskParameters/getPrimaryType.d.ts +2 -0
- package/dist/utils/taskParameters/index.d.ts +6 -0
- package/dist/utils/taskParameters/mergeHiddenFieldDefaults.d.ts +2 -0
- package/dist/utils/taskParameters/orderPropertyKeys.d.ts +2 -0
- package/dist/utils/taskParameters/types.d.ts +36 -0
- package/package.json +2 -2
- package/dist/components/Dashboard/containers/TaskContainer/useTaskNotifications.d.ts +0 -12
- /package/dist/{components/Dashboard/components/Chart/ChartPlotContext.d.ts → contexts/ChartPlotContext/index.d.ts} +0 -0
- /package/dist/{components/Dashboard/components/Chart/FillContext.d.ts → contexts/FillContext/index.d.ts} +0 -0
- /package/dist/{components/Dashboard/containers/TaskContainer → contexts/TaskExecutionContext}/utils/clampDescription.d.ts +0 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function isObject(value) {
|
|
4
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const BINARY_PRECEDENCE = {
|
|
8
|
+
"||": 1,
|
|
9
|
+
"&&": 2,
|
|
10
|
+
"===": 3,
|
|
11
|
+
"!==": 3,
|
|
12
|
+
"==": 3,
|
|
13
|
+
"!=": 3,
|
|
14
|
+
">": 4,
|
|
15
|
+
">=": 4,
|
|
16
|
+
"<": 4,
|
|
17
|
+
"<=": 4,
|
|
18
|
+
"+": 5,
|
|
19
|
+
"-": 5,
|
|
20
|
+
"*": 6,
|
|
21
|
+
"/": 6,
|
|
22
|
+
"%": 6,
|
|
23
|
+
};
|
|
24
|
+
const UNARY_PRECEDENCE = 7;
|
|
25
|
+
const CONDITION_TOKEN = new RegExp([
|
|
26
|
+
String.raw `^(?:(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|`,
|
|
27
|
+
String.raw `((?:#[\w]+|\$(?:event|params)(?:\.(?!includes\b)[\w]+)*|`,
|
|
28
|
+
String.raw `\$(?:dashboard|card):[\w]+(?::[\w]+)?|%[\w]+(?:\.(?!includes\b)[\w]+)?|\{[^{}]+\}))|`,
|
|
29
|
+
String.raw `(===|!==|>=|<=|==|!=|&&|\|\||[+*/%<>!()[\],.-])|(true|false|null|Date\.parse|includes)\b)`,
|
|
30
|
+
].join(""));
|
|
31
|
+
|
|
32
|
+
// Грамматика подстановок независима от React и графиков: она нужна также проверке JSON-конфигов.
|
|
33
|
+
const FILTER_PREFIX = "%";
|
|
34
|
+
const PROVIDER_PREFIX = "$";
|
|
35
|
+
var ProviderPrefix;
|
|
36
|
+
(function (ProviderPrefix) {
|
|
37
|
+
/** `$card:<слой>:<поле>` — атрибут объекта карточки. */
|
|
38
|
+
ProviderPrefix["Card"] = "card";
|
|
39
|
+
/**
|
|
40
|
+
* Ближайший дашборд, в ряд к `%` своего виджета: `$dashboard:<фильтр>[.свойство]` — его фильтр,
|
|
41
|
+
* `$dashboard:<датасорс>:<поле>` — поле первой строки его датасорса.
|
|
42
|
+
*/
|
|
43
|
+
ProviderPrefix["Dashboard"] = "dashboard";
|
|
44
|
+
})(ProviderPrefix || (ProviderPrefix = {}));
|
|
45
|
+
/**
|
|
46
|
+
* Имя слоя или датасорса в подстановке: буквы, цифры, `_`, `.` и `-`. Более широкий класс захватил бы
|
|
47
|
+
* соседний токен: `$dashboard:a.min–$dashboard:a.max` читался бы как датасорс `a.min–$dashboard`.
|
|
48
|
+
*/
|
|
49
|
+
const CONFIG_SOURCE_NAME = "[\\w.-]+";
|
|
50
|
+
/**
|
|
51
|
+
* Все подстановки строки конфига одной грамматикой, по группам:
|
|
52
|
+
* 1–2 `$dashboard:<датасорс>:<поле>`, 3–4 `$dashboard:<фильтр>[.свойство]`, 5–6 `$card:<слой>:<поле>`,
|
|
53
|
+
* 7–8 `%<фильтр>[.свойство]`, 9 `{<атрибут>}`. Форма датасорса проверяется раньше формы фильтра:
|
|
54
|
+
* их различает второе двоеточие. Имя фильтра — `\w+` целиком, поэтому `%name` не цепляет начало
|
|
55
|
+
* `%name2`, а точка в конце предложения («за %year.») не считается свойством.
|
|
56
|
+
*/
|
|
57
|
+
const CONFIG_TOKEN_REGEXP = new RegExp([
|
|
58
|
+
`\\${PROVIDER_PREFIX}${ProviderPrefix.Dashboard}:(${CONFIG_SOURCE_NAME}):(\\w+)`,
|
|
59
|
+
`\\${PROVIDER_PREFIX}${ProviderPrefix.Dashboard}:(\\w+)(?:\\.(\\w+))?`,
|
|
60
|
+
`\\${PROVIDER_PREFIX}${ProviderPrefix.Card}:(${CONFIG_SOURCE_NAME}):(\\w+)`,
|
|
61
|
+
`${FILTER_PREFIX}(\\w+)(?:\\.(\\w+))?`,
|
|
62
|
+
"\\{([^{}\\s]+)\\}",
|
|
63
|
+
].join("|"), "g");
|
|
64
|
+
|
|
65
|
+
const toToken = ([raw, ...groups]) => {
|
|
66
|
+
const [dataSourceName, dataSourceField, dashboardFilter, dashboardProp, cardLayer, cardField, filterName, filterProp, attributeName, resultPath, eventPath, parametersPath,] = groups;
|
|
67
|
+
if (resultPath || raw.startsWith("$event") || raw.startsWith("$params")) {
|
|
68
|
+
return { kind: "action", scope: resultPath ? "result" : raw.startsWith("$event") ? "event" : "parameters", path: resultPath || eventPath || parametersPath || "", raw };
|
|
69
|
+
}
|
|
70
|
+
if (dataSourceName) {
|
|
71
|
+
return { kind: "dataSource", dataSourceName, fieldName: dataSourceField, raw };
|
|
72
|
+
}
|
|
73
|
+
if (dashboardFilter) {
|
|
74
|
+
return { kind: "filter", scope: "dashboard", name: dashboardFilter, prop: dashboardProp, raw };
|
|
75
|
+
}
|
|
76
|
+
if (cardLayer) {
|
|
77
|
+
return { kind: "card", layerName: cardLayer, fieldName: cardField, raw };
|
|
78
|
+
}
|
|
79
|
+
if (filterName) {
|
|
80
|
+
return { kind: "filter", scope: "widget", name: filterName, prop: filterProp, raw };
|
|
81
|
+
}
|
|
82
|
+
return { kind: "attribute", name: attributeName, raw };
|
|
83
|
+
};
|
|
84
|
+
/** Разбирает строку конфига на литералы и токены подстановок — в исходном порядке. */
|
|
85
|
+
const tokenizeConfigString = (value) => {
|
|
86
|
+
const segments = [];
|
|
87
|
+
let lastIndex = 0;
|
|
88
|
+
for (const match of value.matchAll(new RegExp(`${CONFIG_TOKEN_REGEXP.source}|#([\\w]+(?:\\.[\\w]+)*)|\\$event(?:\\.([\\w]+(?:\\.[\\w]+)*))?\\b|\\$params(?:\\.([\\w]+(?:\\.[\\w]+)*))?\\b`, "g"))) {
|
|
89
|
+
const token = toToken(match);
|
|
90
|
+
const escaped = match.index > 0 && value[match.index - 1] === "\\";
|
|
91
|
+
if (escaped) {
|
|
92
|
+
segments.push(value.slice(lastIndex, match.index - 1), match[0]);
|
|
93
|
+
lastIndex = match.index + match[0].length;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (token.kind === "action" && match.index > 0 && /[\w/#?=.-]/.test(value[match.index - 1]))
|
|
97
|
+
continue;
|
|
98
|
+
if (match.index > lastIndex) {
|
|
99
|
+
segments.push(value.slice(lastIndex, match.index));
|
|
100
|
+
}
|
|
101
|
+
segments.push(token);
|
|
102
|
+
lastIndex = match.index + match[0].length;
|
|
103
|
+
}
|
|
104
|
+
if (lastIndex < value.length) {
|
|
105
|
+
segments.push(value.slice(lastIndex));
|
|
106
|
+
}
|
|
107
|
+
return segments;
|
|
108
|
+
};
|
|
109
|
+
/** Строка целиком — один токен: тогда значение уходит как есть, с типом (число, массив). */
|
|
110
|
+
const getSingleToken = (segments) => segments.length === 1 && typeof segments[0] !== "string" ? segments[0] : null;
|
|
111
|
+
|
|
112
|
+
// Строковые литералы читаются отдельно: префиксы подстановок внутри кавычек остаются обычным текстом.
|
|
113
|
+
const readString = (input) => {
|
|
114
|
+
const quote = input[0];
|
|
115
|
+
let value = "";
|
|
116
|
+
let index = 1;
|
|
117
|
+
while (index < input.length) {
|
|
118
|
+
const character = input[index++];
|
|
119
|
+
if (character === quote)
|
|
120
|
+
return { kind: "literal", text: input.slice(0, index), value };
|
|
121
|
+
if (character !== "\\") {
|
|
122
|
+
value += character;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const escaped = input[index++];
|
|
126
|
+
const escapes = { n: "\n", r: "\r", t: "\t", "\\": "\\", "'": "'", "\"": "\"" };
|
|
127
|
+
if (!(escaped in escapes))
|
|
128
|
+
throw new Error("Unsupported string escape");
|
|
129
|
+
value += escapes[escaped];
|
|
130
|
+
}
|
|
131
|
+
throw new Error("Unterminated string");
|
|
132
|
+
};
|
|
133
|
+
const readToken = (input, expectsOperand) => {
|
|
134
|
+
if (input[0] === "'" || input[0] === "\"")
|
|
135
|
+
return readString(input);
|
|
136
|
+
// % в начале операнда — фильтр, после значения — остаток от деления.
|
|
137
|
+
if (input[0] === "%" && !expectsOperand)
|
|
138
|
+
return { kind: "operator", text: "%" };
|
|
139
|
+
const match = input.match(CONDITION_TOKEN);
|
|
140
|
+
if (!match) {
|
|
141
|
+
const property = input.match(/^[A-Za-z_][\w]*/);
|
|
142
|
+
if (property)
|
|
143
|
+
return { kind: "operator", text: property[0] };
|
|
144
|
+
throw new Error(`Unexpected token: ${input[0]}`);
|
|
145
|
+
}
|
|
146
|
+
const [text, number, reference, operator, keyword] = match;
|
|
147
|
+
if (number)
|
|
148
|
+
return { kind: "literal", text, value: Number(number) };
|
|
149
|
+
if (reference) {
|
|
150
|
+
// Формат ссылки разбирает общий механизм конфигов; здесь определяется только её граница в выражении.
|
|
151
|
+
const token = getSingleToken(tokenizeConfigString(text));
|
|
152
|
+
if (!token)
|
|
153
|
+
throw new Error(`Invalid reference: ${text}`);
|
|
154
|
+
return { kind: "reference", text, reference: token };
|
|
155
|
+
}
|
|
156
|
+
if (keyword === "null")
|
|
157
|
+
return { kind: "literal", text, value: null };
|
|
158
|
+
if (keyword === "true" || keyword === "false")
|
|
159
|
+
return { kind: "literal", text, value: keyword === "true" };
|
|
160
|
+
return { kind: "operator", text: operator ?? keyword };
|
|
161
|
+
};
|
|
162
|
+
const tokenizeActionCondition = (expression) => {
|
|
163
|
+
const tokens = [];
|
|
164
|
+
let remaining = expression.trim();
|
|
165
|
+
let expectsOperand = true;
|
|
166
|
+
while (remaining) {
|
|
167
|
+
const token = readToken(remaining, expectsOperand);
|
|
168
|
+
tokens.push(token);
|
|
169
|
+
remaining = remaining.slice(token.text.length).trimStart();
|
|
170
|
+
expectsOperand = token.kind === "operator" && ![")", "]"].includes(token.text);
|
|
171
|
+
}
|
|
172
|
+
return tokens;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Разбирает ограниченное выражение: приоритеты операторов, скобки, свойства, includes и Date.parse.
|
|
177
|
+
* Условия источников данных лишь форматируются для сервера; callback требует вычисления в браузере.
|
|
178
|
+
*/
|
|
179
|
+
const parseActionCondition = (condition) => {
|
|
180
|
+
const tokens = tokenizeActionCondition(condition);
|
|
181
|
+
let position = 0;
|
|
182
|
+
const peekToken = () => tokens[position]?.text;
|
|
183
|
+
function expectToken(text) {
|
|
184
|
+
if (peekToken() !== text)
|
|
185
|
+
throw new Error(`Expected ${text}`);
|
|
186
|
+
position++;
|
|
187
|
+
}
|
|
188
|
+
// Литерал или начало выражения: ссылка, унарный оператор, скобки, массив, разрешённый вызов.
|
|
189
|
+
function parsePrimary() {
|
|
190
|
+
const token = tokens[position++];
|
|
191
|
+
if (!token)
|
|
192
|
+
throw new Error("Expected expression");
|
|
193
|
+
if (token.kind === "literal")
|
|
194
|
+
return { kind: "literal", value: token.value };
|
|
195
|
+
if (token.kind === "reference")
|
|
196
|
+
return { kind: "reference", token: token.reference };
|
|
197
|
+
if (["!", "+", "-"].includes(token.text))
|
|
198
|
+
return { kind: "unary", operator: token.text, operand: parseExpression(UNARY_PRECEDENCE) };
|
|
199
|
+
if (token.text === "(") {
|
|
200
|
+
const node = parseExpression(0);
|
|
201
|
+
expectToken(")");
|
|
202
|
+
return node;
|
|
203
|
+
}
|
|
204
|
+
if (token.text === "[")
|
|
205
|
+
return parseArray();
|
|
206
|
+
if (token.text === "Date.parse") {
|
|
207
|
+
expectToken("(");
|
|
208
|
+
const argument = parseExpression(0);
|
|
209
|
+
expectToken(")");
|
|
210
|
+
return { kind: "date", argument };
|
|
211
|
+
}
|
|
212
|
+
throw new Error(`Unsupported identifier: ${token.text}`);
|
|
213
|
+
}
|
|
214
|
+
function parseArray() {
|
|
215
|
+
const items = [];
|
|
216
|
+
while (peekToken() !== "]") {
|
|
217
|
+
items.push(parseExpression(0));
|
|
218
|
+
if (peekToken() !== ",")
|
|
219
|
+
break;
|
|
220
|
+
position++;
|
|
221
|
+
}
|
|
222
|
+
expectToken("]");
|
|
223
|
+
return { kind: "array", items };
|
|
224
|
+
}
|
|
225
|
+
// После значения разрешены лишь чтение поля/индекса и includes; произвольные методы не вызываются.
|
|
226
|
+
function parsePostfix(initial) {
|
|
227
|
+
let node = initial;
|
|
228
|
+
while (peekToken() === "." || peekToken() === "[") {
|
|
229
|
+
const operator = tokens[position++].text;
|
|
230
|
+
if (operator === "[") {
|
|
231
|
+
node = { kind: "property", target: node, key: parseExpression(0) };
|
|
232
|
+
expectToken("]");
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const name = tokens[position++]?.text;
|
|
236
|
+
if (!name || !/^[A-Za-z_]\w*$/.test(name))
|
|
237
|
+
throw new Error("Invalid property");
|
|
238
|
+
if (name === "includes" && peekToken() === "(") {
|
|
239
|
+
position++;
|
|
240
|
+
node = { kind: "includes", target: node, argument: parseExpression(0) };
|
|
241
|
+
expectToken(")");
|
|
242
|
+
}
|
|
243
|
+
else
|
|
244
|
+
node = { kind: "property", target: node, key: { kind: "literal", value: name } };
|
|
245
|
+
}
|
|
246
|
+
return node;
|
|
247
|
+
}
|
|
248
|
+
// Правый операнд читает операторы с большим приоритетом; одинаковый приоритет вычисляется слева направо.
|
|
249
|
+
function parseExpression(minimum) {
|
|
250
|
+
let left = parsePostfix(parsePrimary());
|
|
251
|
+
while ((BINARY_PRECEDENCE[peekToken()] ?? -1) >= minimum) {
|
|
252
|
+
const operator = tokens[position++].text;
|
|
253
|
+
const right = parseExpression(BINARY_PRECEDENCE[operator] + 1);
|
|
254
|
+
left = { kind: "binary", operator, left, right };
|
|
255
|
+
}
|
|
256
|
+
return left;
|
|
257
|
+
}
|
|
258
|
+
const result = parseExpression(0);
|
|
259
|
+
if (position !== tokens.length)
|
|
260
|
+
throw new Error("Unexpected trailing tokens");
|
|
261
|
+
return result;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const ACTION_TYPES = ["runTask", "setFilters", "openUrl", "openModal"];
|
|
265
|
+
const UNSAFE_ACTION_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
266
|
+
|
|
267
|
+
const mergeActionParameters = (base, override) => {
|
|
268
|
+
if (!isObject(override)) {
|
|
269
|
+
if (Array.isArray(override))
|
|
270
|
+
return override.map(value => mergeActionParameters(undefined, value));
|
|
271
|
+
return override;
|
|
272
|
+
}
|
|
273
|
+
const result = {};
|
|
274
|
+
const original = isObject(base) ? base : {};
|
|
275
|
+
for (const key of new Set([...Object.keys(original), ...Object.keys(override)])) {
|
|
276
|
+
if (UNSAFE_ACTION_KEYS.has(key))
|
|
277
|
+
continue;
|
|
278
|
+
result[key] = Object.prototype.hasOwnProperty.call(override, key)
|
|
279
|
+
? mergeActionParameters(original[key], override[key])
|
|
280
|
+
: mergeActionParameters(undefined, original[key]);
|
|
281
|
+
}
|
|
282
|
+
return result;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const resolveActionInvocation = (invocation, scopes) => {
|
|
286
|
+
if (invocation.type)
|
|
287
|
+
return { ...invocation, event: invocation.event ?? "click" };
|
|
288
|
+
const definition = [...scopes]
|
|
289
|
+
.reverse()
|
|
290
|
+
.flatMap(scope => scope)
|
|
291
|
+
.find(({ id }) => id === invocation.id);
|
|
292
|
+
if (!definition)
|
|
293
|
+
throw new Error(`Unknown action: ${invocation.id}`);
|
|
294
|
+
return {
|
|
295
|
+
...definition,
|
|
296
|
+
options: mergeActionParameters(definition.options, invocation.options ?? {}),
|
|
297
|
+
event: invocation.event ?? definition.event ?? "click",
|
|
298
|
+
callback: Object.prototype.hasOwnProperty.call(invocation, "callback") ? invocation.callback : definition.callback,
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const isFilterScalar = (value) => value === null ||
|
|
303
|
+
typeof value === "string" ||
|
|
304
|
+
typeof value === "boolean" ||
|
|
305
|
+
(typeof value === "number" && Number.isFinite(value)) ||
|
|
306
|
+
(value instanceof Date && Number.isFinite(value.getTime()));
|
|
307
|
+
const isTreeValue = (value) => Object.entries(value).every(([level, ids]) => /^l\d+$/.test(level) && Array.isArray(ids) && ids.every(id => typeof id === "string" || typeof id === "number"));
|
|
308
|
+
const isFeaturesValue = (value) => value.type === "FeatureCollection" &&
|
|
309
|
+
Array.isArray(value.features) &&
|
|
310
|
+
value.features.every(feature => isObject(feature) &&
|
|
311
|
+
feature.type === "Feature" &&
|
|
312
|
+
feature.geometry === null &&
|
|
313
|
+
isObject(feature.properties));
|
|
314
|
+
const isFilterValue = (value) => {
|
|
315
|
+
if (isFilterScalar(value))
|
|
316
|
+
return true;
|
|
317
|
+
if (Array.isArray(value))
|
|
318
|
+
return value.every(isFilterScalar);
|
|
319
|
+
return isObject(value) && (isTreeValue(value) || isFeaturesValue(value));
|
|
320
|
+
};
|
|
321
|
+
const isActionSelectedFilter = (filter) => {
|
|
322
|
+
if (!isObject(filter) || !["value", "min", "max"].some(key => key in filter))
|
|
323
|
+
return false;
|
|
324
|
+
if ("value" in filter && !isFilterValue(filter.value))
|
|
325
|
+
return false;
|
|
326
|
+
if ("min" in filter && !isFilterScalar(filter.min))
|
|
327
|
+
return false;
|
|
328
|
+
if ("max" in filter && !isFilterScalar(filter.max))
|
|
329
|
+
return false;
|
|
330
|
+
return true;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const getActionShapeError = (action, definition) => {
|
|
334
|
+
if (definition && (typeof action.id !== "string" || !action.id))
|
|
335
|
+
return "Definition requires an id";
|
|
336
|
+
if (!definition && action.id !== undefined && action.type !== undefined)
|
|
337
|
+
return "Invocation must be either a reference or inline action";
|
|
338
|
+
if (Object.prototype.hasOwnProperty.call(action, "parameters"))
|
|
339
|
+
return "Action parameters have been renamed to options";
|
|
340
|
+
if (action.event !== undefined && action.event !== "click")
|
|
341
|
+
return "Unknown action event";
|
|
342
|
+
if (action.type === undefined && !definition) {
|
|
343
|
+
if (typeof action.id !== "string" || !action.id)
|
|
344
|
+
return "Invocation requires id or type";
|
|
345
|
+
if (action.options !== undefined && !isObject(action.options))
|
|
346
|
+
return "Options must be an object";
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
if (!ACTION_TYPES.includes(action.type))
|
|
350
|
+
return "Unknown action type";
|
|
351
|
+
return getActionOptionsError(action.type, action.options);
|
|
352
|
+
};
|
|
353
|
+
function getActionOptionsError(type, value) {
|
|
354
|
+
if (!isObject(value))
|
|
355
|
+
return "Options must be an object";
|
|
356
|
+
if (type === "runTask" && (typeof value.taskName !== "string" || !value.taskName))
|
|
357
|
+
return "runTask requires taskName";
|
|
358
|
+
if (type === "openModal" && (typeof value.modalId !== "string" || !value.modalId))
|
|
359
|
+
return "openModal requires modalId";
|
|
360
|
+
if (type === "openUrl" && (typeof value.url !== "string" || !value.url))
|
|
361
|
+
return "openUrl requires url";
|
|
362
|
+
if (type === "openUrl" && value.target !== undefined && !["_self", "_blank"].includes(String(value.target)))
|
|
363
|
+
return "Invalid URL target";
|
|
364
|
+
if (["runTask", "openModal"].includes(type) && value.parameters !== undefined && !isObject(value.parameters))
|
|
365
|
+
return "Nested parameters must be an object";
|
|
366
|
+
if (type === "setFilters") {
|
|
367
|
+
if (!isObject(value.filters))
|
|
368
|
+
return "setFilters requires filters object";
|
|
369
|
+
if (Object.values(value.filters).some(filter => !isActionSelectedFilter(filter)))
|
|
370
|
+
return "Invalid selected filter shape";
|
|
371
|
+
}
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const addActionIssue = (state, frame, message, code = "invalidAction", severity = "error") => {
|
|
376
|
+
state.issues.push({ path: frame.path, ownerId: frame.ownerId, message, code, severity });
|
|
377
|
+
};
|
|
378
|
+
const validateResources = (action, frame, state) => {
|
|
379
|
+
if (!frame.declaration &&
|
|
380
|
+
action.type === "runTask" &&
|
|
381
|
+
frame.tasks &&
|
|
382
|
+
!frame.tasks.some(task => isObject(task) && task.name === action.options.taskName)) {
|
|
383
|
+
addActionIssue(state, frame, `Unknown task: ${action.options.taskName}`, "unknownTask");
|
|
384
|
+
}
|
|
385
|
+
if (action.type === "openModal" && !state.modalIds.has(action.options.modalId)) {
|
|
386
|
+
addActionIssue(state, frame, `Unknown modal: ${action.options.modalId}`, "unknownModal");
|
|
387
|
+
}
|
|
388
|
+
if (action.type === "openUrl" && action.options.target === "_self" && action.callback) {
|
|
389
|
+
addActionIssue(state, frame, "Navigation to _self may unload the callback", "navigationLifecycle", "warning");
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
const enqueueCallback = (callback, frame, pending, state) => {
|
|
393
|
+
const callbackFrame = { ...frame, path: `${frame.path}.callback`, definition: false };
|
|
394
|
+
if (!isObject(callback) || !Object.keys(callback).length) {
|
|
395
|
+
addActionIssue(state, callbackFrame, "Callback requires an invocation or condition map");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if ("id" in callback || "type" in callback) {
|
|
399
|
+
pending.push({ ...callbackFrame, node: callback });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
for (const [condition, invocation] of Object.entries(callback)) {
|
|
403
|
+
const branch = { ...callbackFrame, path: `${callbackFrame.path}[${JSON.stringify(condition)}]` };
|
|
404
|
+
try {
|
|
405
|
+
if (condition !== "else")
|
|
406
|
+
parseActionCondition(condition);
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
addActionIssue(state, branch, "Invalid condition syntax", "invalidCondition");
|
|
410
|
+
}
|
|
411
|
+
if (isObject(invocation))
|
|
412
|
+
pending.push({ ...branch, node: invocation });
|
|
413
|
+
else
|
|
414
|
+
addActionIssue(state, branch, "Branch requires an invocation");
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
const validateActionTree = (initial, state) => {
|
|
418
|
+
const pending = [initial];
|
|
419
|
+
const visited = new Set();
|
|
420
|
+
while (pending.length) {
|
|
421
|
+
const frame = pending.pop();
|
|
422
|
+
if (!frame)
|
|
423
|
+
break;
|
|
424
|
+
const problem = getActionShapeError(frame.node, frame.definition);
|
|
425
|
+
if (problem) {
|
|
426
|
+
addActionIssue(state, frame, problem);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (frame.declaration && !frame.node.type) {
|
|
430
|
+
if (!state.actionIds.has(String(frame.node.id)))
|
|
431
|
+
addActionIssue(state, frame, `Unknown action: ${String(frame.node.id)}`, "unknownAction");
|
|
432
|
+
if (frame.node.callback !== undefined)
|
|
433
|
+
enqueueCallback(frame.node.callback, frame, pending, state);
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
let action;
|
|
437
|
+
try {
|
|
438
|
+
action = frame.definition
|
|
439
|
+
? { ...frame.node, event: frame.node.event ?? "click" }
|
|
440
|
+
: resolveActionInvocation(frame.node, frame.scopes);
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
addActionIssue(state, frame, `Unknown action: ${String(frame.node.id)}`, "unknownAction");
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
const optionsError = getActionOptionsError(action.type, action.options);
|
|
447
|
+
if (optionsError)
|
|
448
|
+
addActionIssue(state, frame, optionsError);
|
|
449
|
+
validateResources(action, frame, state);
|
|
450
|
+
const identity = frame.node.id
|
|
451
|
+
? frame.scopes
|
|
452
|
+
.flatMap(scope => scope)
|
|
453
|
+
.reverse()
|
|
454
|
+
.find(definition => definition.id === frame.node.id)
|
|
455
|
+
: frame.node;
|
|
456
|
+
if (visited.has(identity))
|
|
457
|
+
continue;
|
|
458
|
+
visited.add(identity);
|
|
459
|
+
if (action.callback !== undefined)
|
|
460
|
+
enqueueCallback(action.callback, frame, pending, state);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
const validateRegistry = (value, frame, state) => {
|
|
464
|
+
if (value === undefined)
|
|
465
|
+
return [];
|
|
466
|
+
if (!Array.isArray(value)) {
|
|
467
|
+
addActionIssue(state, frame, "Actions must be an array");
|
|
468
|
+
return [];
|
|
469
|
+
}
|
|
470
|
+
const definitions = [];
|
|
471
|
+
const ids = new Set();
|
|
472
|
+
value.forEach((node, index) => {
|
|
473
|
+
const child = { ...frame, path: `${frame.path}[${index}]` };
|
|
474
|
+
if (!isObject(node)) {
|
|
475
|
+
addActionIssue(state, child, "Definition must be an object");
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const problem = getActionShapeError(node, true);
|
|
479
|
+
if (problem) {
|
|
480
|
+
addActionIssue(state, child, problem);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const definition = node;
|
|
484
|
+
if (ids.has(definition.id))
|
|
485
|
+
addActionIssue(state, child, "Duplicate action id in scope", "duplicateAction");
|
|
486
|
+
ids.add(definition.id);
|
|
487
|
+
definitions.push(definition);
|
|
488
|
+
});
|
|
489
|
+
return definitions;
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
const withPath = (path, key) => (path ? `${path}.${key}` : key);
|
|
493
|
+
const collectIds = (config, key) => {
|
|
494
|
+
const ids = new Set();
|
|
495
|
+
const pending = [config];
|
|
496
|
+
while (pending.length) {
|
|
497
|
+
const node = pending.pop();
|
|
498
|
+
if (!isObject(node))
|
|
499
|
+
continue;
|
|
500
|
+
if (Array.isArray(node[key])) {
|
|
501
|
+
for (const modal of node[key]) {
|
|
502
|
+
if (isObject(modal) && typeof modal.id === "string")
|
|
503
|
+
ids.add(modal.id);
|
|
504
|
+
}
|
|
505
|
+
pending.push(...node[key]);
|
|
506
|
+
}
|
|
507
|
+
if (Array.isArray(node.children))
|
|
508
|
+
pending.push(...node.children);
|
|
509
|
+
}
|
|
510
|
+
return ids;
|
|
511
|
+
};
|
|
512
|
+
const validateBindings = (frame, state) => {
|
|
513
|
+
const { actions } = frame.node;
|
|
514
|
+
if (actions === undefined)
|
|
515
|
+
return;
|
|
516
|
+
if (!Array.isArray(actions)) {
|
|
517
|
+
addActionIssue(state, { ...frame, path: withPath(frame.path, "actions") }, "Actions must be an array");
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
actions.forEach((node, index) => {
|
|
521
|
+
const child = { ...frame, path: `${withPath(frame.path, "actions")}[${index}]` };
|
|
522
|
+
if (isObject(node))
|
|
523
|
+
validateActionTree({ ...child, node }, state);
|
|
524
|
+
else
|
|
525
|
+
addActionIssue(state, child, "Action must be an object");
|
|
526
|
+
});
|
|
527
|
+
};
|
|
528
|
+
const enqueueChildren = (frame, pending) => {
|
|
529
|
+
for (const key of ["children", "modals"]) {
|
|
530
|
+
const children = frame.node[key];
|
|
531
|
+
if (!Array.isArray(children))
|
|
532
|
+
continue;
|
|
533
|
+
children.forEach((node, index) => {
|
|
534
|
+
if (!isObject(node))
|
|
535
|
+
return;
|
|
536
|
+
pending.push({
|
|
537
|
+
...frame,
|
|
538
|
+
node,
|
|
539
|
+
path: `${withPath(frame.path, key)}[${index}]`,
|
|
540
|
+
definition: key === "modals" || frame.node.templateName === "Pages",
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
if (isObject(frame.node.header))
|
|
545
|
+
pending.push({ ...frame, node: frame.node.header, path: withPath(frame.path, "header"), definition: false });
|
|
546
|
+
};
|
|
547
|
+
/** Проверка JSON использует общий механизм разрешения действий и разбора условий. Циклы допустимы. */
|
|
548
|
+
const validateDashboardActions = (config) => {
|
|
549
|
+
if (!isObject(config))
|
|
550
|
+
return [];
|
|
551
|
+
const state = {
|
|
552
|
+
issues: [],
|
|
553
|
+
modalIds: collectIds(config, "modals"),
|
|
554
|
+
actionIds: collectIds(config, "actions"),
|
|
555
|
+
};
|
|
556
|
+
const pending = [{ node: config, path: "", scopes: [], definition: true }];
|
|
557
|
+
while (pending.length) {
|
|
558
|
+
const frame = pending.pop();
|
|
559
|
+
if (!frame)
|
|
560
|
+
break;
|
|
561
|
+
frame.ownerId = typeof frame.node.id === "string" ? frame.node.id : frame.ownerId;
|
|
562
|
+
frame.tasks = Array.isArray(frame.node.tasks) ? frame.node.tasks : frame.tasks;
|
|
563
|
+
if (frame.definition) {
|
|
564
|
+
const definitions = validateRegistry(frame.node.actions, { ...frame, path: withPath(frame.path, "actions") }, state);
|
|
565
|
+
frame.scopes = [...frame.scopes, definitions];
|
|
566
|
+
definitions.forEach((node, index) => validateActionTree({ ...frame, node, declaration: true, path: `${withPath(frame.path, "actions")}[${index}]` }, state));
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
validateBindings(frame, state);
|
|
570
|
+
}
|
|
571
|
+
enqueueChildren(frame, pending);
|
|
572
|
+
}
|
|
573
|
+
return state.issues;
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
exports.validateDashboardActions = validateDashboardActions;
|
|
577
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs","main":"index.js","types":"../dashboardActions.d.ts"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Отдельная точка входа для проверки JSON и серверных инструментов работы с конфигурацией. */
|
|
2
|
+
export { validateDashboardActions } from './components/Dashboard/actions/validation/validateDashboardActions';
|
|
3
|
+
export type { ConfigActionDefinition, ConfigActionInvocation, ConfigActionCallback, ActionValidationIssue } from './components/Dashboard/actions/types';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { RemoteTaskStatus } from '@evergis/api';
|
|
2
|
+
export declare class PythonExecutionError extends Error {
|
|
3
|
+
readonly runId: string;
|
|
4
|
+
readonly status: RemoteTaskStatus;
|
|
5
|
+
readonly taskId: string | null;
|
|
6
|
+
readonly cancelled: boolean;
|
|
7
|
+
notified: boolean;
|
|
8
|
+
constructor(message: string, runId: string, status: RemoteTaskStatus, taskId: string | null, cancelled?: boolean);
|
|
9
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { PythonExecutionDependencies, PythonExecutionRequest, PythonTaskDefinition } from './types';
|
|
2
|
+
export declare const resolvePythonTaskDefinition: (request: PythonExecutionRequest, manager: Pick<PythonExecutionDependencies["manager"], "getTaskResource">) => Promise<PythonTaskDefinition>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Api, RemoteTaskStatus } from '@evergis/api';
|
|
2
|
+
import { ConfigTask } from '../../../components/Dashboard/types';
|
|
3
|
+
import { PythonTaskProgressNotification, PythonTaskProgressSubscription } from '../types';
|
|
4
|
+
export interface PythonExecutionRequest {
|
|
5
|
+
taskName?: string;
|
|
6
|
+
tasks?: ConfigTask[];
|
|
7
|
+
resourceId?: string;
|
|
8
|
+
parameters?: Record<string, unknown>;
|
|
9
|
+
script?: string;
|
|
10
|
+
fileName?: string;
|
|
11
|
+
methodName?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
useNotifications?: boolean;
|
|
14
|
+
statusColors?: Record<RemoteTaskStatus, string>;
|
|
15
|
+
}
|
|
16
|
+
export interface PythonTaskDefinition {
|
|
17
|
+
resourceId: string;
|
|
18
|
+
parameters: Record<string, unknown>;
|
|
19
|
+
script?: string;
|
|
20
|
+
fileName?: string;
|
|
21
|
+
methodName?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface PythonExecutionSnapshot {
|
|
24
|
+
taskId: string | null;
|
|
25
|
+
status: RemoteTaskStatus;
|
|
26
|
+
log: string | null;
|
|
27
|
+
lastMessage: string | null;
|
|
28
|
+
error: Error | null;
|
|
29
|
+
loading: boolean;
|
|
30
|
+
executionTime: number;
|
|
31
|
+
result: Record<string, unknown> | null;
|
|
32
|
+
}
|
|
33
|
+
export interface PythonExecutionHandle {
|
|
34
|
+
runId: string;
|
|
35
|
+
completion: Promise<Record<string, unknown>>;
|
|
36
|
+
getSnapshot: () => PythonExecutionSnapshot;
|
|
37
|
+
subscribe: (listener: () => void) => () => void;
|
|
38
|
+
stop: () => Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
export interface PythonExecutionDependencies {
|
|
41
|
+
manager: Pick<Api["remoteTaskManager"], "createTaskPrototype" | "startTask1" | "get" | "stop" | "getTaskResource">;
|
|
42
|
+
prepare: (resource: {
|
|
43
|
+
resourceId: string;
|
|
44
|
+
}) => Promise<unknown>;
|
|
45
|
+
subscribe?: (payload: PythonTaskProgressSubscription) => Promise<string | null>;
|
|
46
|
+
unsubscribe?: (id: string) => void;
|
|
47
|
+
connection?: {
|
|
48
|
+
on: (event: string, callback: (notification: PythonTaskProgressNotification) => void) => void;
|
|
49
|
+
off: (event: string, callback: (notification: PythonTaskProgressNotification) => void) => void;
|
|
50
|
+
};
|
|
51
|
+
intervalMs?: number;
|
|
52
|
+
timeoutMs?: number;
|
|
53
|
+
}
|