@sovovs/bycli 2.1.29 → 2.1.30
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/clis/juejin/search.js +26 -1
- package/dist/src/output.js +26 -5
- package/package.json +1 -1
package/clis/juejin/search.js
CHANGED
|
@@ -26,6 +26,21 @@ const PAGE_SIZE = 20;
|
|
|
26
26
|
const MAX_LIMIT = 200;
|
|
27
27
|
const MAX_PAGES = 30;
|
|
28
28
|
|
|
29
|
+
// 掘金对**非 0 的时间窗**(search_type=1/2/3)会间歇性返回空首页:同一 query
|
|
30
|
+
// 连打 6 次,2-5 次拿到 `data: []` 而 err_no 仍是 0/success,只有 cursor 里的
|
|
31
|
+
// 实例标识不同——像是部分后端分片对时间窗查询答不出来。实测 period=week 命中
|
|
32
|
+
// 率只有 1/6 ~ 2/6,但 4 次以内总能拿到数据。
|
|
33
|
+
//
|
|
34
|
+
// 真实无结果(如 query="鿃鿄鿅鿆")连打 8 次稳定是 0,所以重试只会救回抖动,
|
|
35
|
+
// 不会把空态变成假数据。只重试**首页且一行都没拿到**的情形:翻页途中的空页
|
|
36
|
+
// 本来就是正常的终止信号。
|
|
37
|
+
const EMPTY_RETRY_ATTEMPTS = 4;
|
|
38
|
+
const EMPTY_RETRY_DELAY_MS = 400;
|
|
39
|
+
|
|
40
|
+
function delay(ms) {
|
|
41
|
+
return new Promise((resolve) => { setTimeout(resolve, ms); });
|
|
42
|
+
}
|
|
43
|
+
|
|
29
44
|
const TYPES = {
|
|
30
45
|
all: 0,
|
|
31
46
|
article: 2,
|
|
@@ -308,9 +323,18 @@ cli({
|
|
|
308
323
|
|
|
309
324
|
while (rows.length < limit && pages < MAX_PAGES) {
|
|
310
325
|
const url = buildUrl({ query, idType, sortType, period, cursor });
|
|
311
|
-
|
|
326
|
+
let payload = await fetchPage(url);
|
|
312
327
|
pages += 1;
|
|
313
328
|
|
|
329
|
+
// 首页空 → 可能是上面说的分片抖动,有界重试;真实空态重试后仍是空。
|
|
330
|
+
if (rows.length === 0 && payload.data.length === 0) {
|
|
331
|
+
for (let attempt = 1; attempt < EMPTY_RETRY_ATTEMPTS; attempt += 1) {
|
|
332
|
+
await delay(EMPTY_RETRY_DELAY_MS);
|
|
333
|
+
payload = await fetchPage(url);
|
|
334
|
+
if (payload.data.length > 0) break;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
314
338
|
for (const entry of payload.data) {
|
|
315
339
|
const normalized = normalizeEntry(entry);
|
|
316
340
|
if (!normalized) continue;
|
|
@@ -357,6 +381,7 @@ export const __test__ = {
|
|
|
357
381
|
PAGE_SIZE,
|
|
358
382
|
MAX_LIMIT,
|
|
359
383
|
MAX_PAGES,
|
|
384
|
+
EMPTY_RETRY_ATTEMPTS,
|
|
360
385
|
requireQuery,
|
|
361
386
|
requireChoice,
|
|
362
387
|
requireLimit,
|
package/dist/src/output.js
CHANGED
|
@@ -10,6 +10,26 @@ function normalizeRows(data) {
|
|
|
10
10
|
return [data];
|
|
11
11
|
return [{ value: data }];
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Render one cell for the flat text formats (table / plain / md / csv).
|
|
15
|
+
*
|
|
16
|
+
* Adapters occasionally expose a structured column (e.g. juejin/search's
|
|
17
|
+
* per-kind `extra`), which `String(v)` would turn into "[object Object]".
|
|
18
|
+
* JSON is lossless and still one line, so it degrades gracefully.
|
|
19
|
+
*/
|
|
20
|
+
function formatCell(v) {
|
|
21
|
+
if (v === null || v === undefined)
|
|
22
|
+
return '';
|
|
23
|
+
if (typeof v === 'object') {
|
|
24
|
+
try {
|
|
25
|
+
return JSON.stringify(v);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return String(v);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return String(v);
|
|
32
|
+
}
|
|
13
33
|
function resolveColumns(rows, opts) {
|
|
14
34
|
return opts.columns ?? Object.keys(rows[0] ?? {});
|
|
15
35
|
}
|
|
@@ -63,8 +83,7 @@ function renderTable(data, opts) {
|
|
|
63
83
|
});
|
|
64
84
|
for (const row of rows) {
|
|
65
85
|
table.push(columns.map(c => {
|
|
66
|
-
|
|
67
|
-
return v === null || v === undefined ? '' : String(v);
|
|
86
|
+
return formatCell(row[c]);
|
|
68
87
|
}));
|
|
69
88
|
}
|
|
70
89
|
console.log();
|
|
@@ -101,7 +120,9 @@ function renderPlain(data, opts) {
|
|
|
101
120
|
}
|
|
102
121
|
}
|
|
103
122
|
rows.forEach((row, index) => {
|
|
104
|
-
const entries = Object.entries(row)
|
|
123
|
+
const entries = Object.entries(row)
|
|
124
|
+
.map(([key, value]) => [key, formatCell(value)])
|
|
125
|
+
.filter(([, value]) => value !== '');
|
|
105
126
|
entries.forEach(([key, value]) => {
|
|
106
127
|
console.log(`${key}: ${value}`);
|
|
107
128
|
});
|
|
@@ -127,7 +148,7 @@ function renderMarkdown(data, opts) {
|
|
|
127
148
|
console.log('| ' + columns.join(' | ') + ' |');
|
|
128
149
|
console.log('| ' + columns.map(() => '---').join(' | ') + ' |');
|
|
129
150
|
for (const row of rows) {
|
|
130
|
-
console.log('| ' + columns.map(c =>
|
|
151
|
+
console.log('| ' + columns.map(c => formatCell(row[c])).join(' | ') + ' |');
|
|
131
152
|
}
|
|
132
153
|
}
|
|
133
154
|
function renderCsv(data, opts) {
|
|
@@ -138,7 +159,7 @@ function renderCsv(data, opts) {
|
|
|
138
159
|
console.log(columns.join(','));
|
|
139
160
|
for (const row of rows) {
|
|
140
161
|
console.log(columns.map(c => {
|
|
141
|
-
const v =
|
|
162
|
+
const v = formatCell(row[c]);
|
|
142
163
|
return v.includes(',') || v.includes('"') || v.includes('\n') || v.includes('\r')
|
|
143
164
|
? `"${v.replace(/"/g, '""')}"` : v;
|
|
144
165
|
}).join(','));
|