@archie/devtools 0.0.40 → 0.0.41

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.
@@ -275,7 +275,31 @@
275
275
  while (fiber && chain.length < MAX_META_CHAIN) {
276
276
  if (fiber.memoizedProps && fiber.memoizedProps.__editorMeta) {
277
277
  var meta = resolveEditorMetaValue(fiber.memoizedProps.__editorMeta)
278
- if (meta) chain.push(meta)
278
+ if (meta) {
279
+ // ARCH-1367 F6 (host-measured incident, 2026-09-06) — `hostIsSelf`:
280
+ // TRUE when this chain entry's fiber renders EXACTLY `el` as its
281
+ // single host element (getSingleHostElementFromFiber), i.e. the
282
+ // usage and the node are the same DOM element — a `<Button>`
283
+ // usage whose whole render is this `<button>`. FALSE for a real
284
+ // ancestor (a `<header>` around it) AND for a usage whose render
285
+ // is a Fragment/array with siblings (`ThemeGlyphs` = two spans):
286
+ // that usage does NOT stand for this node alone. The host walks
287
+ // up from a template root ONLY through `hostIsSelf` entries to
288
+ // find "the element the user sees"; without this flag it climbed
289
+ // by name and could resolve one glyph to the whole `ThemeGlyphs`
290
+ // usage (measured: `const themeIcon = null`). A COPY, so the
291
+ // shared meta object is never mutated.
292
+ var host = null
293
+ try {
294
+ host = getSingleHostElementFromFiber(fiber)
295
+ } catch (hostError) {
296
+ host = null
297
+ }
298
+ var entry = {}
299
+ for (var k in meta) if (Object.prototype.hasOwnProperty.call(meta, k)) entry[k] = meta[k]
300
+ entry.hostIsSelf = host === el
301
+ chain.push(entry)
302
+ }
279
303
  }
280
304
  fiber = fiber["return"]
281
305
  }
@@ -1626,6 +1650,13 @@
1626
1650
  // (`CAPTURE_VIEWPORT` → `VIEWPORT_CAPTURED`). Hosts MUST
1627
1651
  // feature-detect: a frozen earlier inspector has no handler for
1628
1652
  // either message and would never reply.
1653
+ // ARCH-1367 F6 (CR round 3, MAJOR-2) — this build annotates every
1654
+ // `editorMetaChain` entry with `hostIsSelf` and attaches the drop
1655
+ // container's chain to `element-inserted` / arrange-session
1656
+ // inserts. The CDN object is unversioned, so this bit is the ONLY
1657
+ // honest floor a host can check; without it the host refuses a
1658
+ // delete/drop on a kit template root instead of guessing.
1659
+ metaChainHostIsSelf: true,
1629
1660
  rectElementQuery: true,
1630
1661
  // Best-effort by construction — the foreignObject round-trip drops
1631
1662
  // web fonts, cross-origin images and <canvas> pixels, and answers
@@ -3786,7 +3817,68 @@
3786
3817
  return
3787
3818
  }
3788
3819
 
