dsh-unlag 1.9.14 → 1.9.16

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/README.md +2 -1
  2. package/index.js +26 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -68,7 +68,8 @@ dsh plugin install dsh-unlag
68
68
  ## 排错
69
69
 
70
70
  - 日志:`~/.dsh/unlag-reload.log`,换代码/跳过/被拦各一行,含原因。
71
- - 状态:`GET /dsh-unlag/state` → `{"plugins":true,"page":true,"shim":{...}}`。
71
+ - 状态:`GET /dsh-unlag/state` → `{"version":"1.9.14","code":"<index.js 写入时间>","plugins":true,"page":true,"shim":{...}}`。`code` 用来分清「版本号没刷」和「旧代码真的还在跑」。
72
+ - 重建后的日志带起页面代次:`interface rebuilt; the page runs the plugins as served now (page <rev>)`,据此判断新文档加载的是新脚本还是旧脚本。
72
73
 
73
74
  ## 测试
74
75
 
package/index.js CHANGED
@@ -83,6 +83,8 @@ const TEST_PATH = /(^|\/)(test|tests|__tests__|__mocks__)(\/|$)|[.-](test|spec)\
83
83
  const DEFAULT_ROOTS = [join(homedir(), '.dsh', 'plugins')]
84
84
  /** A turn that never goes idle must not turn this into an endless poll. */
85
85
  const MAX_DEFERRALS = 40
86
+ const MAX_STARTUP_ROWS = 64
87
+ const MAX_STARTUP_ROW_CHARS = 4096
86
88
  const MIN_INTERVAL_MS = 1500
87
89
  /** The route the app publishes client bundles under; the same key the module table registers. */
88
90
  const BUNDLE_ROUTE = '/plugins'
@@ -1131,6 +1133,7 @@ function serveBundlesAtCurrentRevision(ctx) {
1131
1133
  const ask = async (url) => {
1132
1134
  let held = null
1133
1135
  let body
1136
+ let failed = false
1134
1137
  if (url !== originalUrl) {
1135
1138
  try {
1136
1139
  req.url = url
@@ -1159,6 +1162,7 @@ function serveBundlesAtCurrentRevision(ctx) {
1159
1162
  try {
1160
1163
  await Promise.resolve(original(req, res))
1161
1164
  } catch (error) {
1165
+ failed = true
1162
1166
  log(`cannot answer a bundle request: ${error?.message ?? error}`)
1163
1167
  } finally {
1164
1168
  try {
@@ -1168,12 +1172,26 @@ function serveBundlesAtCurrentRevision(ctx) {
1168
1172
  /* the response object owns its own shape */
1169
1173
  }
1170
1174
  }
1171
- return { held, body }
1175
+ return { held, body, failed }
1172
1176
  }
1173
1177
  // The table's own answer first: it still holds the generation this page booted from.
1174
1178
  let outcome = await ask(originalUrl)
1175
1179
  if (outcome.held !== null && retargeted !== null) outcome = await ask(retargeted)
1176
- if (outcome.held === null) return undefined
1180
+ if (outcome.held === null) {
1181
+ /**
1182
+ * The table started writing and then failed. A body that already sent its status cannot take
1183
+ * another one, and a socket left open is a document that waits for a script forever; ending it
1184
+ * lets the page see the failure and carry on.
1185
+ */
1186
+ if (outcome.failed === true) {
1187
+ try {
1188
+ if (res.headersSent === true && res.writableEnded !== true && res.destroyed !== true) realEnd.call(res)
1189
+ } catch {
1190
+ /* the response is gone; nothing left to answer */
1191
+ }
1192
+ }
1193
+ return undefined
1194
+ }
1177
1195
  // The shim resolves the set from the address the page asked for, not the retried one.
1178
1196
  try {
1179
1197
  req.url = originalUrl
@@ -1323,9 +1341,14 @@ export async function verifyStartupRows(graph, fetchBody, rows) {
1323
1341
  .filter((row) => typeof row?.id === 'string' && row.id !== '')
1324
1342
  .map((row) => [row.id, row]),
1325
1343
  )
1344
+ // A startup list is a handful of addresses. Anything past that bound is not a list this app could
1345
+ // boot from, and reading it would turn one request into unbounded work on the process that owns
1346
+ // every running turn.
1347
+ const offered = Array.isArray(rows) ? rows.slice(0, MAX_STARTUP_ROWS) : []
1326
1348
  const wanted = []
1327
- for (const row of Array.isArray(rows) ? rows : []) {
1349
+ for (const row of offered) {
1328
1350
  if (typeof row !== 'string' || row === '') continue
1351
+ if (row.length > MAX_STARTUP_ROW_CHARS) continue
1329
1352
  const path = row.includes('://') ? row.replace(/^[a-z]+:\/\/[^/]+/iu, '') : row
1330
1353
  const url = path.startsWith('/') ? path : `/${path.replace(/^\.?\//u, '')}`
1331
1354
  const request = bundleRequest(url)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-unlag",
3
- "version": "1.9.14",
3
+ "version": "1.9.16",
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",