mikser-io 9.74.0 → 9.75.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/package.json +1 -1
- package/src/manifest.js +29 -0
- package/src/server.js +45 -4
package/package.json
CHANGED
package/src/manifest.js
CHANGED
|
@@ -1289,7 +1289,36 @@ onFinalize(async () => {
|
|
|
1289
1289
|
// That made "resolve the collision by deleting the stub" delete the
|
|
1290
1290
|
// homepage, which is the opposite of what the operator asked for and the
|
|
1291
1291
|
// exact operation the new collision reporting invites.
|
|
1292
|
+
// Destinations THIS cycle's surviving renders just wrote.
|
|
1293
|
+
//
|
|
1294
|
+
// Their snapshot rows are not inserted until 3c below, so the
|
|
1295
|
+
// by-destination query cannot see them — the same blind spot 2a already
|
|
1296
|
+
// compensates for in the other direction ("recorded snapshots only
|
|
1297
|
+
// describe PRIOR cycles").
|
|
1298
|
+
//
|
|
1299
|
+
// Without this, renaming a source's extension unlinks the output the new
|
|
1300
|
+
// entity just wrote. `index.md` → `index.yml` keeps the entity NAME and
|
|
1301
|
+
// the destination but changes the id, so one cycle carries a DELETE for
|
|
1302
|
+
// the old id and a RENDER for the new one. The render writes the page,
|
|
1303
|
+
// the delete stages the same destination, nothing surviving claims it
|
|
1304
|
+
// yet, and the file goes — with `Rendered: 1`, a green build and no
|
|
1305
|
+
// warning. A second build does not fix it (nothing changed), nor does
|
|
1306
|
+
// touch (the input hash is the same); only a real content edit re-renders
|
|
1307
|
+
// it. Only --verify ever said so, and renaming an extension is the most
|
|
1308
|
+
// common action there is during a migration.
|
|
1309
|
+
const claimedByThisCycle = new Set()
|
|
1310
|
+
for (const { entity } of renderedEntries) {
|
|
1311
|
+
if (deleted.has(entity.id) || (entity.parent && deleted.has(entity.parent))) continue
|
|
1312
|
+
if (entity.destination) claimedByThisCycle.add(entity.destination)
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1292
1315
|
for (const { destination, reason } of filesToUnlink) {
|
|
1316
|
+
// Written this cycle by an entity that is staying. Keep it, and say
|
|
1317
|
+
// nothing: the snapshot recorded at 3c is this render's own, so there
|
|
1318
|
+
// is no staleness to report — unlike the surviving-snapshot case
|
|
1319
|
+
// below, where the bytes belong to the entity that went away.
|
|
1320
|
+
if (claimedByThisCycle.has(destination)) continue
|
|
1321
|
+
|
|
1293
1322
|
// "Still claimed" means by something that SURVIVES this pass. The
|
|
1294
1323
|
// ids going away here are not just the deleted entities: pagination
|
|
1295
1324
|
// children staged above are removed too, and counting a child's own
|
package/src/server.js
CHANGED
|
@@ -220,13 +220,42 @@ export function setupServer() {
|
|
|
220
220
|
.sendFile(faviconFile)
|
|
221
221
|
})
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
// Why this needs an error handler AND an address() check, rather
|
|
224
|
+
// than trusting the callback:
|
|
225
|
+
//
|
|
226
|
+
// `app.listen(port, cb)` invokes cb even when the bind FAILED —
|
|
227
|
+
// observed with a squatter on the port, cb running with
|
|
228
|
+
// `address() === null` and EADDRINUSE arriving on the error event
|
|
229
|
+
// afterwards. With no error handler, the process logged
|
|
230
|
+
// "Server listening: http://localhost:3779", printed a LAN
|
|
231
|
+
// address, completed the build green, and answered nothing: every
|
|
232
|
+
// request went to whoever actually held the port, which on a dev
|
|
233
|
+
// machine is another project's site. The operator opens the
|
|
234
|
+
// address, sees somebody else's pages, and the log insists they
|
|
235
|
+
// are their own.
|
|
236
|
+
//
|
|
237
|
+
// A server that cannot bind is not a server. --server asks to
|
|
238
|
+
// BECOME the instance, which is exactly the request that cannot be
|
|
239
|
+
// satisfied by carrying on — the same reasoning that makes a second
|
|
240
|
+
// server exit 1 rather than forward.
|
|
241
|
+
await new Promise((resolve, reject) => {
|
|
242
|
+
const httpServer = runtime.options.app.listen(runtime.options.port, function onListening() {
|
|
225
243
|
// Public URL wins for operator-clickable log lines —
|
|
226
244
|
// a reverse-proxy/tunnel/ngrok setup binds locally but
|
|
227
245
|
// is reached externally at runtime.options.url. Fall
|
|
228
246
|
// back to the bind URL when no public origin is set.
|
|
229
|
-
|
|
247
|
+
// The bind is the authority on which port answers, not
|
|
248
|
+
// the port that was asked for. A null address means the
|
|
249
|
+
// listen failed and the error handler below owns it — say
|
|
250
|
+
// nothing here, or the log claims a port it does not have.
|
|
251
|
+
// `this`, not the const above: EventEmitter calls a
|
|
252
|
+
// listener with the emitter, and a synchronous listen
|
|
253
|
+
// reaches here while `httpServer` is still in its
|
|
254
|
+
// temporal dead zone.
|
|
255
|
+
const bound = this?.address?.()
|
|
256
|
+
if (!bound) return
|
|
257
|
+
|
|
258
|
+
const externalUrl = runtime.options.url ?? `http://localhost:${bound.port}`
|
|
230
259
|
logger.info('Server listening: %s', externalUrl)
|
|
231
260
|
|
|
232
261
|
// The addresses that are NOT localhost.
|
|
@@ -246,13 +275,25 @@ export function setupServer() {
|
|
|
246
275
|
const lan = localAddresses()
|
|
247
276
|
if (lan.length) {
|
|
248
277
|
logger.info('Also on: %s', lan
|
|
249
|
-
.map(address => `http://${address}:${
|
|
278
|
+
.map(address => `http://${address}:${bound.port}`)
|
|
250
279
|
.join(' '))
|
|
251
280
|
}
|
|
252
281
|
}
|
|
253
282
|
resolve()
|
|
254
283
|
})
|
|
255
284
|
|
|
285
|
+
httpServer.on('error', (err) => {
|
|
286
|
+
if (err.code === 'EADDRINUSE') {
|
|
287
|
+
reject(new Error(
|
|
288
|
+
`port ${runtime.options.port} is already in use — something else is listening there, `
|
|
289
|
+
+ `and requests to it will reach that instead of this build. `
|
|
290
|
+
+ `Stop it, or pass --server <other port>.`))
|
|
291
|
+
return
|
|
292
|
+
}
|
|
293
|
+
reject(new Error(
|
|
294
|
+
`could not listen on port ${runtime.options.port} — ${err.code ?? err.message}`))
|
|
295
|
+
})
|
|
296
|
+
|
|
256
297
|
// Node caps a single request at 5 minutes (requestTimeout,
|
|
257
298
|
// default 300_000ms), which is not a timeout in the usual
|
|
258
299
|
// sense here — it is an upload size limit expressed in
|