@sovovs/bycli 2.1.26 → 2.1.28

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.
@@ -1,5 +1,5 @@
1
1
  import {
2
- AuthRequiredError, CommandExecutionError, EmptyResultError,
2
+ AuthRequiredError, CliError, CommandExecutionError, EmptyResultError, RateLimitedError,
3
3
  } from '@sovovs/bycli/errors';
4
4
  import { buildSecretSet, redactText } from './redact.js';
5
5
 
@@ -16,12 +16,17 @@ function safePhase(error, secrets) {
16
16
  }
17
17
 
18
18
  export function isEligibleArticleFallbackError(error) {
19
- return error instanceof CommandExecutionError || error instanceof EmptyResultError;
19
+ return error instanceof CommandExecutionError
20
+ || error instanceof EmptyResultError
21
+ || (error instanceof CliError && error.code === 'RATE_LIMITED');
20
22
  }
21
23
 
22
24
  export function withMissingFallbackName(operation, error) {
23
25
  const hint = `${error.hint ? `${error.hint} ` : ''}Sogou fallback requires the exact official-account name in --name.`;
24
26
  if (error instanceof EmptyResultError) return new EmptyResultError(operation, hint);
27
+ if (error instanceof CliError && error.code === 'RATE_LIMITED') {
28
+ return new RateLimitedError(error.message, hint);
29
+ }
25
30
  return new CommandExecutionError(error.message, hint);
26
31
  }
27
32
 
