@jskit-ai/users-web 0.1.105 → 0.1.106
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/package.descriptor.mjs +7 -7
- package/package.json +7 -7
- package/src/client/composables/records/useAddEdit.js +4 -0
- package/src/client/composables/records/useList.js +4 -0
- package/src/client/composables/records/useView.js +4 -0
- package/src/client/composables/runtime/useEndpointResource.js +18 -8
- package/src/client/composables/runtime/useListCore.js +11 -1
- package/src/client/composables/support/resourceLoadStateHelpers.js +188 -1
- package/src/client/composables/useCrudListScreen.js +2 -0
- package/src/client/composables/useCrudViewScreen.js +2 -0
- package/test/requestTransportOptions.test.js +38 -0
- package/test/resourceLoadStateHelpers.test.js +117 -1
package/package.descriptor.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { HOME_COG_OUTLET } from "./src/shared/toolsOutletContracts.js";
|
|
|
3
3
|
export default Object.freeze({
|
|
4
4
|
packageVersion: 1,
|
|
5
5
|
packageId: "@jskit-ai/users-web",
|
|
6
|
-
version: "0.1.
|
|
6
|
+
version: "0.1.106",
|
|
7
7
|
kind: "runtime",
|
|
8
8
|
description: "Users web module: account/profile UI plus shared users web widgets.",
|
|
9
9
|
dependsOn: [
|
|
@@ -278,12 +278,12 @@ export default Object.freeze({
|
|
|
278
278
|
dependencies: {
|
|
279
279
|
runtime: {
|
|
280
280
|
"@mdi/js": "^7.4.47",
|
|
281
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
282
|
-
"@jskit-ai/realtime": "0.1.
|
|
283
|
-
"@jskit-ai/kernel": "0.1.
|
|
284
|
-
"@jskit-ai/shell-web": "0.1.
|
|
285
|
-
"@jskit-ai/uploads-image-web": "0.1.
|
|
286
|
-
"@jskit-ai/users-core": "0.1.
|
|
281
|
+
"@jskit-ai/http-runtime": "0.1.90",
|
|
282
|
+
"@jskit-ai/realtime": "0.1.90",
|
|
283
|
+
"@jskit-ai/kernel": "0.1.91",
|
|
284
|
+
"@jskit-ai/shell-web": "0.1.90",
|
|
285
|
+
"@jskit-ai/uploads-image-web": "0.1.69",
|
|
286
|
+
"@jskit-ai/users-core": "0.1.101"
|
|
287
287
|
},
|
|
288
288
|
dev: {}
|
|
289
289
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/users-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.106",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@mdi/js": "^7.4.47",
|
|
47
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
48
|
-
"@jskit-ai/kernel": "0.1.
|
|
49
|
-
"@jskit-ai/realtime": "0.1.
|
|
50
|
-
"@jskit-ai/shell-web": "0.1.
|
|
51
|
-
"@jskit-ai/uploads-image-web": "0.1.
|
|
52
|
-
"@jskit-ai/users-core": "0.1.
|
|
47
|
+
"@jskit-ai/http-runtime": "0.1.90",
|
|
48
|
+
"@jskit-ai/kernel": "0.1.91",
|
|
49
|
+
"@jskit-ai/realtime": "0.1.90",
|
|
50
|
+
"@jskit-ai/shell-web": "0.1.90",
|
|
51
|
+
"@jskit-ai/uploads-image-web": "0.1.69",
|
|
52
|
+
"@jskit-ai/users-core": "0.1.101"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"@tanstack/vue-query": "^5.90.5",
|
|
@@ -30,6 +30,8 @@ function useAddEdit({
|
|
|
30
30
|
readEnabled = true,
|
|
31
31
|
writeMethod = "PATCH",
|
|
32
32
|
transport = null,
|
|
33
|
+
requestRecovery = null,
|
|
34
|
+
requestRecoveryLabel = "Resource",
|
|
33
35
|
placementSource = "users-web.add-edit",
|
|
34
36
|
fallbackLoadError = "Unable to load resource.",
|
|
35
37
|
fallbackSaveError = "Unable to save resource.",
|
|
@@ -121,6 +123,8 @@ function useAddEdit({
|
|
|
121
123
|
writeMethod,
|
|
122
124
|
readQuery: requestQueryRuntime.requestQuery,
|
|
123
125
|
transport,
|
|
126
|
+
requestRecovery,
|
|
127
|
+
requestRecoveryLabel,
|
|
124
128
|
fallbackLoadError,
|
|
125
129
|
fallbackSaveError: String(fallbackSaveError || effectiveMessages.saveError || "Unable to save resource.")
|
|
126
130
|
});
|
|
@@ -46,6 +46,8 @@ function useList({
|
|
|
46
46
|
transport = null,
|
|
47
47
|
requestOptions,
|
|
48
48
|
queryOptions,
|
|
49
|
+
requestRecovery,
|
|
50
|
+
requestRecoveryLabel = "List",
|
|
49
51
|
realtime = null,
|
|
50
52
|
adapter = null,
|
|
51
53
|
recordIdParam = "recordId",
|
|
@@ -228,6 +230,8 @@ function useList({
|
|
|
228
230
|
selectItems,
|
|
229
231
|
requestOptions: listRequestOptions,
|
|
230
232
|
queryOptions,
|
|
233
|
+
requestRecovery,
|
|
234
|
+
requestRecoveryLabel,
|
|
231
235
|
fallbackLoadError
|
|
232
236
|
});
|
|
233
237
|
const routeSyncHydrated = ref(routeSyncConfig.enabled !== true);
|
|
@@ -21,6 +21,8 @@ function useView({
|
|
|
21
21
|
readMethod = "GET",
|
|
22
22
|
readEnabled = true,
|
|
23
23
|
transport = null,
|
|
24
|
+
requestRecovery = null,
|
|
25
|
+
requestRecoveryLabel = "Resource",
|
|
24
26
|
placementSource = "users-web.view",
|
|
25
27
|
fallbackLoadError = "Unable to load resource.",
|
|
26
28
|
notFoundStatuses = [404],
|
|
@@ -104,6 +106,8 @@ function useView({
|
|
|
104
106
|
readMethod,
|
|
105
107
|
readQuery: requestQueryRuntime.requestQuery,
|
|
106
108
|
transport,
|
|
109
|
+
requestRecovery,
|
|
110
|
+
requestRecoveryLabel,
|
|
107
111
|
refreshOnPull: true,
|
|
108
112
|
fallbackLoadError
|
|
109
113
|
});
|
|
@@ -7,7 +7,8 @@ import { toQueryErrorMessage } from "../support/errorMessageHelpers.js";
|
|
|
7
7
|
import { useOperationRealtime } from "../useRealtimeQueryInvalidation.js";
|
|
8
8
|
import {
|
|
9
9
|
hasResolvedQueryData,
|
|
10
|
-
mergeQueryMeta
|
|
10
|
+
mergeQueryMeta,
|
|
11
|
+
mergeRequestRecoveryQueryMeta
|
|
11
12
|
} from "../support/resourceLoadStateHelpers.js";
|
|
12
13
|
|
|
13
14
|
function buildEndpointReadRequestOptions({
|
|
@@ -75,6 +76,8 @@ function useEndpointResource({
|
|
|
75
76
|
realtime = null,
|
|
76
77
|
queryOptions = null,
|
|
77
78
|
mutationOptions = null,
|
|
79
|
+
requestRecovery = null,
|
|
80
|
+
requestRecoveryLabel = "Request",
|
|
78
81
|
fallbackLoadError = "Unable to load resource.",
|
|
79
82
|
fallbackSaveError = "Unable to save resource."
|
|
80
83
|
} = {}) {
|
|
@@ -102,13 +105,20 @@ function useEndpointResource({
|
|
|
102
105
|
});
|
|
103
106
|
},
|
|
104
107
|
enabled: queryEnabled,
|
|
105
|
-
...(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
...mergeRequestRecoveryQueryMeta(
|
|
109
|
+
refreshOnPull
|
|
110
|
+
? mergeQueryMeta(asPlainObject(queryOptions), {
|
|
111
|
+
jskit: {
|
|
112
|
+
refreshOnPull: true
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
: asPlainObject(queryOptions),
|
|
116
|
+
requestRecovery,
|
|
117
|
+
{
|
|
118
|
+
label: requestRecoveryLabel,
|
|
119
|
+
method: readMethod
|
|
120
|
+
}
|
|
121
|
+
)
|
|
112
122
|
});
|
|
113
123
|
const realtimeBinding = useOperationRealtime({
|
|
114
124
|
realtime,
|
|
@@ -3,6 +3,7 @@ import { usersWebHttpClient } from "../../lib/httpClient.js";
|
|
|
3
3
|
import { asPlainObject } from "../support/scopeHelpers.js";
|
|
4
4
|
import { resolveEnabledRef, resolveTextRef } from "../support/refValueHelpers.js";
|
|
5
5
|
import { usePagedCollection } from "../usePagedCollection.js";
|
|
6
|
+
import { mergeRequestRecoveryQueryMeta } from "../support/resourceLoadStateHelpers.js";
|
|
6
7
|
|
|
7
8
|
const DEFAULT_LIST_LIMIT = 20;
|
|
8
9
|
|
|
@@ -47,6 +48,10 @@ function resolveRequestOptionsObject(value = null) {
|
|
|
47
48
|
return asPlainObject(source);
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
function resolveListRequestMethod(requestOptions = null) {
|
|
52
|
+
return String(resolveRequestOptionsObject(requestOptions).method || "GET").toUpperCase();
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
function useListCore({
|
|
51
56
|
queryKey,
|
|
52
57
|
path = "",
|
|
@@ -58,6 +63,8 @@ function useListCore({
|
|
|
58
63
|
selectItems,
|
|
59
64
|
requestOptions = null,
|
|
60
65
|
queryOptions = null,
|
|
66
|
+
requestRecovery = null,
|
|
67
|
+
requestRecoveryLabel = "List",
|
|
61
68
|
fallbackLoadError = "Unable to load list."
|
|
62
69
|
} = {}) {
|
|
63
70
|
if (!client || typeof client.request !== "function") {
|
|
@@ -88,7 +95,10 @@ function useListCore({
|
|
|
88
95
|
},
|
|
89
96
|
getNextPageParam,
|
|
90
97
|
selectItems,
|
|
91
|
-
queryOptions,
|
|
98
|
+
queryOptions: mergeRequestRecoveryQueryMeta(queryOptions, requestRecovery, {
|
|
99
|
+
label: requestRecoveryLabel,
|
|
100
|
+
method: resolveListRequestMethod(requestOptions)
|
|
101
|
+
}),
|
|
92
102
|
fallbackLoadError
|
|
93
103
|
});
|
|
94
104
|
|
|
@@ -36,7 +36,194 @@ function mergeQueryMeta(queryOptions = null, meta = {}) {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function normalizeText(value = "") {
|
|
40
|
+
return String(value || "").trim();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function firstNormalizedText(...values) {
|
|
44
|
+
for (const value of values) {
|
|
45
|
+
const normalized = normalizeText(value);
|
|
46
|
+
if (normalized) {
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function firstNonNegativeFiniteNumber(...values) {
|
|
54
|
+
for (const value of values) {
|
|
55
|
+
const numberValue = Number(value);
|
|
56
|
+
if (Number.isFinite(numberValue)) {
|
|
57
|
+
return Math.max(0, numberValue);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function normalizePlainObject(value = null) {
|
|
64
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizeRequestRecoveryInput(requestRecovery = null) {
|
|
68
|
+
if (typeof requestRecovery === "string") {
|
|
69
|
+
return {
|
|
70
|
+
label: requestRecovery
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return normalizePlainObject(requestRecovery);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizeRequestRecoveryMethod(value = "") {
|
|
77
|
+
return normalizeText(value).toUpperCase();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isRequestRecoveryMetaDisabled(sourceJskitMeta = {}, sourceMeta = {}) {
|
|
81
|
+
return Boolean(
|
|
82
|
+
sourceJskitMeta.requestRecovery === false ||
|
|
83
|
+
sourceMeta.jskitRequestRecovery === false ||
|
|
84
|
+
sourceMeta.requestRecovery === false
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function hasUsableMetaValue(source = {}, key = "") {
|
|
89
|
+
return Object.hasOwn(source, key) && source[key] !== undefined && source[key] !== null && source[key] !== "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function buildRequestRecoveryMeta(requestRecovery = null, defaults = {}) {
|
|
93
|
+
if (requestRecovery === false) {
|
|
94
|
+
return {
|
|
95
|
+
jskit: {
|
|
96
|
+
requestRecovery: false
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const fallback = normalizePlainObject(defaults);
|
|
102
|
+
const source = normalizeRequestRecoveryInput(requestRecovery);
|
|
103
|
+
if (source.requestRecovery === false || source.enabled === false) {
|
|
104
|
+
return {
|
|
105
|
+
jskit: {
|
|
106
|
+
requestRecovery: false
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const label = firstNormalizedText(
|
|
112
|
+
source.requestRecoveryLabel,
|
|
113
|
+
source.label,
|
|
114
|
+
fallback.requestRecoveryLabel,
|
|
115
|
+
fallback.label
|
|
116
|
+
);
|
|
117
|
+
const recoverySource = firstNormalizedText(
|
|
118
|
+
source.requestRecoverySource,
|
|
119
|
+
source.source,
|
|
120
|
+
fallback.requestRecoverySource,
|
|
121
|
+
fallback.source
|
|
122
|
+
);
|
|
123
|
+
const dedupeKey = firstNormalizedText(
|
|
124
|
+
source.requestRecoveryDedupeKey,
|
|
125
|
+
source.dedupeKey,
|
|
126
|
+
fallback.requestRecoveryDedupeKey,
|
|
127
|
+
fallback.dedupeKey
|
|
128
|
+
);
|
|
129
|
+
const method = normalizeRequestRecoveryMethod(
|
|
130
|
+
firstNormalizedText(
|
|
131
|
+
source.requestRecoveryMethod,
|
|
132
|
+
source.method,
|
|
133
|
+
fallback.requestRecoveryMethod,
|
|
134
|
+
fallback.method
|
|
135
|
+
)
|
|
136
|
+
);
|
|
137
|
+
const dedupeWindowMs = firstNonNegativeFiniteNumber(
|
|
138
|
+
source.requestRecoveryDedupeWindowMs,
|
|
139
|
+
source.dedupeWindowMs,
|
|
140
|
+
fallback.requestRecoveryDedupeWindowMs,
|
|
141
|
+
fallback.dedupeWindowMs
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const jskit = {};
|
|
145
|
+
if (label) {
|
|
146
|
+
jskit.requestRecoveryLabel = label;
|
|
147
|
+
}
|
|
148
|
+
if (recoverySource) {
|
|
149
|
+
jskit.requestRecoverySource = recoverySource;
|
|
150
|
+
}
|
|
151
|
+
if (dedupeKey) {
|
|
152
|
+
jskit.requestRecoveryDedupeKey = dedupeKey;
|
|
153
|
+
}
|
|
154
|
+
if (method) {
|
|
155
|
+
jskit.requestRecoveryMethod = method;
|
|
156
|
+
}
|
|
157
|
+
if (dedupeWindowMs !== null) {
|
|
158
|
+
jskit.requestRecoveryDedupeWindowMs = dedupeWindowMs;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return Object.keys(jskit).length > 0 ? { jskit } : {};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function hasRequestRecoveryMetaValue(sourceJskitMeta = {}, sourceMeta = {}, key = "") {
|
|
165
|
+
const suffix = String(key || "").trim();
|
|
166
|
+
if (!suffix) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const jskitKey = `requestRecovery${suffix}`;
|
|
171
|
+
const legacyJskitKey = `jskitRequestRecovery${suffix}`;
|
|
172
|
+
const legacyKey = `requestRecovery${suffix}`;
|
|
173
|
+
|
|
174
|
+
return Boolean(
|
|
175
|
+
hasUsableMetaValue(sourceJskitMeta, jskitKey) ||
|
|
176
|
+
hasUsableMetaValue(sourceMeta, legacyJskitKey) ||
|
|
177
|
+
hasUsableMetaValue(sourceMeta, legacyKey)
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function resolveRequestRecoveryDefaults(queryOptions = null, defaults = {}) {
|
|
182
|
+
const sourceOptions = normalizePlainObject(queryOptions);
|
|
183
|
+
const sourceMeta = normalizePlainObject(sourceOptions.meta);
|
|
184
|
+
const sourceJskitMeta = normalizePlainObject(sourceMeta.jskit);
|
|
185
|
+
const fallback = normalizePlainObject(defaults);
|
|
186
|
+
if (isRequestRecoveryMetaDisabled(sourceJskitMeta, sourceMeta)) {
|
|
187
|
+
return {};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const hasExplicitLabel = hasRequestRecoveryMetaValue(sourceJskitMeta, sourceMeta, "Label");
|
|
191
|
+
const hasExplicitSource = hasRequestRecoveryMetaValue(sourceJskitMeta, sourceMeta, "Source");
|
|
192
|
+
const hasExplicitDedupeKey = hasRequestRecoveryMetaValue(sourceJskitMeta, sourceMeta, "DedupeKey");
|
|
193
|
+
const hasExplicitDedupeWindowMs = hasRequestRecoveryMetaValue(sourceJskitMeta, sourceMeta, "DedupeWindowMs");
|
|
194
|
+
const hasExplicitMethod = hasRequestRecoveryMetaValue(sourceJskitMeta, sourceMeta, "Method");
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
...fallback,
|
|
198
|
+
requestRecoveryLabel: hasExplicitLabel ? "" : fallback.requestRecoveryLabel,
|
|
199
|
+
label: hasExplicitLabel ? "" : fallback.label,
|
|
200
|
+
requestRecoverySource: hasExplicitSource ? "" : fallback.requestRecoverySource,
|
|
201
|
+
source: hasExplicitSource ? "" : fallback.source,
|
|
202
|
+
requestRecoveryDedupeKey: hasExplicitDedupeKey ? "" : fallback.requestRecoveryDedupeKey,
|
|
203
|
+
dedupeKey: hasExplicitDedupeKey ? "" : fallback.dedupeKey,
|
|
204
|
+
requestRecoveryDedupeWindowMs: hasExplicitDedupeWindowMs ? "" : fallback.requestRecoveryDedupeWindowMs,
|
|
205
|
+
dedupeWindowMs: hasExplicitDedupeWindowMs ? "" : fallback.dedupeWindowMs,
|
|
206
|
+
requestRecoveryMethod: hasExplicitMethod ? "" : fallback.requestRecoveryMethod,
|
|
207
|
+
method: hasExplicitMethod ? "" : fallback.method
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function mergeRequestRecoveryQueryMeta(queryOptions = null, requestRecovery = null, defaults = {}) {
|
|
212
|
+
const sourceOptions = normalizePlainObject(queryOptions);
|
|
213
|
+
const effectiveDefaults =
|
|
214
|
+
requestRecovery == null
|
|
215
|
+
? resolveRequestRecoveryDefaults(sourceOptions, defaults)
|
|
216
|
+
: normalizePlainObject(defaults);
|
|
217
|
+
const meta = buildRequestRecoveryMeta(requestRecovery, effectiveDefaults);
|
|
218
|
+
if (Object.keys(meta).length < 1) {
|
|
219
|
+
return sourceOptions;
|
|
220
|
+
}
|
|
221
|
+
return mergeQueryMeta(sourceOptions, meta);
|
|
222
|
+
}
|
|
223
|
+
|
|
39
224
|
export {
|
|
225
|
+
buildRequestRecoveryMeta,
|
|
40
226
|
hasResolvedQueryData,
|
|
41
|
-
mergeQueryMeta
|
|
227
|
+
mergeQueryMeta,
|
|
228
|
+
mergeRequestRecoveryQueryMeta
|
|
42
229
|
};
|
|
@@ -31,6 +31,7 @@ function useCrudListScreen({
|
|
|
31
31
|
listFilters = {},
|
|
32
32
|
listBulkActions = [],
|
|
33
33
|
routeQueryBlacklist = Object.freeze(["include", "cursor", "limit"]),
|
|
34
|
+
requestRecoveryLabel = "Records",
|
|
34
35
|
fallbackLoadError = "Unable to load records."
|
|
35
36
|
} = {}) {
|
|
36
37
|
const filterRuntime = useCrudListFilters(listFilters);
|
|
@@ -62,6 +63,7 @@ function useCrudListScreen({
|
|
|
62
63
|
queryParamBlacklist: routeQueryBlacklist
|
|
63
64
|
},
|
|
64
65
|
placementSource: `ui-generator.${normalizedResourceNamespace}.list`,
|
|
66
|
+
requestRecoveryLabel,
|
|
65
67
|
fallbackLoadError,
|
|
66
68
|
recordIdParam,
|
|
67
69
|
recordIdSelector,
|
|
@@ -12,6 +12,7 @@ function useCrudViewScreen({
|
|
|
12
12
|
listUrlTemplate = "",
|
|
13
13
|
editUrlTemplate = "",
|
|
14
14
|
recordChangedEvent = "",
|
|
15
|
+
requestRecoveryLabel = "Record",
|
|
15
16
|
fallbackLoadError = "Unable to load record.",
|
|
16
17
|
notFoundMessage = "Record not found."
|
|
17
18
|
} = {}) {
|
|
@@ -31,6 +32,7 @@ function useCrudViewScreen({
|
|
|
31
32
|
String(workspaceSlug || "")
|
|
32
33
|
],
|
|
33
34
|
placementSource: `ui-generator.${normalizedResourceNamespace}.view`,
|
|
35
|
+
requestRecoveryLabel,
|
|
34
36
|
fallbackLoadError,
|
|
35
37
|
notFoundMessage,
|
|
36
38
|
listUrlTemplate,
|
|
@@ -249,3 +249,41 @@ test("endpoint resources invalidate their query key from configured realtime eve
|
|
|
249
249
|
}
|
|
250
250
|
]);
|
|
251
251
|
});
|
|
252
|
+
|
|
253
|
+
test("endpoint resource reads attach request recovery metadata to query options", async () => {
|
|
254
|
+
const queryClient = new QueryClient();
|
|
255
|
+
const queryKey = ["project-access", "beepollen"];
|
|
256
|
+
const app = createSSRApp({
|
|
257
|
+
setup() {
|
|
258
|
+
useEndpointResource({
|
|
259
|
+
queryKey,
|
|
260
|
+
path: "/api/project-access",
|
|
261
|
+
client: {
|
|
262
|
+
async request() {
|
|
263
|
+
return {};
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
requestRecovery: {
|
|
267
|
+
label: "Project access",
|
|
268
|
+
source: "project-access.panel",
|
|
269
|
+
dedupeKey: "project-access"
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
return () => h("div");
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
app.use(VueQueryPlugin, {
|
|
276
|
+
queryClient
|
|
277
|
+
});
|
|
278
|
+
await renderToString(app);
|
|
279
|
+
|
|
280
|
+
const query = queryClient.getQueryCache().find({
|
|
281
|
+
queryKey
|
|
282
|
+
});
|
|
283
|
+
assert.deepEqual(query?.meta?.jskit, {
|
|
284
|
+
requestRecoveryLabel: "Project access",
|
|
285
|
+
requestRecoverySource: "project-access.panel",
|
|
286
|
+
requestRecoveryDedupeKey: "project-access",
|
|
287
|
+
requestRecoveryMethod: "GET"
|
|
288
|
+
});
|
|
289
|
+
});
|
|
@@ -2,8 +2,10 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import { ref } from "vue";
|
|
4
4
|
import {
|
|
5
|
+
buildRequestRecoveryMeta,
|
|
5
6
|
hasResolvedQueryData,
|
|
6
|
-
mergeQueryMeta
|
|
7
|
+
mergeQueryMeta,
|
|
8
|
+
mergeRequestRecoveryQueryMeta
|
|
7
9
|
} from "../src/client/composables/support/resourceLoadStateHelpers.js";
|
|
8
10
|
|
|
9
11
|
test("hasResolvedQueryData returns true when the query succeeded", () => {
|
|
@@ -71,3 +73,117 @@ test("mergeQueryMeta preserves caller metadata while adding JSKIT refresh policy
|
|
|
71
73
|
}
|
|
72
74
|
);
|
|
73
75
|
});
|
|
76
|
+
|
|
77
|
+
test("request recovery query metadata supports labels and disabling recovery", () => {
|
|
78
|
+
assert.deepEqual(
|
|
79
|
+
buildRequestRecoveryMeta("Project access"),
|
|
80
|
+
{
|
|
81
|
+
jskit: {
|
|
82
|
+
requestRecoveryLabel: "Project access"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
assert.deepEqual(
|
|
88
|
+
buildRequestRecoveryMeta(false),
|
|
89
|
+
{
|
|
90
|
+
jskit: {
|
|
91
|
+
requestRecovery: false
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("mergeRequestRecoveryQueryMeta preserves caller metadata while adding recovery hints", () => {
|
|
98
|
+
assert.deepEqual(
|
|
99
|
+
mergeRequestRecoveryQueryMeta(
|
|
100
|
+
{
|
|
101
|
+
staleTime: 5000,
|
|
102
|
+
meta: {
|
|
103
|
+
owner: "project-access",
|
|
104
|
+
jskit: {
|
|
105
|
+
refreshOnPull: true
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
label: "Project access",
|
|
111
|
+
source: "app.project-access",
|
|
112
|
+
dedupeKey: "project-access",
|
|
113
|
+
method: "get",
|
|
114
|
+
dedupeWindowMs: 100
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
{
|
|
118
|
+
staleTime: 5000,
|
|
119
|
+
meta: {
|
|
120
|
+
owner: "project-access",
|
|
121
|
+
jskit: {
|
|
122
|
+
refreshOnPull: true,
|
|
123
|
+
requestRecoveryLabel: "Project access",
|
|
124
|
+
requestRecoverySource: "app.project-access",
|
|
125
|
+
requestRecoveryDedupeKey: "project-access",
|
|
126
|
+
requestRecoveryMethod: "GET",
|
|
127
|
+
requestRecoveryDedupeWindowMs: 100
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("mergeRequestRecoveryQueryMeta does not overwrite explicit query recovery labels with defaults", () => {
|
|
135
|
+
assert.deepEqual(
|
|
136
|
+
mergeRequestRecoveryQueryMeta(
|
|
137
|
+
{
|
|
138
|
+
meta: {
|
|
139
|
+
jskit: {
|
|
140
|
+
requestRecoveryLabel: "Explicit label",
|
|
141
|
+
requestRecoveryMethod: "HEAD",
|
|
142
|
+
requestRecoveryDedupeWindowMs: 0
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
null,
|
|
147
|
+
{
|
|
148
|
+
label: "Default label",
|
|
149
|
+
method: "GET",
|
|
150
|
+
dedupeWindowMs: 5000
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
{
|
|
154
|
+
meta: {
|
|
155
|
+
jskit: {
|
|
156
|
+
requestRecoveryLabel: "Explicit label",
|
|
157
|
+
requestRecoveryMethod: "HEAD",
|
|
158
|
+
requestRecoveryDedupeWindowMs: 0
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("mergeRequestRecoveryQueryMeta preserves disabled recovery metadata", () => {
|
|
166
|
+
assert.deepEqual(
|
|
167
|
+
mergeRequestRecoveryQueryMeta(
|
|
168
|
+
{
|
|
169
|
+
meta: {
|
|
170
|
+
jskit: {
|
|
171
|
+
requestRecovery: false
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
null,
|
|
176
|
+
{
|
|
177
|
+
label: "Default label",
|
|
178
|
+
method: "GET"
|
|
179
|
+
}
|
|
180
|
+
),
|
|
181
|
+
{
|
|
182
|
+
meta: {
|
|
183
|
+
jskit: {
|
|
184
|
+
requestRecovery: false
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
});
|