dsh-unlag 1.9.25 → 1.9.27
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/client.js +1 -0
- package/index.js +32 -3
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -245,6 +245,7 @@ window.__ModuleLoader__.load({
|
|
|
245
245
|
network: 'script', // ui-copy-ignore
|
|
246
246
|
parse: 'parse', // ui-copy-ignore
|
|
247
247
|
register: 'plugin', // ui-copy-ignore
|
|
248
|
+
slow: 'script', // ui-copy-ignore
|
|
248
249
|
dependencies: 'covers',
|
|
249
250
|
list: 'list', // ui-copy-ignore
|
|
250
251
|
};
|
package/index.js
CHANGED
|
@@ -31,6 +31,12 @@ export const inject = ['webServer']
|
|
|
31
31
|
|
|
32
32
|
/** A transaction that has not finished in this long is stuck; the press reports it instead of hanging. */
|
|
33
33
|
const RELOAD_TIMEOUT_MS = 20_000
|
|
34
|
+
/**
|
|
35
|
+
* How long a press waits for an answer before it is told the update is still applying. The page
|
|
36
|
+
* gives up on its own request sooner than the update may take, and a press that ends in "did not
|
|
37
|
+
* finish" while the plugin set is still being replaced is a lie the person cannot check.
|
|
38
|
+
*/
|
|
39
|
+
const RELOAD_ANSWER_MS = 10_000
|
|
34
40
|
|
|
35
41
|
|
|
36
42
|
const LOG_PATH = join(homedir(), '.dsh', 'unlag-reload.log')
|
|
@@ -92,6 +98,12 @@ const MAX_STARTUP_ROW_CHARS = 4096
|
|
|
92
98
|
* is work the request itself must not be able to grow without bound.
|
|
93
99
|
*/
|
|
94
100
|
const MAX_BUNDLE_IDS = 128
|
|
101
|
+
/**
|
|
102
|
+
* How long one startup check may spend reading the list. Each address is bounded on its own, but a
|
|
103
|
+
* list of them is not: dozens of slow addresses would otherwise hold a press past the moment the
|
|
104
|
+
* page gives up waiting, and the person would see a button that did nothing.
|
|
105
|
+
*/
|
|
106
|
+
const MAX_VERIFY_MS = 9000
|
|
95
107
|
const MIN_INTERVAL_MS = 1500
|
|
96
108
|
/** The route the app publishes client bundles under; the same key the module table registers. */
|
|
97
109
|
const BUNDLE_ROUTE = '/plugins'
|
|
@@ -1417,7 +1429,7 @@ function parseAsScript(body, name) {
|
|
|
1417
1429
|
* @param fetchBody reads one script address and answers a response-like, or null when it cannot.
|
|
1418
1430
|
* @param rows the startup script addresses, as the next document would request them.
|
|
1419
1431
|
*/
|
|
1420
|
-
export async function verifyStartupRows(graph, fetchBody, rows) {
|
|
1432
|
+
export async function verifyStartupRows(graph, fetchBody, rows, budgetMs = MAX_VERIFY_MS) {
|
|
1421
1433
|
const entries = new Map(
|
|
1422
1434
|
(Array.isArray(graph?.entries) ? graph.entries : [])
|
|
1423
1435
|
.filter((row) => typeof row?.id === 'string' && row.id !== '')
|
|
@@ -1447,7 +1459,11 @@ export async function verifyStartupRows(graph, fetchBody, rows) {
|
|
|
1447
1459
|
const leftOut = new Set()
|
|
1448
1460
|
const listed = new Set()
|
|
1449
1461
|
const bodies = []
|
|
1462
|
+
const began = Date.now()
|
|
1450
1463
|
for (const item of wanted) {
|
|
1464
|
+
if (Date.now() - began > budgetMs) {
|
|
1465
|
+
return { status: 'blocked', reason: 'slow', detail: 'the startup list took longer than this check waits' }
|
|
1466
|
+
}
|
|
1451
1467
|
for (const id of item.ids) if (served.has(id)) listed.add(id)
|
|
1452
1468
|
for (const id of item.ids) if (!served.has(id)) leftOut.add(id)
|
|
1453
1469
|
let response = null
|
|
@@ -1595,6 +1611,9 @@ function serveButton(ctx, controller, config) {
|
|
|
1595
1611
|
} catch (error) {
|
|
1596
1612
|
verdict = { status: 'blocked', reason: 'list', detail: String(error?.message ?? error) }
|
|
1597
1613
|
}
|
|
1614
|
+
if (verdict?.status !== 'ok') {
|
|
1615
|
+
log(`a startup check refused the page: ${String(verdict?.reason ?? 'list')} (${String(verdict?.detail ?? 'no detail')})`)
|
|
1616
|
+
}
|
|
1598
1617
|
if (Array.isArray(verdict?.leftOut) && verdict.leftOut.length > 0) {
|
|
1599
1618
|
log(`a startup row names a plugin this app no longer serves: ${verdict.leftOut.join(', ')}; the page comes up without it`)
|
|
1600
1619
|
}
|
|
@@ -1654,8 +1673,18 @@ function serveButton(ctx, controller, config) {
|
|
|
1654
1673
|
if (method !== 'POST') return sendJson(res, 405, { status: 'method' })
|
|
1655
1674
|
if (buttonAllowed(req) === false) return sendJson(res, 403, { status: 'forbidden' })
|
|
1656
1675
|
if (!settings.reloadPlugins) return sendJson(res, 200, { status: 'off' })
|
|
1657
|
-
const
|
|
1658
|
-
|
|
1676
|
+
const limit = Number(process.env.DSH_UNLAG_ANSWER_MS ?? '') || RELOAD_ANSWER_MS
|
|
1677
|
+
let timer = null
|
|
1678
|
+
const stillApplying = new Promise((resolve) => {
|
|
1679
|
+
timer = setTimeout(() => resolve({ status: 'applying', warnings: [], pending: [], restartReason: null, failedEntries: [] }), limit)
|
|
1680
|
+
})
|
|
1681
|
+
try {
|
|
1682
|
+
const outcome = await Promise.race([controller.reloadNow(), stillApplying])
|
|
1683
|
+
if (outcome?.status === 'applying') log('the update outlasted the answer the page waits for: the press is told it is still applying')
|
|
1684
|
+
sendJson(res, 200, outcome)
|
|
1685
|
+
} finally {
|
|
1686
|
+
if (timer !== null) clearTimeout(timer)
|
|
1687
|
+
}
|
|
1659
1688
|
} catch (error) {
|
|
1660
1689
|
log(`cannot apply a plugin reload: ${error?.message ?? error}`)
|
|
1661
1690
|
sendJson(res, 500, { status: 'failed', detail: String(error?.message ?? error) })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-unlag",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.27",
|
|
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",
|