@archie/devtools 0.0.48 → 0.1.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.
@@ -4081,13 +4081,21 @@ function summarizeRuntimeEvent(detail) {
4081
4081
  container: summarizeIdentity(detail.container),
4082
4082
  beforeSibling: summarizeIdentity(detail.beforeSibling),
4083
4083
  sourceContainer: summarizeIdentity(detail.sourceContainer),
4084
- diagnostics: detail.diagnostics ?? null
4084
+ diagnostics: detail.diagnostics ?? null,
4085
+ targetVid: detail.targetVid ?? null,
4086
+ containerVid: detail.containerVid ?? null,
4087
+ index: detail.index
4085
4088
  };
4089
+ case "drag-started":
4090
+ return { targetVid: detail.targetVid, droppableCount: detail.droppable.length };
4091
+ case "drag-refused":
4092
+ return { code: detail.code, cause: detail.cause, vid: detail.vid };
4086
4093
  case "element-resized":
4087
4094
  return {
4088
4095
  operationId: detail.operationId,
4089
4096
  selected: summarizeIdentity(detail.selected),
4090
4097
  anchor: summarizeIdentity(detail.anchor),
4098
+ targetVid: detail.targetVid ?? null,
4091
4099
  resize: {
4092
4100
  mode: detail.resize.mode,
4093
4101
  selectedNodeKey: detail.resize.selectedNodeKey,
@@ -4104,6 +4112,8 @@ function summarizeRuntimeEvent(detail) {
4104
4112
  nodeKey: detail.nodeKey,
4105
4113
  displayName: detail.displayName,
4106
4114
  container: summarizeIdentity(detail.container),
4115
+ containerVid: detail.containerVid ?? null,
4116
+ insertedVid: detail.insertedVid ?? null,
4107
4117
  index: detail.index,
4108
4118
  beforeSibling: summarizeIdentity(detail.beforeSibling),
4109
4119
  hasTsxCode: !!detail.tsxCode,
@@ -4185,6 +4195,58 @@ function isValidLaneContainer(containerId, projection, nodeMap) {
4185
4195
  if (container.children.length === 0) return false;
4186
4196
  return !isStructuralContainer(containerId, nodeMap);
4187
4197
  }
4198
+ function isNonDroppableNodeMeta(meta) {
4199
+ if (!meta) return false;
4200
+ if (meta.containerKind && meta.containerKind !== "plain") return true;
4201
+ if (meta.isChrome) return true;
4202
+ if (meta.isKitOrigin) return true;
4203
+ return false;
4204
+ }
4205
+ function isNonDraggableNodeMeta(meta) {
4206
+ if (!meta) return false;
4207
+ if (meta.dynamicType && meta.dynamicType !== "STATIC") return true;
4208
+ if (meta.isChrome) return true;
4209
+ if (meta.isKitOrigin) return true;
4210
+ return false;
4211
+ }
4212
+ function collectSubtreeIds(rootId, projection) {
4213
+ const ids2 = /* @__PURE__ */ new Set([rootId]);
4214
+ const stack = [rootId];
4215
+ while (stack.length > 0) {
4216
+ const id = stack.pop();
4217
+ const node = projection.containerIndex.get(id);
4218
+ if (!node) continue;
4219
+ for (const childId of node.children) {
4220
+ if (ids2.has(childId)) continue;
4221
+ ids2.add(childId);
4222
+ stack.push(childId);
4223
+ }
4224
+ }
4225
+ return ids2;
4226
+ }
4227
+ function computeDroppableSet(sourceId, projection, nodeMap) {
4228
+ const excluded = collectSubtreeIds(sourceId, projection);
4229
+ const droppable = /* @__PURE__ */ new Set();
4230
+ for (const containerId of projection.containerIndex.keys()) {
4231
+ if (excluded.has(containerId)) continue;
4232
+ if (isStructuralContainer(containerId, nodeMap)) continue;
4233
+ if (isNonDroppableNodeMeta(nodeMap.get(containerId)?.editorMeta)) continue;
4234
+ droppable.add(containerId);
4235
+ }
4236
+ return droppable;
4237
+ }
4238
+ function buildBlockedNodeMetaContainerIds(projection, nodeMap) {
4239
+ const blocked = /* @__PURE__ */ new Set();
4240
+ for (const containerId of projection.containerIndex.keys()) {
4241
+ if (isNonDroppableNodeMeta(nodeMap.get(containerId)?.editorMeta)) {
4242
+ blocked.add(containerId);
4243
+ }
4244
+ }
4245
+ return blocked;
4246
+ }
4247
+ function resolveRefusalCode(meta, fallback) {
4248
+ return meta?.lockReason ?? fallback;
4249
+ }
4188
4250
  function buildBlockedStructuralContainerIds(projection, nodeMap, sourceContainerId) {
4189
4251
  const blocked = /* @__PURE__ */ new Set();
4190
4252
  for (const containerId of projection.containerIndex.keys()) {
@@ -4195,6 +4257,7 @@ function buildBlockedStructuralContainerIds(projection, nodeMap, sourceContainer
4195
4257
  }
4196
4258
 
4197
4259
  // src/client/dnd/design-mode/fiber-bridge.ts
4260
+ import { NodeMetaSchema } from "@8base-archie/editor-protocol";
4198
4261
  var FIBER_KEY_PREFIX = "__reactFiber$";
4199
4262
  var PROPS_KEY_PREFIX = "__reactProps$";
4200
4263
  var metaCache = /* @__PURE__ */ new WeakMap();
@@ -4457,6 +4520,9 @@ function resolveDragSurface(el) {
4457
4520
  };
4458
4521
  }
4459
4522
 
4523
+ // src/types/editor-meta.ts
4524
+ import { VID_PATTERN, VID_ATTR } from "@8base-archie/editor-protocol";
4525
+
4460
4526
  // src/client/dnd/design-mode/node-key.ts
4461
4527
  var _runtimeKeyMap = /* @__PURE__ */ new WeakMap();
4462
4528
  var _runtimeKeyCounter = 0;
@@ -4474,11 +4540,18 @@ function getNodeKeyFromElement(el) {
4474
4540
  const resolved = resolveDragSurface(el);
4475
4541
  const host = resolved.host ?? el;
4476
4542
  const meta = identityMeta(host);
4543
+ if (meta?.vid) return meta.vid;
4544
+ const domVid = host.getAttribute("data-vid");
4545
+ if (domVid) return domVid;
4477
4546
  if (meta) return meta.nodeId;
4478
4547
  const persisted = host.getAttribute("data-dnd-node-id");
4479
4548
  if (persisted) return persisted;
4480
4549
  return getRuntimeKey(host);
4481
4550
  }
4551
+ function resolveVidFromElement(el) {
4552
+ const key2 = getNodeKeyFromElement(el);
4553
+ return VID_PATTERN.test(key2) ? key2 : null;
4554
+ }
4482
4555
  function getInspectorRefFromElement(el) {
4483
4556
  const resolved = resolveDragSurface(el);
4484
4557
  const host = resolved.host ?? el;
@@ -4805,6 +4878,8 @@ var useDesignModeStore = create((set, get) => ({
4805
4878
  afterSibling,
4806
4879
  diagnostics,
4807
4880
  binding,
4881
+ targetVid,
4882
+ containerVid,
4808
4883
  componentName,
4809
4884
  before,
4810
4885
  after
@@ -4832,6 +4907,8 @@ var useDesignModeStore = create((set, get) => ({
4832
4907
  afterSibling: afterSibling ?? null,
4833
4908
  diagnostics: diagnostics ?? null,
4834
4909
  binding: binding ?? null,
4910
+ targetVid: targetVid ?? null,
4911
+ containerVid: containerVid ?? null,
4835
4912
  payload: { before, after },
4836
4913
  at: now
4837
4914
  };
@@ -4860,6 +4937,7 @@ var useDesignModeStore = create((set, get) => ({
4860
4937
  nodeId,
4861
4938
  inspectorRef,
4862
4939
  componentName,
4940
+ targetVid,
4863
4941
  before,
4864
4942
  after
4865
4943
  }) => {
@@ -4871,6 +4949,7 @@ var useDesignModeStore = create((set, get) => ({
4871
4949
  nodeKey,
4872
4950
  nodeId,
4873
4951
  inspectorRef,
4952
+ targetVid: targetVid ?? null,
4874
4953
  payload: { before, after },
4875
4954
  at: now
4876
4955
  };
@@ -4904,6 +4983,8 @@ var useDesignModeStore = create((set, get) => ({
4904
4983
  tsxImports,
4905
4984
  containerId,
4906
4985
  containerNodeKey,
4986
+ containerVid,
4987
+ insertedVid,
4907
4988
  index,
4908
4989
  beforeSiblingInspectorRef,
4909
4990
  beforeSiblingNodeKey
@@ -4922,6 +5003,8 @@ var useDesignModeStore = create((set, get) => ({
4922
5003
  tsxImports: tsxImports ?? null,
4923
5004
  containerId,
4924
5005
  containerNodeKey,
5006
+ containerVid: containerVid ?? null,
5007
+ insertedVid: insertedVid ?? null,
4925
5008
  index,
4926
5009
  beforeSiblingInspectorRef: beforeSiblingInspectorRef ?? null,
4927
5010
  beforeSiblingNodeKey: beforeSiblingNodeKey ?? null,
@@ -5035,6 +5118,7 @@ function shouldShowGrabFeedback(target, scannedByNodeId) {
5035
5118
  }
5036
5119
  function isEligibleForDrag(sc) {
5037
5120
  if (sc.draggable === false) return false;
5121
+ if (isNonDraggableNodeMeta(sc.editorMeta)) return false;
5038
5122
  const source = sc.eligibilitySource ?? "editor-meta";
5039
5123
  return source === "editor-meta" || source === "force-allow" || source === "inferred-sortable-descendant" || // RFC E2.4 P1: single host root of a kit-component usage (MetricCard,
5040
5124
  // Section, …) carrying the usage's projected meta.
@@ -6657,6 +6741,15 @@ function serializeArrangeChange(event, index) {
6657
6741
  diagnostics: event.kind === "move" ? event.diagnostics : void 0,
6658
6742
  // ARCH-1065 D17 — which slice row this move addresses.
6659
6743
  binding: event.kind === "move" ? event.binding : void 0,
6744
+ // `data-vid` identity migration (`host-bridge.ts`'s `RuntimeMoveDetail`
6745
+ // fields). `index` here is `event.payload.after.index` — the MOVE's own
6746
+ // JSX-child index — never the `index` PARAMETER above (this change's
6747
+ // sequence position in the session). `targetVid` also carries the
6748
+ // resized node's vid on a `resize` change; `containerVid` also carries
6749
+ // the drop container's vid on an `insert` change.
6750
+ targetVid: event.kind === "move" || event.kind === "resize" ? event.targetVid : void 0,
6751
+ containerVid: event.kind === "move" || event.kind === "insert" ? event.containerVid : void 0,
6752
+ index: event.kind === "move" ? event.payload.after.index : void 0,
6660
6753
  // Insert-specific fields
6661
6754
  displayName: event.kind === "insert" ? event.displayName : void 0,
6662
6755
  html: event.kind === "insert" ? event.html : void 0,
@@ -6664,6 +6757,7 @@ function serializeArrangeChange(event, index) {
6664
6757
  tsxImports: event.kind === "insert" ? event.tsxImports : void 0,
6665
6758
  containerId: event.kind === "insert" ? event.containerId : void 0,
6666
6759
  containerNodeKey: event.kind === "insert" ? event.containerNodeKey : void 0,
6760
+ insertedVid: event.kind === "insert" ? event.insertedVid : void 0,
6667
6761
  insertIndex: event.kind === "insert" ? event.index : void 0,
6668
6762
  beforeSiblingInspectorRef: event.kind === "insert" ? event.beforeSiblingInspectorRef : void 0,
6669
6763
  beforeSiblingNodeKey: event.kind === "insert" ? event.beforeSiblingNodeKey : void 0,
@@ -6920,6 +7014,7 @@ function useOverlayRefs(undoRequestId, redoRequestId) {
6920
7014
  const resolvedReorderRef = useRef8(null);
6921
7015
  const finalVisualKeyRef = useRef8(null);
6922
7016
  const dragStartPositionRef = useRef8(null);
7017
+ const droppableContainerIdsRef = useRef8(null);
6923
7018
  const sourceCommitLaneRef = useRef8(null);
6924
7019
  const commitLaneCacheRef = useRef8(/* @__PURE__ */ new Map());
6925
7020
  const safeZoneRef = useRef8(null);
@@ -6951,6 +7046,7 @@ function useOverlayRefs(undoRequestId, redoRequestId) {
6951
7046
  resolvedReorderRef,
6952
7047
  finalVisualKeyRef,
6953
7048
  dragStartPositionRef,
7049
+ droppableContainerIdsRef,
6954
7050
  sourceCommitLaneRef,
6955
7051
  commitLaneCacheRef,
6956
7052
  safeZoneRef,
@@ -8152,6 +8248,14 @@ function useDragVisualLoop({
8152
8248
 
8153
8249
  // src/client/dnd/design-mode/useDragLifecycle.ts
8154
8250
  import { useState as useState5, useCallback as useCallback6 } from "react";
8251
+ function emitRefused(code, cause, element) {
8252
+ dispatchRuntimeEvent({
8253
+ type: "drag-refused",
8254
+ code,
8255
+ cause,
8256
+ vid: element ? resolveVidFromElement(element) : null
8257
+ });
8258
+ }
8155
8259
  function resolveLaneAllowedContainers(anchorElement, srcContainerId) {
8156
8260
  if (!srcContainerId) return null;
8157
8261
  if (!resolveRowBinding(anchorElement)) return null;
@@ -8268,6 +8372,10 @@ function recordCommittedMove(args) {
8268
8372
  });
8269
8373
  const componentName = getEditorMeta(anchorEl)?.componentName;
8270
8374
  const operationId = `${command.at}:${anchorNodeId}`;
8375
+ const targetVid = resolveVidFromElement(selectedEl);
8376
+ const targetContainerId = command.to.parentNodeId ?? command.to.laneContainerId;
8377
+ const targetContainerEl = targetContainerId ? resolveNodeElement(targetContainerId) : null;
8378
+ const containerVid = targetContainerEl ? resolveVidFromElement(targetContainerEl) : null;
8271
8379
  const binding = resolveRowBinding(selectedEl);
8272
8380
  const beforePayload = buildMovePayloadWithEffectiveIdentities({
8273
8381
  placement: command.from,
@@ -8299,6 +8407,8 @@ function recordCommittedMove(args) {
8299
8407
  sourceAfterSibling: sourceAfterSiblingIdentity,
8300
8408
  diagnostics,
8301
8409
  binding,
8410
+ targetVid,
8411
+ containerVid,
8302
8412
  container: containerIdentity,
8303
8413
  beforeSibling: beforeSiblingIdentity,
8304
8414
  afterSibling: afterSiblingIdentity,
@@ -8310,6 +8420,9 @@ function recordCommittedMove(args) {
8310
8420
  type: "element-moved",
8311
8421
  operationId,
8312
8422
  commandType: command.type,
8423
+ targetVid,
8424
+ containerVid,
8425
+ index: command.to.index,
8313
8426
  selected: {
8314
8427
  nodeId: selectedNodeId,
8315
8428
  nodeKey: selectedNodeKey,
@@ -8372,6 +8485,7 @@ function useDragLifecycle({
8372
8485
  refs.resolvedReorderRef.current = null;
8373
8486
  refs.finalVisualKeyRef.current = null;
8374
8487
  refs.dragStartPositionRef.current = null;
8488
+ refs.droppableContainerIdsRef.current = null;
8375
8489
  clearCommitLaneCache();
8376
8490
  refs.safeZoneRef.current = null;
8377
8491
  setInsideRect(null);
@@ -8405,6 +8519,11 @@ function useDragLifecycle({
8405
8519
  eligibilitySource: found.eligibilitySource ?? "editor-meta",
8406
8520
  draggable: found.draggable
8407
8521
  });
8522
+ emitRefused(
8523
+ resolveRefusalCode(found.editorMeta, "GATE_REJECT"),
8524
+ "Node is not draggable (dynamic render, chrome, or kit-origin).",
8525
+ found.element
8526
+ );
8408
8527
  return;
8409
8528
  }
8410
8529
  veTrace("dnd", "drag:start", {
@@ -8428,12 +8547,29 @@ function useDragLifecycle({
8428
8547
  clearCommitLaneCache();
8429
8548
  const anchor = resolveDragAnchor(activeId, proj, refs.nodeMapRef.current);
8430
8549
  const anchorScanned = refs.nodeMapRef.current.get(anchor.anchorNodeId) ?? found;
8431
- if (!anchorScanned || !isEligibleForDrag(anchorScanned)) return;
8550
+ if (!anchorScanned || !isEligibleForDrag(anchorScanned)) {
8551
+ emitRefused(
8552
+ resolveRefusalCode(anchorScanned?.editorMeta, "GATE_REJECT"),
8553
+ "Resolved drag anchor is not draggable.",
8554
+ anchorScanned?.element ?? found.element
8555
+ );
8556
+ return;
8557
+ }
8432
8558
  const anchorNodeId = anchor.anchorNodeId;
8433
8559
  const srcContainerId = anchor.sourceContainerId;
8434
8560
  const sourceCommitLane = getCommitLaneSnapshot(srcContainerId);
8435
8561
  const startPosition = getCurrentCommitPlacement(anchorNodeId, srcContainerId);
8436
8562
  const blockedContainerIds = buildBlockedStructuralContainerIds(proj, refs.nodeMapRef.current, srcContainerId);
8563
+ for (const id of buildBlockedNodeMetaContainerIds(proj, refs.nodeMapRef.current)) {
8564
+ blockedContainerIds.add(id);
8565
+ }
8566
+ const droppableSet = computeDroppableSet(anchorNodeId, proj, refs.nodeMapRef.current);
8567
+ refs.droppableContainerIdsRef.current = droppableSet;
8568
+ dispatchRuntimeEvent({
8569
+ type: "drag-started",
8570
+ targetVid: resolveVidFromElement(anchorScanned.element),
8571
+ droppable: Array.from(droppableSet)
8572
+ });
8437
8573
  const engineOptions = buildDragIntentEngineOptions(anchorScanned.element, srcContainerId, blockedContainerIds);
8438
8574
  refs.rowLockedRef.current = !!engineOptions.laneAllowedContainers;
8439
8575
  refs.engineRef.current = createDragIntentEngine(proj, anchorNodeId, engineOptions);
@@ -8506,6 +8642,19 @@ function useDragLifecycle({
8506
8642
  syncHistoryAvailability();
8507
8643
  return;
8508
8644
  }
8645
+ if (dropTargetId && !refs.droppableContainerIdsRef.current?.has(dropTargetId)) {
8646
+ const targetContainerEl = resolveNodeElement(dropTargetId);
8647
+ const targetMeta = refs.nodeMapRef.current.get(dropTargetId)?.editorMeta;
8648
+ const fallbackCode = targetMeta?.containerKind === "expression" ? "EXPRESSION_CONTAINER" : "CONTAINER_NOT_PLAIN";
8649
+ emitRefused(
8650
+ resolveRefusalCode(targetMeta, fallbackCode),
8651
+ "Drop target is outside the droppable set for this node.",
8652
+ targetContainerEl
8653
+ );
8654
+ clearDragState();
8655
+ syncHistoryAvailability();
8656
+ return;
8657
+ }
8509
8658
  let command = null;
8510
8659
  if (nestTarget) {
8511
8660
  if (!activeEl.contains(nestTarget.element) && !nestTarget.element.contains(activeEl) && sourcePos?.commitNodeId) {
@@ -8717,6 +8866,7 @@ function useResizeSession({
8717
8866
  return;
8718
8867
  }
8719
8868
  const inspectorRef = getInspectorRefFromElement(element);
8869
+ const targetVid = resolveVidFromElement(element);
8720
8870
  const resizeCmd = {
8721
8871
  type: "resize",
8722
8872
  mode,
@@ -8736,12 +8886,14 @@ function useResizeSession({
8736
8886
  nodeId: selectedNodeId,
8737
8887
  inspectorRef,
8738
8888
  componentName: getEditorMeta(element)?.componentName,
8889
+ targetVid,
8739
8890
  before: { mode, selectedNodeKey, targetNodeKey, targetNodeId, width: resizeCmd.before.width, height: resizeCmd.before.height, colSpan: resizeCmd.before.colSpan, rowSpan: resizeCmd.before.rowSpan, styleDiff: { before: beforeStyles, after: afterStyles } },
8740
8891
  after: { mode, selectedNodeKey, targetNodeKey, targetNodeId, width: resizeCmd.after.width, height: resizeCmd.after.height, colSpan: resizeCmd.after.colSpan, rowSpan: resizeCmd.after.rowSpan, styleDiff: { before: beforeStyles, after: afterStyles } }
8741
8892
  });
8742
8893
  dispatchRuntimeEvent({
8743
8894
  type: "element-resized",
8744
8895
  operationId,
8896
+ targetVid,
8745
8897
  selected: {
8746
8898
  nodeId: selectedNodeId,
8747
8899
  nodeKey: selectedNodeKey,
@@ -8964,6 +9116,8 @@ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, s
8964
9116
  };
8965
9117
  }
8966
9118
  const operationId = `${insertCommand.at}:${nodeId}`;
9119
+ const containerVid = containerEl ? resolveVidFromElement(containerEl) : null;
9120
+ const insertedVid = resolveVidFromElement(newElement);
8967
9121
  useDesignModeStore.getState().recordInsertChange({
8968
9122
  operationId,
8969
9123
  nodeKey,
@@ -8975,6 +9129,8 @@ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, s
8975
9129
  tsxImports: component.tsxImports ?? null,
8976
9130
  containerId: intent.containerId,
8977
9131
  containerNodeKey,
9132
+ containerVid,
9133
+ insertedVid,
8978
9134
  index: intent.index,
8979
9135
  beforeSiblingInspectorRef: beforeSiblingIdentity?.inspectorRef ?? null,
8980
9136
  beforeSiblingNodeKey: beforeSiblingIdentity?.nodeKey ?? null
@@ -8989,6 +9145,8 @@ function commitExternalDrop(x, y, engine, component, refs, resolveNodeElement, s
8989
9145
  tsxCode: component.tsxCode ?? null,
8990
9146
  tsxImports: component.tsxImports ?? null,
8991
9147
  container: containerIdentity,
9148
+ containerVid,
9149
+ insertedVid,
8992
9150
  index: intent.index,
8993
9151
  beforeSibling: beforeSiblingIdentity
8994
9152
  });
@@ -26,6 +26,7 @@ module.exports = __toCommonJS(babel_plugin_collection_binding_exports);
26
26
  var import_types = require("@babel/types");
27
27
 
28
28
  // src/babel-plugin-editor-meta.ts
29
+ var import_editor_protocol = require("@8base-archie/editor-protocol");
29
30
  function meaningfulChildren(t, children) {
30
31
  return children.filter((child) => {
31
32
  if (t.isJSXText(child)) return child.value.trim() !== "";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  meaningfulChildren
3
- } from "./chunk-P5XMHBFS.mjs";
3
+ } from "./chunk-THKKWDMJ.mjs";
4
4
 
5
5
  // src/babel-plugin-collection-binding.ts
6
6
  import { VISITOR_KEYS } from "@babel/types";
package/dist/index.d.mts CHANGED
@@ -16,6 +16,13 @@ import { types, PluginObj } from '@babel/core';
16
16
  * + (ARCH-1450) childrenSource, propSources — same-file identifier tracing
17
17
  * for values `propBindings` cannot reach (no destructured-prop component
18
18
  * in scope, or a value that never touches a prop at all)
19
+ * + (data-vid identity migration) vid, isKitOrigin, dynamicType, isChrome,
20
+ * containerKind, lockReason — read from a `data-vid` attribute (stamped
21
+ * into persisted source by the Genesis stamper) plus the `NodeMeta`
22
+ * contract fields (`@8base-archie/editor-protocol`) computed from the
23
+ * element's own JSX/function ancestry. `nodeId === vid` when `data-vid`
24
+ * is present; `allowLegacyIds` (default `true`, transitional) governs
25
+ * what happens when it is not — see `EditorMetaPluginOptions` below.
19
26
  *
20
27
  * At runtime the Fiber bridge reads `memoizedProps.__editorMeta` to resolve
21
28
  * element identity without any DOM attributes.
@@ -136,6 +143,28 @@ interface EditorMetaPluginOptions {
136
143
  * @default false
137
144
  */
138
145
  templateOrigin?: boolean;
146
+ /**
147
+ * `data-vid` identity migration (vid-identity, decision binding). When a
148
+ * `JSXOpeningElement` carries a well-formed `data-vid` attribute
149
+ * (`VID_PATTERN`, `/^[a-z0-9]{6,8}$/`, stamped into persisted source by
150
+ * the Genesis stamper — see `@8base-archie/editor-protocol`'s
151
+ * `src/ids.ts`), the plugin ALWAYS reads it and uses it as `nodeId`
152
+ * (`meta.vid` is also emitted). This option only controls what happens
153
+ * when NO `data-vid` is present:
154
+ *
155
+ * - `true` (default in this version — TRANSITIONAL, to be removed once
156
+ * every source tree is stamped) — fall back to the legacy
157
+ * `"file:line:col:componentName"` addressing, exactly as before this
158
+ * change.
159
+ * - `false` — an element with no `data-vid` is NOT instrumented at all
160
+ * (no `__editorMeta` is emitted for it): a stable identity contract
161
+ * cannot be met by an addressing scheme that invalidates on every
162
+ * source write, so the plugin declines rather than silently hand out
163
+ * one.
164
+ *
165
+ * @default true
166
+ */
167
+ allowLegacyIds?: boolean;
139
168
  }
140
169
  declare function babelPluginEditorMeta({ types: t }: {
141
170
  types: typeof types;
@@ -347,10 +376,27 @@ type PropSource = {
347
376
  } | {
348
377
  kind: 'other';
349
378
  };
379
+
350
380
  /** Metadata injected into every JSX element by babel-plugin-editor-meta. */
351
381
  interface EditorMeta {
352
- /** Stable identifier: "file:line:col:componentName" */
382
+ /**
383
+ * Stable identifier for this node. `vid` identity (see below) is the
384
+ * canonical shape going forward: when the persisted source carries a
385
+ * `data-vid` attribute, `nodeId === vid`. Absent a `data-vid` (a bundle
386
+ * built with `allowLegacyIds: true`, the plugin's transitional default),
387
+ * this falls back to the legacy `"file:line:col:componentName"` shape,
388
+ * which invalidates on every write to the source.
389
+ */
353
390
  nodeId: string;
391
+ /**
392
+ * `data-vid` identity (vid-identity migration) — present only when the
393
+ * source element carried a well-formed `data-vid` attribute
394
+ * (`VID_PATTERN`, `/^[a-z0-9]{6,8}$/`). Stamped into PERSISTED source by
395
+ * the Genesis stamper, NOT computed here — the plugin only reads it.
396
+ * Absent on a node with no `data-vid` (legacy addressing) and on every
397
+ * bundle built before this change.
398
+ */
399
+ vid?: string;
354
400
  /** Relative file path from project root. */
355
401
  file: string;
356
402
  line: number;
@@ -553,6 +599,51 @@ interface EditorMeta {
553
599
  * already says). Absent on every bundle built before ARCH-1450.
554
600
  */
555
601
  propSources?: Record<string, PropSource>;
602
+ /**
603
+ * `NodeMeta` fields (vid-identity migration, `@8base-archie/editor-protocol`
604
+ * contract). Emitted only alongside `vid` (a legacy, `data-vid`-less node
605
+ * has no NodeMeta to report) — read via `fiber-bridge.ts`'s `getNodeMeta`,
606
+ * which validates the assembled `NodeMeta` object with `NodeMetaSchema`
607
+ * rather than trusting these fields individually.
608
+ *
609
+ * `isKitOrigin` restates the existing `origin === 'kit'` fact as a plain
610
+ * boolean (the `NodeMeta` contract's shape) — `origin` itself is
611
+ * unchanged and stays the field every existing consumer already reads.
612
+ */
613
+ isKitOrigin?: boolean;
614
+ /**
615
+ * Whether this node renders under a `.map`/`forEach`/`flatMap` callback
616
+ * (`'ARRAY'`), under a `ConditionalExpression`/`LogicalExpression` inside a
617
+ * `JSXExpressionContainer` (`'CONDITIONAL'`), or neither (`'STATIC'`).
618
+ * Computed at compile time by walking JSX/function ancestors — see
619
+ * `computeDynamicType` in `babel-plugin-editor-meta.ts` for the exact walk
620
+ * and its stopping rule at the first non-qualifying function boundary.
621
+ */
622
+ dynamicType?: 'STATIC' | 'ARRAY' | 'CONDITIONAL';
623
+ /**
624
+ * True for a node inside `shell.jsx`, `components/shell/**`, or with a
625
+ * `nav`/`aside`/`header` ancestor in the same file — the app shell/chrome
626
+ * region, structurally locked against drag/drop regardless of
627
+ * `isKitOrigin`. See `computeIsChrome` in `babel-plugin-editor-meta.ts`.
628
+ */
629
+ isChrome?: boolean;
630
+ /**
631
+ * Shape of this element's JSX children for drop-target purposes: `'plain'`
632
+ * when every child is a `JSXElement` or whitespace-trimmed `JSXText`,
633
+ * `'expression'` when any child is a `JSXExpressionContainer`/spread/
634
+ * iteration, `'none'` when the element has no element children (including
635
+ * self-closing). See `computeContainerKind` in `babel-plugin-editor-meta.ts`.
636
+ */
637
+ containerKind?: 'plain' | 'expression' | 'none';
638
+ /**
639
+ * Derived refusal reason, in priority order: `KIT_INTERNAL` (kit-origin) >
640
+ * `CHROME_STRUCTURAL` (shell/chrome) > `DYNAMIC_ARRAY`/`DYNAMIC_CONDITIONAL`
641
+ * (dynamic rendering shape) > `EXPRESSION_CONTAINER` (only relevant when
642
+ * this node is considered as a DROP TARGET, i.e. `containerKind ===
643
+ * 'expression'`). Absent when none of these apply — the node is a normal,
644
+ * writable, static, non-chrome, non-kit element.
645
+ */
646
+ lockReason?: 'KIT_INTERNAL' | 'DYNAMIC_ARRAY' | 'DYNAMIC_CONDITIONAL' | 'CHROME_STRUCTURAL' | 'EXPRESSION_CONTAINER';
556
647
  }
557
648
 
558
649
  interface ArchieDevToolsOptions {
package/dist/index.d.ts CHANGED
@@ -16,6 +16,13 @@ import { types, PluginObj } from '@babel/core';
16
16
  * + (ARCH-1450) childrenSource, propSources — same-file identifier tracing
17
17
  * for values `propBindings` cannot reach (no destructured-prop component
18
18
  * in scope, or a value that never touches a prop at all)
19
+ * + (data-vid identity migration) vid, isKitOrigin, dynamicType, isChrome,
20
+ * containerKind, lockReason — read from a `data-vid` attribute (stamped
21
+ * into persisted source by the Genesis stamper) plus the `NodeMeta`
22
+ * contract fields (`@8base-archie/editor-protocol`) computed from the
23
+ * element's own JSX/function ancestry. `nodeId === vid` when `data-vid`
24
+ * is present; `allowLegacyIds` (default `true`, transitional) governs
25
+ * what happens when it is not — see `EditorMetaPluginOptions` below.
19
26
  *
20
27
  * At runtime the Fiber bridge reads `memoizedProps.__editorMeta` to resolve
21
28
  * element identity without any DOM attributes.
@@ -136,6 +143,28 @@ interface EditorMetaPluginOptions {
136
143
  * @default false
137
144
  */
138
145
  templateOrigin?: boolean;
146
+ /**
147
+ * `data-vid` identity migration (vid-identity, decision binding). When a
148
+ * `JSXOpeningElement` carries a well-formed `data-vid` attribute
149
+ * (`VID_PATTERN`, `/^[a-z0-9]{6,8}$/`, stamped into persisted source by
150
+ * the Genesis stamper — see `@8base-archie/editor-protocol`'s
151
+ * `src/ids.ts`), the plugin ALWAYS reads it and uses it as `nodeId`
152
+ * (`meta.vid` is also emitted). This option only controls what happens
153
+ * when NO `data-vid` is present:
154
+ *
155
+ * - `true` (default in this version — TRANSITIONAL, to be removed once
156
+ * every source tree is stamped) — fall back to the legacy
157
+ * `"file:line:col:componentName"` addressing, exactly as before this
158
+ * change.
159
+ * - `false` — an element with no `data-vid` is NOT instrumented at all
160
+ * (no `__editorMeta` is emitted for it): a stable identity contract
161
+ * cannot be met by an addressing scheme that invalidates on every
162
+ * source write, so the plugin declines rather than silently hand out
163
+ * one.
164
+ *
165
+ * @default true
166
+ */
167
+ allowLegacyIds?: boolean;
139
168
  }
140
169
  declare function babelPluginEditorMeta({ types: t }: {
141
170
  types: typeof types;
@@ -347,10 +376,27 @@ type PropSource = {
347
376
  } | {
348
377
  kind: 'other';
349
378
  };
379
+
350
380
  /** Metadata injected into every JSX element by babel-plugin-editor-meta. */
351
381
  interface EditorMeta {
352
- /** Stable identifier: "file:line:col:componentName" */
382
+ /**
383
+ * Stable identifier for this node. `vid` identity (see below) is the
384
+ * canonical shape going forward: when the persisted source carries a
385
+ * `data-vid` attribute, `nodeId === vid`. Absent a `data-vid` (a bundle
386
+ * built with `allowLegacyIds: true`, the plugin's transitional default),
387
+ * this falls back to the legacy `"file:line:col:componentName"` shape,
388
+ * which invalidates on every write to the source.
389
+ */
353
390
  nodeId: string;
391
+ /**
392
+ * `data-vid` identity (vid-identity migration) — present only when the
393
+ * source element carried a well-formed `data-vid` attribute
394
+ * (`VID_PATTERN`, `/^[a-z0-9]{6,8}$/`). Stamped into PERSISTED source by
395
+ * the Genesis stamper, NOT computed here — the plugin only reads it.
396
+ * Absent on a node with no `data-vid` (legacy addressing) and on every
397
+ * bundle built before this change.
398
+ */
399
+ vid?: string;
354
400
  /** Relative file path from project root. */
355
401
  file: string;
356
402
  line: number;
@@ -553,6 +599,51 @@ interface EditorMeta {
553
599
  * already says). Absent on every bundle built before ARCH-1450.
554
600
  */
555
601
  propSources?: Record<string, PropSource>;
602
+ /**
603
+ * `NodeMeta` fields (vid-identity migration, `@8base-archie/editor-protocol`
604
+ * contract). Emitted only alongside `vid` (a legacy, `data-vid`-less node
605
+ * has no NodeMeta to report) — read via `fiber-bridge.ts`'s `getNodeMeta`,
606
+ * which validates the assembled `NodeMeta` object with `NodeMetaSchema`
607
+ * rather than trusting these fields individually.
608
+ *
609
+ * `isKitOrigin` restates the existing `origin === 'kit'` fact as a plain
610
+ * boolean (the `NodeMeta` contract's shape) — `origin` itself is
611
+ * unchanged and stays the field every existing consumer already reads.
612
+ */
613
+ isKitOrigin?: boolean;
614
+ /**
615
+ * Whether this node renders under a `.map`/`forEach`/`flatMap` callback
616
+ * (`'ARRAY'`), under a `ConditionalExpression`/`LogicalExpression` inside a
617
+ * `JSXExpressionContainer` (`'CONDITIONAL'`), or neither (`'STATIC'`).
618
+ * Computed at compile time by walking JSX/function ancestors — see
619
+ * `computeDynamicType` in `babel-plugin-editor-meta.ts` for the exact walk
620
+ * and its stopping rule at the first non-qualifying function boundary.
621
+ */
622
+ dynamicType?: 'STATIC' | 'ARRAY' | 'CONDITIONAL';
623
+ /**
624
+ * True for a node inside `shell.jsx`, `components/shell/**`, or with a
625
+ * `nav`/`aside`/`header` ancestor in the same file — the app shell/chrome
626
+ * region, structurally locked against drag/drop regardless of
627
+ * `isKitOrigin`. See `computeIsChrome` in `babel-plugin-editor-meta.ts`.
628
+ */
629
+ isChrome?: boolean;
630
+ /**
631
+ * Shape of this element's JSX children for drop-target purposes: `'plain'`
632
+ * when every child is a `JSXElement` or whitespace-trimmed `JSXText`,
633
+ * `'expression'` when any child is a `JSXExpressionContainer`/spread/
634
+ * iteration, `'none'` when the element has no element children (including
635
+ * self-closing). See `computeContainerKind` in `babel-plugin-editor-meta.ts`.
636
+ */
637
+ containerKind?: 'plain' | 'expression' | 'none';
638
+ /**
639
+ * Derived refusal reason, in priority order: `KIT_INTERNAL` (kit-origin) >
640
+ * `CHROME_STRUCTURAL` (shell/chrome) > `DYNAMIC_ARRAY`/`DYNAMIC_CONDITIONAL`
641
+ * (dynamic rendering shape) > `EXPRESSION_CONTAINER` (only relevant when
642
+ * this node is considered as a DROP TARGET, i.e. `containerKind ===
643
+ * 'expression'`). Absent when none of these apply — the node is a normal,
644
+ * writable, static, non-chrome, non-kit element.
645
+ */
646
+ lockReason?: 'KIT_INTERNAL' | 'DYNAMIC_ARRAY' | 'DYNAMIC_CONDITIONAL' | 'CHROME_STRUCTURAL' | 'EXPRESSION_CONTAINER';
556
647
  }
557
648
 
558
649
  interface ArchieDevToolsOptions {