3820
+ // ARCH-1367 F6 — attach the drop container's editor meta chain to a
3821
+ // container descriptor ({selector, nodeId, …}). Shared by the
3822
+ // `element-inserted` relay AND the `arrange-session-changed` relay:
3823
+ // the host builds its insert change from the SESSION snapshot, not
3824
+ // from the one-shot inserted event, so both must carry it. Additive
3825
+ // and fail-open.
3826
+ function attachContainerMetaChain(container) {
3827
+ if (!container || typeof container !== "object") return
3828
+ var containerEl = null
3829
+ if (typeof container.selector === "string" && container.selector) {
3830
+ try {
3831
+ containerEl = _rawDocumentQuerySelector.call(document, container.selector)
3832
+ } catch (selectorError) {
3833
+ containerEl = null
3834
+ }
3835
+ }
3836
+ if (!containerEl && typeof container.nodeId === "string" && container.nodeId) {
3837
+ containerEl = _rawDocumentQuerySelector.call(
3838
+ document,
3839
+ '[data-dnd-node-id="' + container.nodeId.replace(/"/g, '\\"') + '"]'
3840
+ )
3841
+ }
3842
+ if (containerEl) {
3843
+ container.editorMetaChain = collectEditorMetaChain(containerEl)
3844
+ } else {
3845
+ // CR round 4 (archie-app #1820), MAJOR-D — a silent miss here is
3846
+ // indistinguishable, on the host, from an inspector that predates
3847
+ // the chain. Say it, and attach an EMPTY chain so the host can
3848
+ // tell "this inspector tried and could not find the container"
3849
+ // from "no chain at all"; the host fails closed either way.
3850
+ container.editorMetaChain = []
3851
+ inspectorWarn(
3852
+ "[Inspector] drop container not found for meta chain",
3853
+ container.selector || container.nodeId || null
3854
+ )
3855
+ }
3856
+ }
3857
+
3789
3858
  if (detail.type === "element-inserted") {
3859
+ // ARCH-1367 F6 (host-measured incident, 2026-09-06) — the drop
3860
+ // container the DnD runtime reports is the DEEPEST droppable under
3861
+ // the pointer, which for a kit primitive is that primitive's own
3862
+ // TEMPLATE root (`components/ui/button.jsx:8:78:button` when the
3863
+ // drop lands on the theme toggle). The host has no way to see the
3864
+ // USAGE that root stands for (`<IconButton>` in shell-layouts.jsx)
3865
+ // — so it wrote the insert INTO the template, and every button in
3866
+ // the project grew a nested button. The runtime's `element-inserted`
3867
+ // detail carries the container's selector/nodeId only; this is the
3868
+ // one place that can still reach the live element and its fiber
3869
+ // chain, so the chain is attached here — ADDITIVE, the host reads
3870
+ // `container.editorMetaChain` when present and ignores its absence.
3871
+ // Same collector `ELEMENT_SELECTED` uses, so both sides agree.
3872
+ try {
3873
+ attachContainerMetaChain(detail && detail.container)
3874
+ } catch (chainError) {
3875
+ // CR round 5, NIT-5 — a throw must not collapse the "tried and
3876
+ // failed" signal into "no chain at all".
3877
+ if (detail && detail.container && typeof detail.container === "object") {
3878
+ detail.container.editorMetaChain = []
3879
+ }
3880
+ inspectorWarn("[Inspector] element-inserted: container meta chain unavailable", chainError)
3881
+ }
3790
3882
  postMessageToParent({
3791
3883
  type: "ELEMENT_INSERTED",
3792
3884
  payload: detail,
@@ -3795,6 +3887,38 @@
3795
3887
  }
3796
3888
 
3797
3889
  if (detail.type === "arrange-session-changed") {
3890
+ // ARCH-1367 F6 — same chain on every INSERT change of the session
3891
+ // snapshot (this is what the host actually builds its change from).
3892
+ try {
3893
+ var sessionChanges = detail.session && detail.session.changes
3894
+ if (Array.isArray(sessionChanges)) {
3895
+ for (var sc = 0; sc < sessionChanges.length; sc++) {
3896
+ var sessionChange = sessionChanges[sc]
3897
+ if (sessionChange && sessionChange.kind === "insert") {
3898
+ if (!sessionChange.container || typeof sessionChange.container !== "object") {
3899
+ sessionChange.container = { nodeId: sessionChange.containerId || null, selector: null }
3900
+ }
3901
+ if (!sessionChange.container.nodeId && sessionChange.containerId) {
3902
+ sessionChange.container.nodeId = sessionChange.containerId
3903
+ }
3904
+ attachContainerMetaChain(sessionChange.container)
3905
+ }
3906
+ }
3907
+ }
3908
+ } catch (sessionChainError) {
3909
+ try {
3910
+ var scs = detail.session && detail.session.changes
3911
+ if (Array.isArray(scs)) {
3912
+ for (var sci = 0; sci < scs.length; sci++) {
3913
+ var scc = scs[sci]
3914
+ if (scc && scc.kind === "insert" && scc.container && typeof scc.container === "object" && !Array.isArray(scc.container.editorMetaChain)) {
3915
+ scc.container.editorMetaChain = []
3916
+ }
3917
+ }
3918
+ }
3919
+ } catch (ignored) {}
3920
+ inspectorWarn("[Inspector] arrange-session-changed: container meta chain unavailable", sessionChainError)
3921
+ }
3798
3922
  postMessageToParent({
3799
3923
  type: "ARRANGE_SESSION_CHANGED",
3800
3924
  session: detail.session,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archie/devtools",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "description": "DevTools for Archie generated applications - Route synchronization and editor communication",
5
5
  "repository": {
6
6
  "type": "git",