@revenexx/studio-integrations 3.6.2 → 3.7.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.
- package/CHANGELOG.md +12 -0
- package/dist/module.json +1 -1
- package/dist/runtime/composables/integrationsApi.d.ts +2 -0
- package/dist/runtime/composables/integrationsApi.js +2 -0
- package/dist/runtime/composables/useNodeConfigResolve.js +25 -0
- package/dist/runtime/data/resolveAnswerStore.d.ts +31 -0
- package/dist/runtime/data/resolveAnswerStore.js +24 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @revenexx/studio-integrations
|
|
2
2
|
|
|
3
|
+
## 3.7.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 16300f0: A setting the platform refused after it had answered is asked again when its dialog is opened again, instead of showing the earlier answer (IN-82).
|
|
8
|
+
|
|
9
|
+
## 3.7.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- f447c9c: A node's settings dialog that is closed and opened again shows what the platform last answered for its dynamic settings at once, instead of asking again and waiting (IN-82). The answers are kept for the tab, per tenant, node, version and the settings they depend on: one younger than 30 seconds is not asked for again, an older one is shown while it is asked for again behind it, and a refusal is never kept.
|
|
14
|
+
|
|
3
15
|
## 3.6.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/module.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare function useIntegrationsApi(): {
|
|
2
2
|
baseUrl: import("vue").ComputedRef<string>;
|
|
3
|
+
/** The active tenant's slug, empty until it is known. */
|
|
4
|
+
tenant: import("vue").ComputedRef<any>;
|
|
3
5
|
get: <T>(path: string, opts?: {
|
|
4
6
|
signal?: AbortSignal;
|
|
5
7
|
}) => Promise<T>;
|
|
@@ -42,6 +42,8 @@ export function useIntegrationsApi() {
|
|
|
42
42
|
}
|
|
43
43
|
return {
|
|
44
44
|
baseUrl,
|
|
45
|
+
/** The active tenant's slug, empty until it is known. */
|
|
46
|
+
tenant: computed(() => onlyTenantSlug.value ?? ""),
|
|
45
47
|
get: (path, opts) => request("GET", path, void 0, opts),
|
|
46
48
|
post: (path, body, opts) => request("POST", path, body ?? {}, opts),
|
|
47
49
|
put: (path, body) => request("PUT", path, body ?? {}),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { computed, onScopeDispose, ref, toValue, watch } from "vue";
|
|
2
2
|
import { isVersionExecutable } from "../data/integrationNodeCatalog.js";
|
|
3
3
|
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
4
|
+
import { forgetAnswer, keepAnswer, keptAnswer } from "../data/resolveAnswerStore.js";
|
|
4
5
|
import { IntegrationsApiError, errorReason } from "../utils/apiError.js";
|
|
5
6
|
export function isResolvableValue(v) {
|
|
6
7
|
if (v === void 0 || v === null) return false;
|
|
@@ -79,6 +80,11 @@ export function useNodeConfigResolve(opts) {
|
|
|
79
80
|
let controller = null;
|
|
80
81
|
let lastFetchedKey = null;
|
|
81
82
|
let lastFetchedAt = 0;
|
|
83
|
+
function storeKey(key) {
|
|
84
|
+
const tenant = toValue(api.tenant);
|
|
85
|
+
if (!tenant) return void 0;
|
|
86
|
+
return JSON.stringify([tenant, toValue(opts.slug), toValue(opts.version), toValue(opts.target), key]);
|
|
87
|
+
}
|
|
82
88
|
async function run(key) {
|
|
83
89
|
if (controller) controller.abort();
|
|
84
90
|
controller = new AbortController();
|
|
@@ -99,9 +105,13 @@ export function useNodeConfigResolve(opts) {
|
|
|
99
105
|
error.value = null;
|
|
100
106
|
lastFetchedKey = key;
|
|
101
107
|
lastFetchedAt = Date.now();
|
|
108
|
+
const kept = storeKey(key);
|
|
109
|
+
if (kept) keepAnswer(kept, result, lastFetchedAt);
|
|
102
110
|
} catch (err) {
|
|
103
111
|
if (isAbortError(err) || signal.aborted) return;
|
|
104
112
|
error.value = classifyError(err);
|
|
113
|
+
const kept = storeKey(key);
|
|
114
|
+
if (kept) forgetAnswer(kept);
|
|
105
115
|
} finally {
|
|
106
116
|
if (!signal.aborted) isFetching.value = false;
|
|
107
117
|
}
|
|
@@ -112,8 +122,23 @@ export function useNodeConfigResolve(opts) {
|
|
|
112
122
|
if (!force && lastFetchedKey === key && Date.now() - lastFetchedAt < STALE_MS && !error.value) {
|
|
113
123
|
return;
|
|
114
124
|
}
|
|
125
|
+
const kept = !force && lastFetchedKey !== key ? keptStored(key) : void 0;
|
|
126
|
+
if (kept) {
|
|
127
|
+
data.value = kept.result;
|
|
128
|
+
hasData.value = true;
|
|
129
|
+
error.value = null;
|
|
130
|
+
if (kept.age < STALE_MS) {
|
|
131
|
+
lastFetchedKey = key;
|
|
132
|
+
lastFetchedAt = Date.now() - kept.age;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
115
136
|
void run(key);
|
|
116
137
|
}
|
|
138
|
+
function keptStored(key) {
|
|
139
|
+
const stored = storeKey(key);
|
|
140
|
+
return stored ? keptAnswer(stored) : void 0;
|
|
141
|
+
}
|
|
117
142
|
watch([debouncedKey, isEnabled], () => maybeFetch(false), { immediate: true });
|
|
118
143
|
function refetch() {
|
|
119
144
|
maybeFetch(true);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The answers of `config:resolve` kept for the tab (IN-82), so a node's settings
|
|
3
|
+
* dialog that is closed and opened again shows what the platform last said
|
|
4
|
+
* instead of asking it again and waiting.
|
|
5
|
+
*
|
|
6
|
+
* A resolve can be expensive on the platform side — a SOAP Request reads and
|
|
7
|
+
* builds its whole WSDL for each one — and the dialog's own memory goes with the
|
|
8
|
+
* dialog. This store outlives it. It is a module-level map, so it lives exactly as
|
|
9
|
+
* long as the tab: a reload starts empty.
|
|
10
|
+
*
|
|
11
|
+
* Only answers are kept, never a refusal: a refusal is usually temporary (a
|
|
12
|
+
* bundle still building), and keeping it would show it again after it is gone.
|
|
13
|
+
*
|
|
14
|
+
* The key is built by the caller and has to name everything the answer depends
|
|
15
|
+
* on — tenant, node, version, target and the config the query is keyed by.
|
|
16
|
+
*/
|
|
17
|
+
import type { NodeConfigResolveResult } from '../types/integrations.js';
|
|
18
|
+
/** How long an answer may still be shown while it is asked for again. */
|
|
19
|
+
export declare const KEEP_MS: number;
|
|
20
|
+
/** How many answers the tab keeps; the oldest goes first. */
|
|
21
|
+
export declare const MAX_ANSWERS = 50;
|
|
22
|
+
/** The kept answer and its age in ms, or `undefined` when there is none young enough to show. */
|
|
23
|
+
export declare function keptAnswer(key: string, now?: number): {
|
|
24
|
+
result: NodeConfigResolveResult;
|
|
25
|
+
age: number;
|
|
26
|
+
} | undefined;
|
|
27
|
+
export declare function keepAnswer(key: string, result: NodeConfigResolveResult, now?: number): void;
|
|
28
|
+
/** Forgets the answer kept under `key`: the platform refused it since, so it is not shown again. */
|
|
29
|
+
export declare function forgetAnswer(key: string): void;
|
|
30
|
+
/** Test hook: forget every answer. */
|
|
31
|
+
export declare function _clearKeptAnswers(): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const KEEP_MS = 10 * 6e4;
|
|
2
|
+
export const MAX_ANSWERS = 50;
|
|
3
|
+
const answers = /* @__PURE__ */ new Map();
|
|
4
|
+
export function keptAnswer(key, now = Date.now()) {
|
|
5
|
+
const hit = answers.get(key);
|
|
6
|
+
if (!hit) return void 0;
|
|
7
|
+
const age = now - hit.at;
|
|
8
|
+
if (age >= KEEP_MS) {
|
|
9
|
+
answers.delete(key);
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
return { result: hit.result, age };
|
|
13
|
+
}
|
|
14
|
+
export function keepAnswer(key, result, now = Date.now()) {
|
|
15
|
+
answers.delete(key);
|
|
16
|
+
answers.set(key, { result, at: now });
|
|
17
|
+
while (answers.size > MAX_ANSWERS) answers.delete(answers.keys().next().value);
|
|
18
|
+
}
|
|
19
|
+
export function forgetAnswer(key) {
|
|
20
|
+
answers.delete(key);
|
|
21
|
+
}
|
|
22
|
+
export function _clearKeptAnswers() {
|
|
23
|
+
answers.clear();
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revenexx/studio-integrations",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Integration Studio (core: workflows, node editor, runs, schedules, credentials, secrets, audit) as a standalone, publishable Nuxt module.",
|
|
6
6
|
"repository": {
|