mikser-io 9.97.0 → 9.99.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.
@@ -385,9 +385,21 @@ the content: an upgraded renderer, a changed helper, a dependency that shifted
385
385
  under the build.
386
386
 
387
387
  Only entities that actually rendered can drift, so an ordinary build reports
388
- on what moved. Under `--force` everything re-renders with unchanged inputs,
389
- which makes it a full sweep **`mikser --force` after a package upgrade is
390
- the regression check**.
388
+ on what moved. Under `--force` every ENTITY re-renders with unchanged inputs,
389
+ which makes it the check for a renderer, helper or dependency that shifted
390
+ **`mikser --force` after a package upgrade is the regression check for
391
+ rendered output**.
392
+
393
+ It is not a full sweep, and calling it one was wrong. Derivatives are outside
394
+ it: an asset is re-derived when its source changes or its preset's `revision`
395
+ moves, and `--force` is neither — so on a site with image presets, `--force`
396
+ re-renders the documents and touches no derivative at all. A sharp upgrade,
397
+ which is exactly the kind of dependency shift this paragraph promises to
398
+ catch, is invisible to it.
399
+
400
+ Use `--render-presets` for that half, and `--fingerprint` before and after to
401
+ compare both halves at once — it hashes the derivatives too, which is the
402
+ thing `find out -type f` cannot do.
391
403
 
392
404
  ### The rest, briefly
393
405
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mikser-io",
3
- "version": "9.97.0",
3
+ "version": "9.99.0",
4
4
  "files": [
5
5
  "app.js",
6
6
  "index.js",
package/src/engine.js CHANGED
@@ -579,11 +579,16 @@ Which check answers which question:
579
579
  with themselves.
580
580
 
581
581
  Did an upgrade change what a render produces?
582
- --force re-renders everything and reports output-drift:
582
+ --force re-renders every ENTITY and reports output-drift:
583
583
  same inputs, different bytes. This is the one that
584
584
  catches a renderer, helper or dependency moving
585
585
  under the build. It also reconciles deletions.
586
586
 
587
+ NOT derivatives. Assets are re-derived on a preset
588
+ revision or a source change, and --force is
589
+ neither — so a sharp upgrade is invisible to it.
590
+ Use --render-presets for that half.
591
+
587
592
  Do the URLs in the output point at anything?
588
593
  (runs every build) reads the emitted html and css and resolves each
589
594
  reference the way a browser would. Reports what
@@ -610,6 +615,15 @@ Which check answers which question:
610
615
  tree, stable across runs. Take it before and after
611
616
  an upgrade and compare.
612
617
 
618
+ Is this build one a person is waiting on?
619
+ (automatic) runtime.options.requested is true for a build a
620
+ client asked for — including one forwarded to a
621
+ running instance — and false for a watcher's own
622
+ cycle. An expensive check reads it to stand down in
623
+ the dev loop without standing down forever: an
624
+ instance is ALWAYS in watch mode, so watch alone
625
+ answers the wrong question.
626
+
613
627
  What did this build do, and cost?
614
628
  --json the whole report as one document on stdout, with
615
629
  every warning carrying a stable code, and per-phase
@@ -1473,10 +1487,10 @@ The full version, with what each code means: docs/diagnostics.md`)
1473
1487
  const brokenTargets = await reportBrokenReferences(useLogger())
1474
1488
  await reportMissingAssets(useLogger(), brokenTargets)
1475
1489
 
1476
- // After the cycle, and only under --json. stdout has been kept clear
1477
- // for exactly this (the logger writes to stderr under --json), so the
1478
- // document is the only thing on it and can be piped to jq.
1479
- emitReport()
1490
+ // The report is NOT emitted here any more. This hook is registered
1491
+ // when the engine is imported, so it runs first among finalized hooks
1492
+ // and every plugin's findings would land after the document was
1493
+ // written. runtime.finalize() emits it once every hook has run.
1480
1494
 
1481
1495
  // Non-zero for a one-shot build, so `mikser && mikser --audit-output` cannot
1482
1496
  // pass with every page in the site stale. `exitCode` rather than
package/src/instance.js CHANGED
@@ -399,7 +399,20 @@ function refuseStale(socket, movedFile) {
399
399
  // not put the instance into json mode for everyone.
400
400
  async function withRequestOutput(request, run) {
401
401
  const prior = {}
402
- for (const key of ['json', 'tool', 'tools']) {
402
+ // `requested` is not a flag the client sent — it is the fact that a client
403
+ // sent anything at all.
404
+ //
405
+ // An instance is always in watch or server mode, so a plugin asking "am I
406
+ // in the dev loop" gets `yes` for a build a PERSON just typed and is
407
+ // waiting on. An expensive check that stands down in the dev loop then
408
+ // stands down forever on a project whose documented model is a watcher
409
+ // always up — it never runs, and says nothing, which is the failure this
410
+ // whole surface exists to remove.
411
+ //
412
+ // Set for the duration of the request and restored with the rest, so a
413
+ // watcher's own cycle before or after is unaffected.
414
+ request = { ...request, requested: true }
415
+ for (const key of ['json', 'tool', 'tools', 'requested']) {
403
416
  prior[key] = runtime.options[key]
404
417
  if (request[key]) runtime.options[key] = request[key]
405
418
  }
package/src/report.js CHANGED
@@ -133,6 +133,10 @@ export function resetReport() {
133
133
  // an import here would close the cycle.
134
134
  runtime.resetReport = resetReport
135
135
 
136
+ // And so it can emit the report AFTER every finalized hook, for the same
137
+ // reason and by the same route. See runtime.finalize().
138
+ runtime.emitReport = emitReport
139
+
136
140
  // End of a cycle: stamp it, file it, and wake anyone waiting on it.
137
141
  export function finishCycle() {
138
142
  if (!runtime.state?.cycle || runtime.state.cycle.finishedAt) return
package/src/runtime.js CHANGED
@@ -213,6 +213,23 @@ const runtime = {
213
213
  async finalize(signal) {
214
214
  await this.callHooks(this.hooks.finalize, signal, 'finalize')
215
215
  await this.callHooks(this.hooks.finalized, signal, 'finalized')
216
+
217
+ // The report is the LAST thing that happens, after every hook that
218
+ // could still add to it.
219
+ //
220
+ // It used to be emitted from the engine's own onFinalized — which is
221
+ // registered when the engine module is imported, so it ran FIRST among
222
+ // finalized hooks and every plugin's findings landed after the
223
+ // document was already written. A plugin could print a warning to the
224
+ // console and have it absent from --json, with nothing to suggest the
225
+ // two disagreed.
226
+ //
227
+ // That is not a lint bug or a schemas bug; it is one ordering bug that
228
+ // every plugin inherits, which is why it is fixed here rather than in
229
+ // each of them. A finding raised through logger.warn reaches the
230
+ // report because the report is a VIEW of that stream — and a view has
231
+ // to be taken after the writing stops.
232
+ await this.emitReport?.()
216
233
  },
217
234
 
218
235
  async sync(operation) {