wpops 0.1.0 → 0.2.0

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/lib/batch.js ADDED
@@ -0,0 +1,54 @@
1
+ // 批量请求命令(/wp-json/batch/v1)。
2
+ // 一次提交多条子请求,减少往返。
3
+ import { printJson, dry, parseJson, readJsonFile } from './util.js';
4
+ import { UsageError } from './errors.js';
5
+
6
+ const BATCH = '/wp-json/batch/v1';
7
+
8
+ const NEED =
9
+ 'batch 需要 --requests JSON 数组 或 --requests-file 文件';
10
+
11
+ function readRequests(flags) {
12
+ if (flags.requests === undefined && flags['requests-file'] === undefined) return undefined;
13
+ try {
14
+ const parsed = flags.requests !== undefined
15
+ ? parseJson(flags.requests)
16
+ : readJsonFile(flags['requests-file']);
17
+ // 同时接受裸数组和 {"requests": [...]} 两种写法。
18
+ return Array.isArray(parsed?.requests) ? parsed.requests : parsed;
19
+ } catch (err) {
20
+ throw new UsageError(`${NEED}(${err.message})`);
21
+ }
22
+ }
23
+
24
+ /** POST /batch/v1,body 为 { requests }。 */
25
+ export async function batch(client, flags) {
26
+ const requests = readRequests(flags);
27
+ if (!Array.isArray(requests) || requests.length === 0) {
28
+ throw new UsageError(NEED);
29
+ }
30
+ const BATCH_METHODS = ['POST', 'PUT', 'PATCH', 'DELETE'];
31
+ const bad = requests.find(
32
+ (r) => r && r.method && !BATCH_METHODS.includes(String(r.method).toUpperCase()),
33
+ );
34
+ if (bad) {
35
+ throw new UsageError(`batch 不支持 ${bad.method}:WordPress 批量端点只接受 ${BATCH_METHODS.join('/')}`);
36
+ }
37
+ if (flags['dry-run']) return dry(`批量请求 ${requests.length} 条`);
38
+ const { data } = await client.request('POST', BATCH, { body: { requests } });
39
+ if (flags.json) return printJson(data);
40
+
41
+ const responses = Array.isArray(data?.responses)
42
+ ? data.responses
43
+ : Array.isArray(data)
44
+ ? data
45
+ : [];
46
+ console.log(`批量返回 ${responses.length} 条:`);
47
+ responses.forEach((response, index) => {
48
+ const request = requests[index] || {};
49
+ const status = response?.status ?? request.status ?? '?';
50
+ const method = response?.method ?? request.method ?? '?';
51
+ const path = response?.path ?? request.path ?? '?';
52
+ console.log(` [${status}] ${method} ${path}`);
53
+ });
54
+ }
package/lib/blocks.js ADDED
@@ -0,0 +1,72 @@
1
+ // 可复用区块(wp_block):/wp-json/wp/v2/blocks。
2
+
3
+ import { readFileSync } from 'node:fs';
4
+ import { stripTags, printJson, dry, listRequest } from './util.js';
5
+ import { UsageError } from './errors.js';
6
+
7
+ const BLOCKS = '/wp-json/wp/v2/blocks';
8
+
9
+ export async function listBlocks(client, flags) {
10
+ const query = {
11
+ per_page: flags['per-page'] ?? 20,
12
+ page: flags.page,
13
+ search: flags.search,
14
+ status: flags.status,
15
+ context: 'edit',
16
+ };
17
+ const { data, total } = await listRequest(client, BLOCKS, query, flags);
18
+ if (flags.json) return printJson(data);
19
+ console.log(`区块 ${total ?? data.length} 个:`);
20
+ for (const b of data) {
21
+ console.log(` #${b.id} [${b.status}] ${stripTags(b.title?.raw ?? b.title?.rendered ?? '(无标题)')}`);
22
+ }
23
+ }
24
+
25
+ export async function getBlock(client, id, flags) {
26
+ const { data } = await client.request('GET', `${BLOCKS}/${Number(id)}`, {
27
+ query: { context: flags.context || 'edit' },
28
+ });
29
+ if (flags.json) return printJson(data);
30
+ console.log(`#${data.id} [${data.status}] ${stripTags(data.title?.raw ?? data.title?.rendered ?? '')}`);
31
+ console.log('─'.repeat(44));
32
+ console.log(data.content?.raw ?? stripTags(data.content?.rendered || ''));
33
+ }
34
+
35
+ function blockBody(flags) {
36
+ const body = {};
37
+ if (flags.title !== undefined) body.title = String(flags.title);
38
+ if (flags['from-file'] !== undefined) body.content = readFileSync(String(flags['from-file']), 'utf8');
39
+ else if (flags.content !== undefined) body.content = String(flags.content);
40
+ if (flags.status !== undefined) body.status = String(flags.status);
41
+ return body;
42
+ }
43
+
44
+ export async function createBlock(client, flags) {
45
+ const body = blockBody(flags);
46
+ if (body.title === undefined && body.content === undefined) {
47
+ throw new UsageError('创建区块至少需要 --title 或 --content');
48
+ }
49
+ if (flags['dry-run']) return dry(`创建区块 ${JSON.stringify(body)}`);
50
+ const { data } = await client.request('POST', BLOCKS, { body });
51
+ if (flags.json) return printJson(data);
52
+ console.log(`✓ 已创建区块 #${data.id} [${data.status}]`);
53
+ }
54
+
55
+ export async function updateBlock(client, id, flags) {
56
+ const body = blockBody(flags);
57
+ if (!Object.keys(body).length) {
58
+ throw new UsageError('没有可更新的字段(--title/--content/--from-file/--status)');
59
+ }
60
+ if (flags['dry-run']) return dry(`更新区块 #${id} ${JSON.stringify(body)}`);
61
+ const { data } = await client.request('POST', `${BLOCKS}/${Number(id)}`, { body });
62
+ if (flags.json) return printJson(data);
63
+ console.log(`✓ 已更新区块 #${data.id}`);
64
+ }
65
+
66
+ export async function deleteBlock(client, id, flags) {
67
+ if (flags['dry-run']) return dry(`删除区块 #${id}`);
68
+ if (!flags.force) throw new UsageError('删除区块需要 --force(不可撤销)');
69
+ const { data } = await client.request('DELETE', `${BLOCKS}/${Number(id)}`, { query: { force: 'true' } });
70
+ if (flags.json) return printJson(data);
71
+ console.log(data.deleted ? `✓ 已删除区块 #${id}` : `? 区块 #${id} 未删除`);
72
+ }