mikser-io 9.40.2 → 9.40.3

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.
@@ -563,6 +563,18 @@ cannot model is how the entity's own frontmatter would change — that is
563
563
  parsed during import, so an edit that moves `meta.layout` moves the
564
564
  destination too, and this does not see it.
565
565
 
566
+ **Layout sidecars are the one entity answered by a different route.** A
567
+ sidecar never renders, nothing points at it by ref, and no query matches
568
+ it, so all three candidate walks come back empty — yet editing one
569
+ re-renders the whole site. Its dependency is the layout's *input digest*,
570
+ not an edge: `mikser-io-layouts` folds every sidecar script under the
571
+ folder into a single `sharedDigest` carried by every layout's checksum, so
572
+ one sidecar edit moves every layout. `affectedBy` therefore reports every
573
+ destination that rendered through any layout, each attributed to the
574
+ layout it went through. Entities with no layout — a copied asset, a
575
+ `files()` passthrough — stay out, or the answer would be "everything,
576
+ always", which is the same non-answer as "nothing" with the sign flipped.
577
+
566
578
  ### `runtime.provenance`
567
579
 
568
580
  Where a value was **written** — source file, field path, line and column.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mikser-io",
3
- "version": "9.40.2",
3
+ "version": "9.40.3",
4
4
  "description": "<p align=\"center\"> <img src=\"mikser-lockup-stacked.svg\" alt=\"mikser\" width=\"198\" /> </p>",
5
5
  "main": "index.js",
6
6
  "exports": {
package/src/manifest.js CHANGED
@@ -484,6 +484,25 @@ export function createManifest(db) {
484
484
  WHERE refClosure LIKE '%"kind":"query"%'
485
485
  `)
486
486
 
487
+ // Everything that rendered through a layout — the blast radius of a change
488
+ // to a layout SIDECAR.
489
+ //
490
+ // A sidecar is reachable by none of the three routes affectedBy otherwise
491
+ // walks: it never renders, so it has no snapshots; nothing points at it by
492
+ // ref; and no query matches it. Its dependency is the layout's input
493
+ // digest, which is not an edge. So the preview answered "nothing would be
494
+ // affected" for a change that re-renders the entire site — the one
495
+ // direction of wrong that matters, since it says a site-wide edit is safe.
496
+ //
497
+ // The blast radius really is everything: the layouts plugin folds every
498
+ // OTHER sidecar under the folder into each layout's `shared` digest, so any
499
+ // sidecar edit moves every layout's checksum, and every page renders
500
+ // through some layout.
501
+ const stmtSnapshotsWithLayout = db.prepare(`
502
+ SELECT id, destination, refClosure FROM mikser_snapshots
503
+ WHERE refClosure LIKE '%"kind":"layout"%'
504
+ `)
505
+
487
506
  // Snapshots holding a non-query edge that names any of the given keys,
488
507
  // by the name asked for OR by the entity it bound to. Both, for the same
489
508
  // reason skipDecision reads both — a name survives a rename only through
@@ -572,6 +591,28 @@ export function createManifest(db) {
572
591
  }
573
592
 
574
593
  const affected = []
594
+
595
+ // Sidecars take the route above rather than the candidate walk:
596
+ // skipDecision compares against the edges a snapshot recorded, and
597
+ // no snapshot records an edge to a sidecar — it would answer "skip"
598
+ // for every page and hide the whole radius.
599
+ if (entity.type === 'sidecar') {
600
+ for (const row of stmtSnapshotsWithLayout.iterate()) {
601
+ let layoutEdge = null
602
+ try {
603
+ layoutEdge = JSON.parse(row.refClosure ?? '[]').find(e => e.kind === 'layout') ?? null
604
+ } catch { /* unreadable closure — still affected, just unattributed */ }
605
+ affected.push({
606
+ id: row.id,
607
+ destination: row.destination,
608
+ reason: 'ref-changed',
609
+ ...(layoutEdge?.target ? { dependency: layoutEdge.target } : {}),
610
+ why: 'a layout sidecar feeds every layout through its shared digest',
611
+ })
612
+ }
613
+ return affected
614
+ }
615
+
575
616
  for (const { id, destination } of candidates.values()) {
576
617
  // Its own renders: the premise of the question is that this
577
618
  // entity changed, so asking skipDecision — which compares the