@reconcrap/boss-recommend-mcp 2.1.24 → 2.1.25
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.json +1 -1
- package/src/chat-runtime-config.js +7 -5
- package/src/core/browser/index.js +16 -9
- package/src/core/run/index.js +5 -4
- package/src/core/screening/index.js +291 -26
- package/src/domains/chat/action-journal.js +97 -4
- package/src/domains/recommend/actions.js +740 -36
- package/src/domains/recommend/colleague-contact.js +836 -90
- package/src/domains/recommend/detail.js +4029 -128
- package/src/domains/recommend/filters.js +46 -28
- package/src/domains/recommend/jobs.js +32 -3
- package/src/domains/recommend/location.js +93 -46
- package/src/domains/recommend/refresh.js +354 -69
- package/src/domains/recommend/run-service.js +2499 -484
- package/src/recommend-mcp.js +514 -92
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getNodeBox,
|
|
3
|
+
getOuterHTML,
|
|
4
|
+
querySelectorAll,
|
|
5
|
+
sleep
|
|
6
|
+
} from "../../core/browser/index.js";
|
|
7
|
+
import { htmlToText, normalizeText } from "../../core/screening/index.js";
|
|
2
8
|
import {
|
|
3
9
|
buildRecommendSelfHealConfig,
|
|
4
10
|
resolveRecommendSelfHealRoots
|
|
@@ -11,10 +17,11 @@ import {
|
|
|
11
17
|
clickRecommendEndRefreshButton,
|
|
12
18
|
waitForRecommendCardNodeIds
|
|
13
19
|
} from "./cards.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
import {
|
|
21
|
+
RECOMMEND_BOTTOM_MARKER_SELECTORS,
|
|
22
|
+
RECOMMEND_RECENT_NOT_VIEW_LABEL,
|
|
23
|
+
RECOMMEND_TARGET_URL
|
|
24
|
+
} from "./constants.js";
|
|
18
25
|
import { selectAndConfirmFirstSafeFilter } from "./filters.js";
|
|
19
26
|
import { ensureRecommendCurrentCityOnly } from "./location.js";
|
|
20
27
|
import {
|
|
@@ -52,38 +59,43 @@ export function buildRecommendFilterGroups(filter = {}, {
|
|
|
52
59
|
? filter.groups
|
|
53
60
|
: [];
|
|
54
61
|
|
|
55
|
-
for (const spec of sourceGroups) {
|
|
56
|
-
const group = normalizeFilterGroup(spec);
|
|
57
|
-
if (group.group || group.labels.length)
|
|
58
|
-
|
|
62
|
+
for (const spec of sourceGroups) {
|
|
63
|
+
const group = normalizeFilterGroup(spec);
|
|
64
|
+
if (group.group || group.labels.length) {
|
|
65
|
+
group.verifySticky = true;
|
|
66
|
+
groups.push(group);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
59
69
|
|
|
60
70
|
const rootGroup = normalizeFilterGroup(filter);
|
|
61
|
-
if ((rootGroup.group || rootGroup.labels.length) && !groups.length) {
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
if ((rootGroup.group || rootGroup.labels.length) && !groups.length) {
|
|
72
|
+
rootGroup.verifySticky = true;
|
|
73
|
+
groups.push(rootGroup);
|
|
74
|
+
}
|
|
64
75
|
|
|
65
76
|
if (forceRecentNotView) {
|
|
66
77
|
const recentGroup = groups.find((item) => item.group === "recentNotView");
|
|
67
78
|
if (recentGroup) {
|
|
68
79
|
if (!recentGroup.labels.some((label) => label.replace(/\s+/g, "") === RECOMMEND_RECENT_NOT_VIEW_LABEL)) {
|
|
69
80
|
recentGroup.labels.push(RECOMMEND_RECENT_NOT_VIEW_LABEL);
|
|
70
|
-
}
|
|
71
|
-
recentGroup.selectAllLabels = true;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
}
|
|
82
|
+
recentGroup.selectAllLabels = true;
|
|
83
|
+
recentGroup.verifySticky = true;
|
|
84
|
+
} else {
|
|
85
|
+
groups.unshift({
|
|
86
|
+
group: "recentNotView",
|
|
87
|
+
labels: [RECOMMEND_RECENT_NOT_VIEW_LABEL],
|
|
88
|
+
selectAllLabels: true,
|
|
89
|
+
allowUnlimited: false,
|
|
90
|
+
verifySticky: true
|
|
91
|
+
});
|
|
80
92
|
}
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
return groups;
|
|
84
96
|
}
|
|
85
97
|
|
|
86
|
-
export function buildRecommendFilterSelectionOptions(filter = {}, {
|
|
98
|
+
export function buildRecommendFilterSelectionOptions(filter = {}, {
|
|
87
99
|
forceRecentNotView = false
|
|
88
100
|
} = {}) {
|
|
89
101
|
const filterGroups = buildRecommendFilterGroups(filter, { forceRecentNotView });
|
|
@@ -134,9 +146,232 @@ export async function applyRecommendFilterEnvelopeStages(filter = {}, {
|
|
|
134
146
|
};
|
|
135
147
|
}
|
|
136
148
|
|
|
137
|
-
function refreshFailureReason(method = "") {
|
|
138
|
-
return method === "page_navigate" ? "page_navigate_failed" : "page_reload_failed";
|
|
139
|
-
}
|
|
149
|
+
function refreshFailureReason(method = "") {
|
|
150
|
+
return method === "page_navigate" ? "page_navigate_failed" : "page_reload_failed";
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function isVerifiedRecommendFilterApplication(filterResult, filterOptions = {}) {
|
|
154
|
+
if (filterResult?.confirmed !== true) return false;
|
|
155
|
+
const requestedGroups = Array.isArray(filterOptions.filterGroups) && filterOptions.filterGroups.length
|
|
156
|
+
? filterOptions.filterGroups
|
|
157
|
+
: (filterOptions.group || filterOptions.labels?.length ? [filterOptions] : []);
|
|
158
|
+
const normalizedRequested = requestedGroups
|
|
159
|
+
.filter((group) => group && String(group.group || "").trim() && Array.isArray(group.labels) && group.labels.length)
|
|
160
|
+
.map((group) => ({
|
|
161
|
+
group: String(group.group || "").trim(),
|
|
162
|
+
labels: [...new Set(normalizeLabels(group.labels).map((label) => label.replace(/\s+/g, "")))],
|
|
163
|
+
allowUnlimited: group.allowUnlimited === true,
|
|
164
|
+
selectAllLabels: group.selectAllLabels !== false
|
|
165
|
+
}));
|
|
166
|
+
if (!normalizedRequested.length) return true;
|
|
167
|
+
if (filterResult?.sticky_verification?.verified !== true) return false;
|
|
168
|
+
const verifiedGroups = Array.isArray(filterResult.sticky_verification.groups)
|
|
169
|
+
? filterResult.sticky_verification.groups
|
|
170
|
+
: [];
|
|
171
|
+
if (verifiedGroups.length !== normalizedRequested.length) return false;
|
|
172
|
+
return normalizedRequested.every((requested) => {
|
|
173
|
+
const matches = verifiedGroups.filter((group) => String(group?.group || "") === requested.group);
|
|
174
|
+
if (matches.length !== 1 || matches[0]?.verified !== true) return false;
|
|
175
|
+
const verifiedLabels = normalizeLabels(matches[0].requested_labels || [])
|
|
176
|
+
.map((label) => label.replace(/\s+/g, ""));
|
|
177
|
+
const uniqueVerifiedLabels = [...new Set(verifiedLabels)];
|
|
178
|
+
if (
|
|
179
|
+
uniqueVerifiedLabels.length !== requested.labels.length
|
|
180
|
+
|| !requested.labels.every((label) => uniqueVerifiedLabels.includes(label))
|
|
181
|
+
) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
const activeLabels = [...new Set(normalizeLabels(matches[0].active_labels || [])
|
|
185
|
+
.map((label) => label.replace(/\s+/g, "")))];
|
|
186
|
+
if (matches[0].unavailable === true) {
|
|
187
|
+
return requested.allowUnlimited
|
|
188
|
+
&& requested.labels.length === 1
|
|
189
|
+
&& requested.labels[0] === "不限"
|
|
190
|
+
&& activeLabels.length === 0;
|
|
191
|
+
}
|
|
192
|
+
if (!requested.selectAllLabels) {
|
|
193
|
+
return activeLabels.length === 1 && requested.labels.includes(activeLabels[0]);
|
|
194
|
+
}
|
|
195
|
+
return activeLabels.length === requested.labels.length
|
|
196
|
+
&& requested.labels.every((label) => activeLabels.includes(label));
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const RECOMMEND_FILTERED_EMPTY_TEXTS = Object.freeze([
|
|
201
|
+
"没有相关数据",
|
|
202
|
+
"暂无相关数据"
|
|
203
|
+
]);
|
|
204
|
+
|
|
205
|
+
const RECOMMEND_FILTERED_EMPTY_TEXT_SCAN_SELECTORS = Object.freeze([
|
|
206
|
+
...RECOMMEND_BOTTOM_MARKER_SELECTORS,
|
|
207
|
+
"[class*=\"empty\"]",
|
|
208
|
+
"[class*=\"no-data\"]",
|
|
209
|
+
"[class*=\"nodata\"]",
|
|
210
|
+
"div, span, p, section, li"
|
|
211
|
+
]);
|
|
212
|
+
|
|
213
|
+
function normalizeRecommendFilteredEmptyText(value = "") {
|
|
214
|
+
return normalizeText(value).replace(/\s+/g, "");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function isUsableVisibleBox(box) {
|
|
218
|
+
return Number(box?.rect?.width || 0) > 2 && Number(box?.rect?.height || 0) > 2;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function inspectRecommendEmptyStateAccessibility(client, nodeId, expectedText) {
|
|
222
|
+
if (typeof client?.Accessibility?.getPartialAXTree !== "function") {
|
|
223
|
+
return {
|
|
224
|
+
available: false,
|
|
225
|
+
verified: false,
|
|
226
|
+
reason: "accessibility_unavailable"
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const result = await client.Accessibility.getPartialAXTree({
|
|
231
|
+
nodeId,
|
|
232
|
+
fetchRelatives: true
|
|
233
|
+
});
|
|
234
|
+
const expected = normalizeRecommendFilteredEmptyText(expectedText);
|
|
235
|
+
const nodes = (result?.nodes || []).map((node) => ({
|
|
236
|
+
ignored: node?.ignored === true,
|
|
237
|
+
role: node?.role?.value || "",
|
|
238
|
+
name: node?.name?.value || "",
|
|
239
|
+
value: node?.value?.value || ""
|
|
240
|
+
}));
|
|
241
|
+
const match = nodes.find((node) => (
|
|
242
|
+
!node.ignored
|
|
243
|
+
&& [node.name, node.value].some((value) => (
|
|
244
|
+
normalizeRecommendFilteredEmptyText(value) === expected
|
|
245
|
+
))
|
|
246
|
+
));
|
|
247
|
+
return {
|
|
248
|
+
available: true,
|
|
249
|
+
verified: Boolean(match),
|
|
250
|
+
reason: match ? "exact_accessible_text" : "exact_accessible_text_missing",
|
|
251
|
+
match: match || null,
|
|
252
|
+
node_count: nodes.length
|
|
253
|
+
};
|
|
254
|
+
} catch (error) {
|
|
255
|
+
return {
|
|
256
|
+
available: true,
|
|
257
|
+
verified: false,
|
|
258
|
+
reason: "accessibility_probe_failed",
|
|
259
|
+
error: error?.message || String(error)
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export async function inspectRecommendFilteredEmptyState(client, rootNodeId, {
|
|
265
|
+
selectors = RECOMMEND_FILTERED_EMPTY_TEXT_SCAN_SELECTORS,
|
|
266
|
+
exactTexts = RECOMMEND_FILTERED_EMPTY_TEXTS,
|
|
267
|
+
maxNodes = 2500
|
|
268
|
+
} = {}) {
|
|
269
|
+
if (!client || !rootNodeId) {
|
|
270
|
+
return {
|
|
271
|
+
verified: false,
|
|
272
|
+
reason: "missing_client_or_root",
|
|
273
|
+
text: null,
|
|
274
|
+
node_id: null
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const expectedTexts = new Set(
|
|
278
|
+
(exactTexts || []).map(normalizeRecommendFilteredEmptyText).filter(Boolean)
|
|
279
|
+
);
|
|
280
|
+
const selectorCounts = {};
|
|
281
|
+
const queryErrors = [];
|
|
282
|
+
const nodeIds = [];
|
|
283
|
+
for (const selector of selectors || []) {
|
|
284
|
+
try {
|
|
285
|
+
const matches = await querySelectorAll(client, rootNodeId, selector);
|
|
286
|
+
selectorCounts[selector] = matches.length;
|
|
287
|
+
nodeIds.push(...matches);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
selectorCounts[selector] = null;
|
|
290
|
+
queryErrors.push({
|
|
291
|
+
selector,
|
|
292
|
+
error: error?.message || String(error)
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const uniqueNodeIds = Array.from(new Set(nodeIds.filter(Boolean)))
|
|
298
|
+
.slice(0, Math.max(0, Number(maxNodes) || 0));
|
|
299
|
+
let checkedNodeCount = 0;
|
|
300
|
+
for (const nodeId of uniqueNodeIds) {
|
|
301
|
+
let text = "";
|
|
302
|
+
try {
|
|
303
|
+
text = normalizeRecommendFilteredEmptyText(htmlToText(await getOuterHTML(client, nodeId)));
|
|
304
|
+
} catch {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (!expectedTexts.has(text)) continue;
|
|
308
|
+
checkedNodeCount += 1;
|
|
309
|
+
let box = null;
|
|
310
|
+
try {
|
|
311
|
+
box = await getNodeBox(client, nodeId);
|
|
312
|
+
} catch {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (!isUsableVisibleBox(box)) continue;
|
|
316
|
+
const accessibility = await inspectRecommendEmptyStateAccessibility(client, nodeId, text);
|
|
317
|
+
if (accessibility.verified !== true) continue;
|
|
318
|
+
return {
|
|
319
|
+
verified: true,
|
|
320
|
+
reason: "exact_visible_filtered_empty_state",
|
|
321
|
+
text,
|
|
322
|
+
node_id: nodeId,
|
|
323
|
+
box: {
|
|
324
|
+
x: box.rect.x,
|
|
325
|
+
y: box.rect.y,
|
|
326
|
+
width: box.rect.width,
|
|
327
|
+
height: box.rect.height
|
|
328
|
+
},
|
|
329
|
+
accessibility,
|
|
330
|
+
selector_counts: selectorCounts,
|
|
331
|
+
checked_node_count: checkedNodeCount,
|
|
332
|
+
candidate_node_count: uniqueNodeIds.length,
|
|
333
|
+
query_errors: queryErrors.slice(0, 20)
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
verified: false,
|
|
339
|
+
reason: checkedNodeCount > 0
|
|
340
|
+
? "exact_empty_text_not_visible_or_accessible"
|
|
341
|
+
: "exact_empty_text_not_found",
|
|
342
|
+
text: null,
|
|
343
|
+
node_id: null,
|
|
344
|
+
selector_counts: selectorCounts,
|
|
345
|
+
checked_node_count: checkedNodeCount,
|
|
346
|
+
candidate_node_count: uniqueNodeIds.length,
|
|
347
|
+
query_errors: queryErrors.slice(0, 20)
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export function isVerifiedRecommendRefreshExhaustion({
|
|
352
|
+
cardCount = 0,
|
|
353
|
+
filter = {},
|
|
354
|
+
filterResult = null,
|
|
355
|
+
pageScopeResult = null,
|
|
356
|
+
currentCityOnlyResult = null,
|
|
357
|
+
emptyState = null
|
|
358
|
+
} = {}) {
|
|
359
|
+
if (
|
|
360
|
+
Number(cardCount) !== 0
|
|
361
|
+
|| pageScopeResult?.selected !== true
|
|
362
|
+
|| emptyState?.verified !== true
|
|
363
|
+
) return false;
|
|
364
|
+
const filterEnabled = filter?.enabled !== false;
|
|
365
|
+
if (filterEnabled && !isVerifiedRecommendFilterApplication(
|
|
366
|
+
filterResult,
|
|
367
|
+
buildRecommendFilterSelectionOptions(filter)
|
|
368
|
+
)) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
const currentCityOnlyRequested = filter?.currentCityOnly === true || filter?.current_city_only === true;
|
|
372
|
+
if (currentCityOnlyRequested && currentCityOnlyResult?.effective !== true) return false;
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
140
375
|
|
|
141
376
|
function safeDiagnosticText(value, maxLength = 4000) {
|
|
142
377
|
const text = String(value ?? "");
|
|
@@ -216,8 +451,8 @@ export function isRetryableRecommendFilterReapplyError(error) {
|
|
|
216
451
|
current = current?.cause;
|
|
217
452
|
}
|
|
218
453
|
const message = messages.join("\n");
|
|
219
|
-
return /Recommend filter panel did not open|Recommend filter trigger was not found|Recommend filter confirm button was not found|No matching recommend filter option|Invalid (?:backend )?node(?:\s*id)?|Node with given id does not exist|No node found for given backend id/i.test(message);
|
|
220
|
-
}
|
|
454
|
+
return /Recommend filter panel did not open|Recommend filter trigger was not found|Recommend filter confirm button was not found|Recommend filter application was not verified|No matching recommend filter option|Invalid (?:backend )?node(?:\s*id)?|Node with given id does not exist|No node found for given backend id/i.test(message);
|
|
455
|
+
}
|
|
221
456
|
|
|
222
457
|
function compactFilterReapplyError(error) {
|
|
223
458
|
return error?.message || String(error || "Recommend filter reapply failed");
|
|
@@ -371,22 +606,29 @@ export async function selectRecommendJobWithRootRefresh(client, rootState, {
|
|
|
371
606
|
throw wrapped;
|
|
372
607
|
}
|
|
373
608
|
|
|
374
|
-
async function selectAndConfirmRefreshFilter(client, rootState, filterOptions, {
|
|
375
|
-
maxAttempts = 3,
|
|
376
|
-
retryDelayMs = 1500
|
|
377
|
-
|
|
609
|
+
export async function selectAndConfirmRefreshFilter(client, rootState, filterOptions, {
|
|
610
|
+
maxAttempts = 3,
|
|
611
|
+
retryDelayMs = 1500,
|
|
612
|
+
selectFilter = selectAndConfirmFirstSafeFilter
|
|
613
|
+
} = {}) {
|
|
378
614
|
const attempts = [];
|
|
379
615
|
let currentRootState = rootState;
|
|
380
616
|
let lastError = null;
|
|
381
617
|
|
|
382
618
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
383
619
|
try {
|
|
384
|
-
const filter = await
|
|
385
|
-
client,
|
|
386
|
-
currentRootState.iframe.documentNodeId,
|
|
387
|
-
filterOptions
|
|
388
|
-
);
|
|
389
|
-
|
|
620
|
+
const filter = await selectFilter(
|
|
621
|
+
client,
|
|
622
|
+
currentRootState.iframe.documentNodeId,
|
|
623
|
+
filterOptions
|
|
624
|
+
);
|
|
625
|
+
if (!isVerifiedRecommendFilterApplication(filter, filterOptions)) {
|
|
626
|
+
const verificationError = new Error("Recommend filter application was not verified after refresh");
|
|
627
|
+
verificationError.code = "RECOMMEND_FILTER_APPLICATION_UNVERIFIED";
|
|
628
|
+
verificationError.filter_result = filter;
|
|
629
|
+
throw verificationError;
|
|
630
|
+
}
|
|
631
|
+
attempts.push({
|
|
390
632
|
ok: true,
|
|
391
633
|
method: "filter_reapply",
|
|
392
634
|
attempt
|
|
@@ -576,15 +818,28 @@ async function applyRefreshMethod(client, method, {
|
|
|
576
818
|
});
|
|
577
819
|
currentCityOnlyResult = filterStages.current_city_only;
|
|
578
820
|
filterResult = filterStages.filter;
|
|
579
|
-
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
580
|
-
timeoutMs: cardTimeoutMs,
|
|
581
|
-
intervalMs: 500
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
821
|
+
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
822
|
+
timeoutMs: cardTimeoutMs,
|
|
823
|
+
intervalMs: 500
|
|
824
|
+
});
|
|
825
|
+
const emptyState = cardNodeIds.length === 0
|
|
826
|
+
? await inspectRecommendFilteredEmptyState(client, currentRootState.iframe.documentNodeId)
|
|
827
|
+
: null;
|
|
828
|
+
const exhausted = isVerifiedRecommendRefreshExhaustion({
|
|
829
|
+
cardCount: cardNodeIds.length,
|
|
830
|
+
filter,
|
|
831
|
+
filterResult,
|
|
832
|
+
pageScopeResult,
|
|
833
|
+
currentCityOnlyResult,
|
|
834
|
+
emptyState
|
|
835
|
+
});
|
|
836
|
+
if (!cardNodeIds.length && !exhausted) {
|
|
837
|
+
throw new Error("No recommend candidate cards were found after refresh reload");
|
|
838
|
+
}
|
|
839
|
+
return {
|
|
840
|
+
ok: true,
|
|
841
|
+
exhausted,
|
|
842
|
+
reason: exhausted ? "filtered_list_exhausted" : null,
|
|
588
843
|
method,
|
|
589
844
|
target_url: method === "page_navigate" ? (targetUrl || RECOMMEND_TARGET_URL) : null,
|
|
590
845
|
job_selection: jobSelection,
|
|
@@ -593,10 +848,11 @@ async function applyRefreshMethod(client, method, {
|
|
|
593
848
|
page_scope: pageScopeResult,
|
|
594
849
|
current_city_only: currentCityOnlyResult,
|
|
595
850
|
current_city_only_attempts: currentCityOnlyAttempts,
|
|
596
|
-
filter: filterResult,
|
|
597
|
-
filter_reapply_attempts: filterReapplyAttempts,
|
|
598
|
-
card_count: cardNodeIds.length,
|
|
599
|
-
|
|
851
|
+
filter: filterResult,
|
|
852
|
+
filter_reapply_attempts: filterReapplyAttempts,
|
|
853
|
+
card_count: cardNodeIds.length,
|
|
854
|
+
empty_state: emptyState,
|
|
855
|
+
root_state: currentRootState,
|
|
600
856
|
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false),
|
|
601
857
|
elapsed_ms: Date.now() - started
|
|
602
858
|
};
|
|
@@ -696,24 +952,53 @@ export async function refreshRecommendListAtEnd(client, {
|
|
|
696
952
|
});
|
|
697
953
|
currentCityOnlyResult = filterStages.current_city_only;
|
|
698
954
|
filterResult = filterStages.filter;
|
|
699
|
-
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
700
|
-
timeoutMs: cardTimeoutMs,
|
|
701
|
-
intervalMs: 500
|
|
702
|
-
});
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
955
|
+
const cardNodeIds = await waitForRecommendCardNodeIds(client, currentRootState.iframe.documentNodeId, {
|
|
956
|
+
timeoutMs: cardTimeoutMs,
|
|
957
|
+
intervalMs: 500
|
|
958
|
+
});
|
|
959
|
+
const emptyState = cardNodeIds.length === 0
|
|
960
|
+
? await inspectRecommendFilteredEmptyState(client, currentRootState.iframe.documentNodeId)
|
|
961
|
+
: null;
|
|
962
|
+
const exhausted = isVerifiedRecommendRefreshExhaustion({
|
|
963
|
+
cardCount: cardNodeIds.length,
|
|
964
|
+
filter,
|
|
965
|
+
filterResult,
|
|
966
|
+
pageScopeResult,
|
|
967
|
+
currentCityOnlyResult,
|
|
968
|
+
emptyState
|
|
969
|
+
});
|
|
970
|
+
if (cardNodeIds.length > 0 || exhausted) {
|
|
971
|
+
return {
|
|
972
|
+
ok: true,
|
|
973
|
+
exhausted,
|
|
974
|
+
reason: exhausted ? "filtered_list_exhausted" : null,
|
|
975
|
+
method: "end_refresh_button",
|
|
976
|
+
attempts,
|
|
977
|
+
page_scope: pageScopeResult,
|
|
978
|
+
current_city_only: currentCityOnlyResult,
|
|
979
|
+
current_city_only_attempts: currentCityOnlyAttempts,
|
|
980
|
+
filter: filterResult,
|
|
981
|
+
filter_reapply_attempts: filterReapplyAttempts,
|
|
982
|
+
card_count: cardNodeIds.length,
|
|
983
|
+
empty_state: emptyState,
|
|
984
|
+
root_state: currentRootState,
|
|
985
|
+
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false)
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
attempts.push({
|
|
989
|
+
ok: false,
|
|
990
|
+
method: "end_refresh_button_after_click",
|
|
991
|
+
reason: "end_refresh_empty_state_unverified",
|
|
992
|
+
page_scope: pageScopeResult,
|
|
993
|
+
current_city_only: currentCityOnlyResult,
|
|
994
|
+
current_city_only_attempts: currentCityOnlyAttempts,
|
|
995
|
+
filter: filterResult,
|
|
996
|
+
filter_reapply_attempts: filterReapplyAttempts,
|
|
997
|
+
card_count: 0,
|
|
998
|
+
empty_state: emptyState,
|
|
999
|
+
forced_recent_not_view: Boolean(forceRecentNotView && filter.enabled !== false)
|
|
1000
|
+
});
|
|
1001
|
+
} catch (error) {
|
|
717
1002
|
attempts.push({
|
|
718
1003
|
ok: false,
|
|
719
1004
|
method: "end_refresh_button_after_click",
|