dsh-unlag 1.9.113 → 1.9.115

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.
Files changed (3) hide show
  1. package/client.js +2 -2
  2. package/index.js +28 -4
  3. package/package.json +1 -1
package/client.js CHANGED
@@ -15,8 +15,8 @@ window.__ModuleLoader__.load({
15
15
  * repeating one generic sentence.
16
16
  */
17
17
  const REASONS = zh
18
- ? { list: '读不到启动信息', slow: '启动信息读得太慢', row: '启动信息不完整', src: '启动脚本没有地址', graph: '已装插件清单读不到', plugin: '有插件已不在运行', script: '启动脚本读不到', empty: '启动脚本是空的', covers: '启动脚本缺插件', parse: '启动脚本内容不完整', size: '启动脚本太大', verify: '插件后端没有应答', screen: '页面状态读不到' }
19
- : { list: 'startup info is unavailable', slow: 'the startup list answered too slowly', row: 'startup info is incomplete', src: 'a startup script has no address', graph: 'the plugin list cannot be read', plugin: 'a plugin is no longer running', script: 'a startup script cannot be read', empty: 'a startup script is empty', covers: 'a startup script misses a plugin', parse: 'a startup script is incomplete', size: 'a startup script is too large', verify: 'the plugin is not answering', screen: 'the page state cannot be read' };
18
+ ? { list: '读不到启动信息', slow: '启动信息读得太慢', row: '启动信息不完整', src: '启动脚本没有地址', graph: '已装插件清单读不到', plugin: '有插件已不在运行', script: '启动脚本读不到', empty: '启动脚本是空的', covers: '启动脚本缺插件', parse: '启动脚本内容不完整', size: '启动脚本太大', verify: '插件后端没有应答', screen: '页面状态读不到', busy: '另一次刷新还在进行', dependencies: '插件依赖还没有运行', missing: '启动清单里有插件已经不见了', network: '读取插件时出错', register: '插件没有登记自己' }
19
+ : { list: 'startup info is unavailable', slow: 'the startup list answered too slowly', row: 'startup info is incomplete', src: 'a startup script has no address', graph: 'the plugin list cannot be read', plugin: 'a plugin is no longer running', script: 'a startup script cannot be read', empty: 'a startup script is empty', covers: 'a startup script misses a plugin', parse: 'a startup script is incomplete', size: 'a startup script is too large', verify: 'the plugin is not answering', screen: 'the page state cannot be read', busy: 'another rebuild is already running', dependencies: 'a plugin dependency is not running', missing: 'a plugin in the startup list is gone', network: 'the plugins could not be read', register: 'a plugin did not register itself' };
20
20
 
21
21
  /** The last reason a readiness check refused for, or null after a check that passed. */
22
22
  let holdReason = null;
package/index.js CHANGED
@@ -1710,11 +1710,14 @@ export async function verifyStartupRows(graph, fetchBody, rows, budgetMs = MAX_V
1710
1710
  for (const id of item.ids) if (served.has(id)) listed.add(id)
1711
1711
  for (const id of item.ids) if (!served.has(id)) leftOut.add(id)
1712
1712
  let response = null
1713
- try {
1714
- response = await fetchBody(item.url)
1715
- } catch (error) {
1716
- return { status: 'blocked', reason: 'network', detail: `${item.url}: ${String(error?.message ?? error)}` }
1713
+ const read = await withDeadline(() => fetchBody(item.url), budgetMs - (Date.now() - began))
1714
+ if (read.timedOut === true) {
1715
+ return { status: 'blocked', reason: 'slow', detail: `${item.url} did not answer within the time this check waits` }
1716
+ }
1717
+ if ('error' in read) {
1718
+ return { status: 'blocked', reason: 'network', detail: `${item.url}: ${String(read.error?.message ?? read.error)}` }
1717
1719
  }
1720
+ response = read.value
1718
1721
  // Reading the answer is the same kind of step as fetching it: a response whose status or body
1719
1722
  // cannot be read is one this check cannot approve, and saying which script failed beats
1720
1723
  // abandoning the whole list without a name in the reason.
@@ -1822,6 +1825,27 @@ export async function verifyStartup(clientModules, rows) {
1822
1825
  return verifyStartupRows(graph, (url) => readStartupScript(clientModules, url), rows)
1823
1826
  }
1824
1827
 
1828
+ /** The sentinel a read that ran past its time resolves to: a value, never an error. */
1829
+ const LATE = Symbol('late')
1830
+
1831
+ /**
1832
+ * One read the check will not wait past. The budget is counted between rows already; a single
1833
+ * address that never answers would otherwise hold the press past the moment the page stops waiting
1834
+ * for it, and the page would be told the check timed out with nothing naming what hung. Racing the
1835
+ * read keeps the reason the check's own, and the read itself is left to finish with its bytes
1836
+ * dropped, so nothing is left half-read.
1837
+ */
1838
+ async function withDeadline(work, ms) {
1839
+ let timer = null
1840
+ const late = new Promise((resolve) => {
1841
+ timer = setTimeout(() => resolve(LATE), Math.max(0, ms))
1842
+ })
1843
+ const settled = Promise.resolve().then(work).then((value) => ({ value }), (error) => ({ error }))
1844
+ const answer = await Promise.race([settled, late])
1845
+ if (timer !== null) clearTimeout(timer)
1846
+ return answer === LATE ? { timedOut: true } : answer
1847
+ }
1848
+
1825
1849
  /** What one press is told when the update it waits for did not answer in time. */
1826
1850
  const STILL_APPLYING = Object.freeze({ status: 'applying', warnings: [], pending: [], restartReason: null, failedEntries: [] });
1827
1851
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-unlag",
3
- "version": "1.9.113",
3
+ "version": "1.9.115",
4
4
  "type": "module",
5
5
  "description": "A press applies plugin edits right away and brings a laggy interface back; automatic reloading stays off unless you turn it on, so the press is the only trigger, and nothing restarts.",
6
6
  "license": "MIT",