@@ -31,11 +36,17 @@ export function combineArticleFallbackErrors({
31
36
  fallbackError,
32
37
  credentials,
33
38
  }) {
34
- if (fallbackError instanceof AuthRequiredError) return fallbackError;
35
39
  const secrets = buildSecretSet(credentials);
36
40
  const primary = safePhase(primaryError, secrets);
37
41
  const fallback = safePhase(fallbackError, secrets);
38
42
  const hint = `Primary (${primary.code}): ${primary.summary}; fallback (${fallback.code}): ${fallback.summary}`;
43
+ if (primaryError instanceof CliError && primaryError.code === 'RATE_LIMITED') {
44
+ return new RateLimitedError(
45
+ 'Weixin article index was rate limited and Sogou fallback failed',
46
+ hint,
47
+ );
48
+ }
49
+ if (fallbackError instanceof AuthRequiredError) return fallbackError;
39
50
  if (primaryError instanceof EmptyResultError && fallbackError instanceof EmptyResultError) {
40
51
  return new EmptyResultError(operation, hint);
41
52
  }
@@ -1,4 +1,4 @@
1
- import { AuthRequiredError, CommandExecutionError } from '@sovovs/bycli/errors';
1
+ import { AuthRequiredError, CommandExecutionError, RateLimitedError } from '@sovovs/bycli/errors';
2
2
  import { buildSecretSet, redactText } from './redact.js';
3
3
 
4
4
  const DOMAIN = 'mp.weixin.qq.com';
@@ -34,8 +34,8 @@ export function mapArticleIndexPayload(payload) {
34
34
  throw new AuthRequiredError(DOMAIN, 'WeChat article-index credentials have expired');
35
35
  }
36
36
  if (ret === 200013 && normalizedMessage === 'freq control') {
37
- throw commandError(
38
- 'was rate limited (ret=200013)',
37
+ throw new RateLimitedError(
38
+ 'WeChat appmsgpublish was rate limited (ret=200013)',
39
39
  'Wait before retrying the WeChat article-index request; repeated retries may extend frequency control.',
40
40
  );
41
41
  }
@@ -122,6 +122,10 @@ function transportError(error, credentials) {
122
122
  const redactedHint = hint ? redactText(hint, secrets) : undefined;
123
123
  if (error instanceof AuthRequiredError && error.domain === DOMAIN
124
124
  && redactedMessage === message && redactedHint === hint) return error;
125
+ if (error instanceof RateLimitedError) {
126
+ if (redactedMessage === message && redactedHint === hint) return error;
127
+ return new RateLimitedError(redactedMessage, redactedHint);
128
+ }
125
129
  if (isWechatVerificationResponse(redactedMessage, redactedHint)) {
126
130
  return new AuthRequiredError(
127
131
  DOMAIN,
@@ -102,21 +102,52 @@ async function fillField(page, selector, value) {
102
102
  }
103
103
 
104
104
  async function fillContent(page, text) {
105
- return page.evaluate(`(() => {
105
+ var result = await page.evaluate(`(() => {
106
+ var normalize = value => String(value ?? '').replace(/\\r\\n?/g, '\\n').trim();
107
+ var expected = normalize(${JSON.stringify(text)});
108
+ var instances = window.UE && window.UE.instants ? Object.values(window.UE.instants) : [];
109
+ var ueditor = instances.find(instance => instance
110
+ && typeof instance.setContent === 'function'
111
+ && typeof instance.getContentTxt === 'function');
112
+ if (ueditor && typeof ueditor.setContent === 'function' && typeof ueditor.getContentTxt === 'function') {
113
+ ueditor.setContent(${JSON.stringify(text)});
114
+ var ueditorActual = normalize(ueditor.getContentTxt());
115
+ if (ueditorActual === expected) {
116
+ return { ok: true, value: ueditorActual };
117
+ }
118
+ }
106
119
  var editors = document.querySelectorAll('div[contenteditable="true"]');
107
120
  var editor = editors[editors.length - 1];
108
121
  if (!editor) return { ok: false, reason: 'content editor not found' };
109
122
  editor.focus();
110
123
  if (editor.querySelector('[contenteditable="false"]')) editor.innerHTML = '';
111
- document.execCommand('selectAll', false, null);
112
- document.execCommand('insertText', false, ${JSON.stringify(text)});
113
- editor.dispatchEvent(new InputEvent('input', { bubbles: true }));
124
+ var selection = window.getSelection();
125
+ if (!selection) return { ok: false, reason: 'text selection is unavailable' };
126
+ var range = document.createRange();
127
+ range.selectNodeContents(editor);
128
+ selection.removeAllRanges();
129
+ selection.addRange(range);
130
+ return { ok: false, nativeTargetFocused: true };
131
+ })()`);
132
+
133
+ if (!result?.nativeTargetFocused || typeof page.nativeType !== 'function') return result;
134
+
135
+ try {
136
+ await page.nativeType(text);
137
+ } catch {
138
+ return result;
139
+ }
140
+
141
+ return page.evaluate(`(() => {
114
142
  var normalize = value => String(value ?? '').replace(/\\r\\n?/g, '\\n').trim();
115
143
  var expected = normalize(${JSON.stringify(text)});
144
+ var editors = document.querySelectorAll('div[contenteditable="true"]');
145
+ var editor = editors[editors.length - 1];
146
+ if (!editor) return { ok: false, reason: 'content editor not found' };
116
147
  var actual = normalize(editor.innerText ?? editor.textContent ?? '');
117
148
  return actual === expected
118
149
  ? { ok: true, value: actual }
119
- : { ok: false, reason: 'value mismatch', value: actual };
150
+ : { ok: false, reason: 'editor content verification failed', value: actual };
120
151
  })()`);
121
152
  }
122
153
 
@@ -14,7 +14,7 @@
14
14
  * 2 Argument / usage error (ArgumentError)
15
15
  * 66 No input / empty result (EmptyResultError)
16
16
  * 69 Service unavailable (BrowserConnectError, adapter load failures)
17
- * 75 Temporary failure, retry later (TimeoutError) EX_TEMPFAIL
17
+ * 75 Temporary failure, retry later (TimeoutError, RateLimitedError) EX_TEMPFAIL
18
18
  * 77 Permission denied / auth needed (AuthRequiredError)
19
19
  * 78 Configuration error (ConfigError)
20
20
  * 130 Interrupted by Ctrl-C (set by tui.ts SIGINT handler)
@@ -51,6 +51,9 @@ export declare class BrowserConnectError extends CliError {
51
51
  export declare class CommandExecutionError extends CliError {
52
52
  constructor(message: string, hint?: string);
53
53
  }
54
+ export declare class RateLimitedError extends CliError {
55
+ constructor(message: string, hint?: string);
56
+ }
54
57
  export declare class ConfigError extends CliError {
55
58
  constructor(message: string, hint?: string);
56
59
  }
@@ -59,6 +59,11 @@ export class CommandExecutionError extends CliError {
59
59
  super('COMMAND_EXEC', message, hint, EXIT_CODES.GENERIC_ERROR);
60
60
  }
61
61
  }
62
+ export class RateLimitedError extends CliError {
63
+ constructor(message, hint) {
64
+ super('RATE_LIMITED', message, hint, EXIT_CODES.TEMPFAIL);
65
+ }
66
+ }
62
67
  export class ConfigError extends CliError {
63
68
  constructor(message, hint) {
64
69
  super('CONFIG', message, hint, EXIT_CODES.CONFIG_ERROR);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sovovs/bycli",
3
- "version": "2.1.26",
3
+ "version": "2.1.28",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },