@archie/devtools 0.0.39 → 0.0.40

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.
@@ -60,7 +60,8 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
60
60
  fileExclude = /node_modules/,
61
61
  instrumentImported = true,
62
62
  includeHostSourceRef = false,
63
- origin = "screen"
63
+ origin = "screen",
64
+ templateOrigin = false
64
65
  } = opts;
65
66
  function getJSXTagName(el) {
66
67
  const { name } = el;
@@ -556,9 +557,15 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
556
557
  )
557
558
  ]);
558
559
  if (isKitOrigin) {
559
- metaObj.properties.push(
560
- t.objectProperty(t.identifier("origin"), t.stringLiteral("kit"))
561
- );
560
+ if (templateOrigin) {
561
+ metaObj.properties.push(
562
+ t.objectProperty(t.identifier("template"), t.booleanLiteral(true))
563
+ );
564
+ } else {
565
+ metaObj.properties.push(
566
+ t.objectProperty(t.identifier("origin"), t.stringLiteral("kit"))
567
+ );
568
+ }
562
569
  }
563
570
  if (slot !== null) {
564
571
  metaObj.properties.push(
@@ -4437,11 +4437,19 @@ function getSingleHostElement(owner) {
4437
4437
  }
4438
4438
  return hosts.length === 1 ? hosts[0] : null;
4439
4439
  }
4440
+ function isTemplateFrame(meta) {
4441
+ return meta?.origin === "kit" || meta?.template === true;
4442
+ }
4443
+ function templateFrameLabel(meta) {
4444
+ if (meta?.origin === "kit") return "kit";
4445
+ if (meta?.template === true) return "template";
4446
+ return null;
4447
+ }
4440
4448
  function getUsageMetaOwnerFiber(el) {
4441
4449
  let fiber = getFiberSelectionFromElement(el).fiber;
4442
4450
  while (fiber) {
4443
4451
  const meta = readEditorMetaValue(fiber.memoizedProps?.__editorMeta);
4444
- if (meta && meta.origin !== "kit") return fiber;
4452
+ if (meta && !isTemplateFrame(meta)) return fiber;
4445
4453
  fiber = fiber.return;
4446
4454
  }
4447
4455
  return null;
@@ -4459,7 +4467,7 @@ function projectUsageMeta(el) {
4459
4467
  }
4460
4468
  function computeUsageMeta(el) {
4461
4469
  const direct = getDirectEditorMeta(el);
4462
- if (direct && direct.origin !== "kit") return null;
4470
+ if (direct && !isTemplateFrame(direct)) return null;
4463
4471
  const usage = projectFromOwner(getUsageMetaOwnerFiber(el), el);
4464
4472
  if (usage) return usage;
4465
4473
  if (direct) return null;
@@ -5367,7 +5375,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5367
5375
  containerStrategy: parentEl2 && isFlexOrGridParent(parentEl2) ? resolveContainerStrategy(parentEl2) : null,
5368
5376
  eligibilitySource: smallEligibility,
5369
5377
  draggable: false,
5370
- identityOrigin: identityMeta3?.origin ?? null,
5378
+ identityOrigin: templateFrameLabel(identityMeta3),
5371
5379
  editorMeta: surfaceMeta2
5372
5380
  });
5373
5381
  }
@@ -5428,7 +5436,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5428
5436
  const resolvedSelector = getUniqueSelector(hostEl);
5429
5437
  const usageMeta = hostEl instanceof HTMLElement ? projectUsageMeta(hostEl) : null;
5430
5438
  const identityMeta2 = usageMeta ?? ownerMeta ?? surfaceMeta;
5431
- const draggable = inferredEligibility === "force-allow" || !nodeId.startsWith("runtime:") && (identityMeta2?.origin !== "kit" || resolveRowBinding(hostEl) !== null);
5439
+ const draggable = inferredEligibility === "force-allow" || !nodeId.startsWith("runtime:") && (!isTemplateFrame(identityMeta2) || resolveRowBinding(hostEl) !== null);
5432
5440
  results.push({
5433
5441
  nodeId,
5434
5442
  element: hostEl,
@@ -5442,7 +5450,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5442
5450
  rect: hostRect,
5443
5451
  containerStrategy,
5444
5452
  eligibilitySource: inferredEligibility,
5445
- identityOrigin: identityMeta2?.origin ?? null,
5453
+ identityOrigin: templateFrameLabel(identityMeta2),
5446
5454
  editorMeta: surfaceMeta
5447
5455
  });
5448
5456
  if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
@@ -5477,8 +5485,8 @@ function buildMovedElementEntry(el, usedNodeIds) {
5477
5485
  // Same draggable rule as the main scan, INCLUDING the force-allow
5478
5486
  // escape — an element that was draggable when the user moved it must
5479
5487
  // not silently lose draggability for having landed below MAX_DEPTH.
5480
- // Same kit-origin bound-row exemption as the main scan (see there).
5481
- draggable: eligibility === "force-allow" || !nodeId.startsWith("runtime:") && (identityMeta2?.origin !== "kit" || resolveRowBinding(el) !== null),
5488
+ // Same template-frame bound-row exemption as the main scan (see there).
5489
+ draggable: eligibility === "force-allow" || !nodeId.startsWith("runtime:") && (!isTemplateFrame(identityMeta2) || resolveRowBinding(el) !== null),
5482
5490
  sourceMetaNodeId: identityMeta2?.nodeId ?? null,
5483
5491
  resolvedHostNodeId: nodeId,
5484
5492
  resolvedHostSelector: selector,
@@ -5487,7 +5495,7 @@ function buildMovedElementEntry(el, usedNodeIds) {
5487
5495
  rect,
5488
5496
  containerStrategy: parentEl && isFlexOrGridParent(parentEl) ? resolveContainerStrategy(parentEl) : null,
5489
5497
  eligibilitySource: eligibility ?? "force-allow",
5490
- identityOrigin: identityMeta2?.origin ?? null,
5498
+ identityOrigin: templateFrameLabel(identityMeta2),
5491
5499
  editorMeta: surfaceMeta
5492
5500
  };
5493
5501
  }
@@ -4402,11 +4402,19 @@ function getSingleHostElement(owner) {
4402
4402
  }
4403
4403
  return hosts.length === 1 ? hosts[0] : null;
4404
4404
  }
4405
+ function isTemplateFrame(meta) {
4406
+ return meta?.origin === "kit" || meta?.template === true;
4407
+ }
4408
+ function templateFrameLabel(meta) {
4409
+ if (meta?.origin === "kit") return "kit";
4410
+ if (meta?.template === true) return "template";
4411
+ return null;
4412
+ }
4405
4413
  function getUsageMetaOwnerFiber(el) {
4406
4414
  let fiber = getFiberSelectionFromElement(el).fiber;
4407
4415
  while (fiber) {
4408
4416
  const meta = readEditorMetaValue(fiber.memoizedProps?.__editorMeta);
4409
- if (meta && meta.origin !== "kit") return fiber;
4417
+ if (meta && !isTemplateFrame(meta)) return fiber;
4410
4418
  fiber = fiber.return;
4411
4419
  }
4412
4420
  return null;
@@ -4424,7 +4432,7 @@ function projectUsageMeta(el) {
4424
4432
  }
4425
4433
  function computeUsageMeta(el) {
4426
4434
  const direct = getDirectEditorMeta(el);
4427
- if (direct && direct.origin !== "kit") return null;
4435
+ if (direct && !isTemplateFrame(direct)) return null;
4428
4436
  const usage = projectFromOwner(getUsageMetaOwnerFiber(el), el);
4429
4437
  if (usage) return usage;
4430
4438
  if (direct) return null;
@@ -5332,7 +5340,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5332
5340
  containerStrategy: parentEl2 && isFlexOrGridParent(parentEl2) ? resolveContainerStrategy(parentEl2) : null,
5333
5341
  eligibilitySource: smallEligibility,
5334
5342
  draggable: false,
5335
- identityOrigin: identityMeta3?.origin ?? null,
5343
+ identityOrigin: templateFrameLabel(identityMeta3),
5336
5344
  editorMeta: surfaceMeta2
5337
5345
  });
5338
5346
  }
@@ -5393,7 +5401,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5393
5401
  const resolvedSelector = getUniqueSelector(hostEl);
5394
5402
  const usageMeta = hostEl instanceof HTMLElement ? projectUsageMeta(hostEl) : null;
5395
5403
  const identityMeta2 = usageMeta ?? ownerMeta ?? surfaceMeta;
5396
- const draggable = inferredEligibility === "force-allow" || !nodeId.startsWith("runtime:") && (identityMeta2?.origin !== "kit" || resolveRowBinding(hostEl) !== null);
5404
+ const draggable = inferredEligibility === "force-allow" || !nodeId.startsWith("runtime:") && (!isTemplateFrame(identityMeta2) || resolveRowBinding(hostEl) !== null);
5397
5405
  results.push({
5398
5406
  nodeId,
5399
5407
  element: hostEl,
@@ -5407,7 +5415,7 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5407
5415
  rect: hostRect,
5408
5416
  containerStrategy,
5409
5417
  eligibilitySource: inferredEligibility,
5410
- identityOrigin: identityMeta2?.origin ?? null,
5418
+ identityOrigin: templateFrameLabel(identityMeta2),
5411
5419
  editorMeta: surfaceMeta
5412
5420
  });
5413
5421
  if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts));
@@ -5442,8 +5450,8 @@ function buildMovedElementEntry(el, usedNodeIds) {
5442
5450
  // Same draggable rule as the main scan, INCLUDING the force-allow
5443
5451
  // escape — an element that was draggable when the user moved it must
5444
5452
  // not silently lose draggability for having landed below MAX_DEPTH.
5445
- // Same kit-origin bound-row exemption as the main scan (see there).
5446
- draggable: eligibility === "force-allow" || !nodeId.startsWith("runtime:") && (identityMeta2?.origin !== "kit" || resolveRowBinding(el) !== null),
5453
+ // Same template-frame bound-row exemption as the main scan (see there).
5454
+ draggable: eligibility === "force-allow" || !nodeId.startsWith("runtime:") && (!isTemplateFrame(identityMeta2) || resolveRowBinding(el) !== null),
5447
5455
  sourceMetaNodeId: identityMeta2?.nodeId ?? null,
5448
5456
  resolvedHostNodeId: nodeId,
5449
5457
  resolvedHostSelector: selector,
@@ -5452,7 +5460,7 @@ function buildMovedElementEntry(el, usedNodeIds) {
5452
5460
  rect,
5453
5461
  containerStrategy: parentEl && isFlexOrGridParent(parentEl) ? resolveContainerStrategy(parentEl) : null,
5454
5462
  eligibilitySource: eligibility ?? "force-allow",
5455
- identityOrigin: identityMeta2?.origin ?? null,
5463
+ identityOrigin: templateFrameLabel(identityMeta2),
5456
5464
  editorMeta: surfaceMeta
5457
5465
  };
5458
5466
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  meaningfulChildren
3
- } from "./chunk-B7TNA5GJ.mjs";
3
+ } from "./chunk-6QB35SLQ.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
@@ -35,6 +35,19 @@ import { types, PluginObj } from '@babel/core';
35
35
  * on every kit-origin node (see `types/editor-meta.ts`'s docstring on that
36
36
  * field for why it exists separately from `movable`).
37
37
  *
38
+ * ARCH-1367 F0b adds the `templateOrigin` option: it decouples those two
39
+ * effects for a kit component EJECTED into the generated project (now real
40
+ * `components/**` project source, not vendor). `movable: false` still holds
41
+ * for the same elements the existing `hasSlotAttr`/`origin === 'kit'` check
42
+ * already treats as kit-origin, but the stamp becomes `template: true`
43
+ * instead of `origin: 'kit'` (CREW r1) — the FE reads `origin: 'kit'` as
44
+ * "vendor, not editable", which is no longer true post-eject, while every
45
+ * identity/DND consumer still needs SOME per-node signal that the frame is
46
+ * template-shaped. See `isTemplateFrame` in `fiber-bridge.ts` for the shared
47
+ * predicate consumers use to treat `origin: 'kit'` and `template: true` as
48
+ * the same fact.
49
+ *
50
+
38
51
  * `parentNodeId` stays INTRA-FILE by construction (it only ever walks JSX
39
52
  * ancestors within the file being transformed), so it never links a kit template
40
53
  * node to a screen usage; per SDD §2.3 it must not be used across that seam.
@@ -60,7 +73,19 @@ interface EditorMetaPluginOptions {
60
73
  fileExclude?: RegExp;
61
74
  /** Allow instrumentation for imported components. Defaults to true for local imports. */
62
75
  instrumentImported?: boolean;
63
- /** Include exact host source refs for runtime DOM lookup. */
76
+ /**
77
+ * Include exact host source refs for runtime DOM lookup.
78
+ *
79
+ * MUST STAY OFF for `components/**` (ARCH-1367 template-origin files, and
80
+ * the kit's own `.gen/` pre-pass — see `build-editor-source.mjs`'s own note
81
+ * on this, SDD §2.10). The ref is `${file}::${line}::${col}` off the
82
+ * INSTRUMENTED source, so any editor-only pre-pass over a file that also
83
+ * ships a plain (non-instrumented) SSR build is a byte divergence between
84
+ * the two DOMs the instant it is enabled — identity must travel on the
85
+ * fiber (`__editorMeta`) for those files, never as a DOM attribute. There is
86
+ * no separate flag gating this — the caller is responsible for leaving it
87
+ * `false` wherever `templateOrigin: true` is passed.
88
+ */
64
89
  includeHostSourceRef?: boolean;
65
90
  /**
66
91
  * Where the transformed sources come from (ARCH-883 F1).
@@ -76,6 +101,37 @@ interface EditorMetaPluginOptions {
76
101
  * and also emits `movable: false`.
77
102
  */
78
103
  origin?: 'screen' | 'kit';
104
+ /**
105
+ * ARCH-1367 F0b. Set by the caller for `components/**` files — the kit's
106
+ * components after they are EJECTED into the generated project and become
107
+ * ordinary project source (never `'kit'`, since that would tell the FE
108
+ * "vendor, not editable").
109
+ *
110
+ * Post-eject, a component under `components/**` is still ONE template
111
+ * behind N screen usages within that project — the same reorder hazard
112
+ * `origin: 'kit'` exists to prevent (PRD §1.3) — but it must not carry the
113
+ * `origin: 'kit'` stamp, because it is no longer vendor code. This option
114
+ * decouples the two effects the current `hasSlotAttr`/`origin === 'kit'`
115
+ * check used to fuse together: the elements that logic would already mark
116
+ * kit-origin (chrome template elements carrying `data-kit-slot`, etc.)
117
+ * still emit `movable: false`, and (CREW r1) still emit an ADDITIVE
118
+ * per-node signal — `template: true` instead of `origin: 'kit'` — because
119
+ * every identity/DND consumer (`fiber-bridge.ts`, `instance-identity.ts`,
120
+ * `useElementScanner.ts`, and the hand-maintained `inspector.standalone.js`
121
+ * twin) needs to keep telling a template frame apart from a real screen
122
+ * usage; it just must not do so by lying that the frame is vendor. `origin`
123
+ * itself is unaffected — it stays whatever the caller passes for this
124
+ * compilation (typically `'screen'`, or omitted). `origin` and `template`
125
+ * are mutually exclusive on any one node — see `EditorMeta.template`'s
126
+ * docstring (`types/editor-meta.ts`) for the full consumer contract.
127
+ *
128
+ * This does NOT change which elements count as kit-origin (still driven by
129
+ * `hasSlotAttr` / `origin === 'kit'`) — a plain element with no slot
130
+ * attribute stays fully movable per `isMovable(path)`, exactly as before.
131
+ *
132
+ * @default false
133
+ */
134
+ templateOrigin?: boolean;
79
135
  }
80
136
  declare function babelPluginEditorMeta({ types: t }: {
81
137
  types: typeof types;
@@ -216,15 +272,56 @@ interface EditorMeta {
216
272
  * its own absence is meaningful). The mitigation is PAGE-LEVEL: the
217
273
  * standalone inspector script's `INSPECTOR_SCRIPT_LOADED` handshake reports
218
274
  * a `kitOriginIdentity` capability bit (see `inspector.standalone.js`) that
219
- * tells the host "this SCRIPT understands `origin` and projects across the
220
- * kit boundary" — it does NOT and cannot tell the host whether the PAGE's
221
- * own plugin build emitted the field, which is the acg/kitgen build's
222
- * `@archie/devtools` version, an axis this package has no visibility into.
223
- * Hosts that need a hard guarantee must track that build version
224
- * themselves; this capability bit only rules out "the runtime code is too
225
- * old to know what `origin` means" as a failure mode.
275
+ * tells the host "this SCRIPT understands `origin` (and, from ARCH-1367
276
+ * F0b, `template`) and projects across a template boundary" — it does NOT
277
+ * and cannot tell the host whether the PAGE's own plugin build emitted
278
+ * either field, which is the acg/kitgen build's `@archie/devtools` version,
279
+ * an axis this package has no visibility into. Hosts that need a hard
280
+ * guarantee must track that build version themselves; this capability bit
281
+ * only rules out "the runtime code is too old to know what `origin`/
282
+ * `template` mean" as a failure mode.
283
+ *
284
+ * ARCH-1367 F0b (CREW r1) adds a sibling field, `template`, for exactly
285
+ * this reason — an EJECTED `components/**` node is template-shaped (one
286
+ * template, N usages, PRD §1.3) but is real project source, not vendor, so
287
+ * stamping it `origin: 'kit'` would be a false "vendor, not editable"
288
+ * claim. See `template`'s own docstring below for the consumer contract;
289
+ * `fiber-bridge.ts`'s `isTemplateFrame` is the shared predicate that treats
290
+ * `origin === 'kit'` and `template === true` as the same fact.
226
291
  */
227
292
  origin?: 'kit';
293
+ /**
294
+ * `true` when the node is a TEMPLATE frame belonging to an EJECTED project
295
+ * component (ARCH-1367 F0b) — the `components/**` twin of `origin: 'kit'`
296
+ * for code that is no longer vendor. Emitted by the plugin's
297
+ * `templateOrigin` option on exactly the elements `origin: 'kit'` would
298
+ * otherwise mark (slot-carrying nodes, etc.); the two keys are mutually
299
+ * exclusive on any one node — a node never carries both.
300
+ *
301
+ * WHY A SEPARATE KEY FROM `origin`, not a third value like
302
+ * `origin: 'template'`. The FE has a live, already-consumed contract on
303
+ * `origin === 'kit'` meaning "vendor, not editable" (archie-app's
304
+ * `kit-screen-save.ts`, `runtime-compatibility.ts`, `edit-intent.ts`, the
305
+ * `shell-lane-telemetry.ts` capability round-trip). Reusing `origin` for a
306
+ * DIFFERENT truth ("template, but editable project source") would corrupt
307
+ * every one of those call sites silently; an additive key cannot.
308
+ *
309
+ * WHY A SEPARATE KEY FROM `movable`, the same reason `origin` needed one
310
+ * (see that field's docstring above): `movable` is ALSO `false` for
311
+ * map-callback roots, fragment children and expression-container siblings
312
+ * in ordinary screens (RFC E2.4 §2.7) — a consumer asking "is this a
313
+ * template frame I should walk past for identity, or refuse to drag" must
314
+ * not read `movable` for that, or it over-blocks real screen nodes.
315
+ *
316
+ * CONSUMER CONTRACT: every site that used to test `origin === 'kit'` alone
317
+ * to detect a template frame must test `origin === 'kit' || template ===
318
+ * true` instead (`isTemplateFrame` in `fiber-bridge.ts` is the shared TS
319
+ * predicate; `inspector.standalone.js` inlines the same check by hand,
320
+ * since it cannot import that module — keep the two in parity). Absent
321
+ * (`undefined`) on every node from a bundle built before F0b, and on any
322
+ * node that is not template-shaped.
323
+ */
324
+ template?: true;
228
325
  /**
229
326
  * Curated kit slot label, e.g. `"AuthFrame/aside/headline"` (SDD §2.6).
230
327
  *
package/dist/index.d.ts CHANGED
@@ -35,6 +35,19 @@ import { types, PluginObj } from '@babel/core';
35
35
  * on every kit-origin node (see `types/editor-meta.ts`'s docstring on that
36
36
  * field for why it exists separately from `movable`).
37
37
  *
38
+ * ARCH-1367 F0b adds the `templateOrigin` option: it decouples those two
39
+ * effects for a kit component EJECTED into the generated project (now real
40
+ * `components/**` project source, not vendor). `movable: false` still holds
41
+ * for the same elements the existing `hasSlotAttr`/`origin === 'kit'` check
42
+ * already treats as kit-origin, but the stamp becomes `template: true`
43
+ * instead of `origin: 'kit'` (CREW r1) — the FE reads `origin: 'kit'` as
44
+ * "vendor, not editable", which is no longer true post-eject, while every
45
+ * identity/DND consumer still needs SOME per-node signal that the frame is
46
+ * template-shaped. See `isTemplateFrame` in `fiber-bridge.ts` for the shared
47
+ * predicate consumers use to treat `origin: 'kit'` and `template: true` as
48
+ * the same fact.
49
+ *
50
+
38
51
  * `parentNodeId` stays INTRA-FILE by construction (it only ever walks JSX
39
52
  * ancestors within the file being transformed), so it never links a kit template
40
53
  * node to a screen usage; per SDD §2.3 it must not be used across that seam.
@@ -60,7 +73,19 @@ interface EditorMetaPluginOptions {
60
73
  fileExclude?: RegExp;
61
74
  /** Allow instrumentation for imported components. Defaults to true for local imports. */
62
75
  instrumentImported?: boolean;
63
- /** Include exact host source refs for runtime DOM lookup. */
76
+ /**
77
+ * Include exact host source refs for runtime DOM lookup.
78
+ *
79
+ * MUST STAY OFF for `components/**` (ARCH-1367 template-origin files, and
80
+ * the kit's own `.gen/` pre-pass — see `build-editor-source.mjs`'s own note
81
+ * on this, SDD §2.10). The ref is `${file}::${line}::${col}` off the
82
+ * INSTRUMENTED source, so any editor-only pre-pass over a file that also
83
+ * ships a plain (non-instrumented) SSR build is a byte divergence between
84
+ * the two DOMs the instant it is enabled — identity must travel on the
85
+ * fiber (`__editorMeta`) for those files, never as a DOM attribute. There is
86
+ * no separate flag gating this — the caller is responsible for leaving it
87
+ * `false` wherever `templateOrigin: true` is passed.
88
+ */
64
89
  includeHostSourceRef?: boolean;
65
90
  /**
66
91
  * Where the transformed sources come from (ARCH-883 F1).
@@ -76,6 +101,37 @@ interface EditorMetaPluginOptions {
76
101
  * and also emits `movable: false`.
77
102
  */
78
103
  origin?: 'screen' | 'kit';
104
+ /**
105
+ * ARCH-1367 F0b. Set by the caller for `components/**` files — the kit's
106
+ * components after they are EJECTED into the generated project and become
107
+ * ordinary project source (never `'kit'`, since that would tell the FE
108
+ * "vendor, not editable").
109
+ *
110
+ * Post-eject, a component under `components/**` is still ONE template
111
+ * behind N screen usages within that project — the same reorder hazard
112
+ * `origin: 'kit'` exists to prevent (PRD §1.3) — but it must not carry the
113
+ * `origin: 'kit'` stamp, because it is no longer vendor code. This option
114
+ * decouples the two effects the current `hasSlotAttr`/`origin === 'kit'`
115
+ * check used to fuse together: the elements that logic would already mark
116
+ * kit-origin (chrome template elements carrying `data-kit-slot`, etc.)
117
+ * still emit `movable: false`, and (CREW r1) still emit an ADDITIVE
118
+ * per-node signal — `template: true` instead of `origin: 'kit'` — because
119
+ * every identity/DND consumer (`fiber-bridge.ts`, `instance-identity.ts`,
120
+ * `useElementScanner.ts`, and the hand-maintained `inspector.standalone.js`
121
+ * twin) needs to keep telling a template frame apart from a real screen
122
+ * usage; it just must not do so by lying that the frame is vendor. `origin`
123
+ * itself is unaffected — it stays whatever the caller passes for this
124
+ * compilation (typically `'screen'`, or omitted). `origin` and `template`
125
+ * are mutually exclusive on any one node — see `EditorMeta.template`'s
126
+ * docstring (`types/editor-meta.ts`) for the full consumer contract.
127
+ *
128
+ * This does NOT change which elements count as kit-origin (still driven by
129
+ * `hasSlotAttr` / `origin === 'kit'`) — a plain element with no slot
130
+ * attribute stays fully movable per `isMovable(path)`, exactly as before.
131
+ *
132
+ * @default false
133
+ */
134
+ templateOrigin?: boolean;
79
135
  }
80
136
  declare function babelPluginEditorMeta({ types: t }: {
81
137
  types: typeof types;
@@ -216,15 +272,56 @@ interface EditorMeta {
216
272
  * its own absence is meaningful). The mitigation is PAGE-LEVEL: the
217
273
  * standalone inspector script's `INSPECTOR_SCRIPT_LOADED` handshake reports
218
274
  * a `kitOriginIdentity` capability bit (see `inspector.standalone.js`) that
219
- * tells the host "this SCRIPT understands `origin` and projects across the
220
- * kit boundary" — it does NOT and cannot tell the host whether the PAGE's
221
- * own plugin build emitted the field, which is the acg/kitgen build's
222
- * `@archie/devtools` version, an axis this package has no visibility into.
223
- * Hosts that need a hard guarantee must track that build version
224
- * themselves; this capability bit only rules out "the runtime code is too
225
- * old to know what `origin` means" as a failure mode.
275
+ * tells the host "this SCRIPT understands `origin` (and, from ARCH-1367
276
+ * F0b, `template`) and projects across a template boundary" — it does NOT
277
+ * and cannot tell the host whether the PAGE's own plugin build emitted
278
+ * either field, which is the acg/kitgen build's `@archie/devtools` version,
279
+ * an axis this package has no visibility into. Hosts that need a hard
280
+ * guarantee must track that build version themselves; this capability bit
281
+ * only rules out "the runtime code is too old to know what `origin`/
282
+ * `template` mean" as a failure mode.
283
+ *
284
+ * ARCH-1367 F0b (CREW r1) adds a sibling field, `template`, for exactly
285
+ * this reason — an EJECTED `components/**` node is template-shaped (one
286
+ * template, N usages, PRD §1.3) but is real project source, not vendor, so
287
+ * stamping it `origin: 'kit'` would be a false "vendor, not editable"
288
+ * claim. See `template`'s own docstring below for the consumer contract;
289
+ * `fiber-bridge.ts`'s `isTemplateFrame` is the shared predicate that treats
290
+ * `origin === 'kit'` and `template === true` as the same fact.
226
291
  */
227
292
  origin?: 'kit';
293
+ /**
294
+ * `true` when the node is a TEMPLATE frame belonging to an EJECTED project
295
+ * component (ARCH-1367 F0b) — the `components/**` twin of `origin: 'kit'`
296
+ * for code that is no longer vendor. Emitted by the plugin's
297
+ * `templateOrigin` option on exactly the elements `origin: 'kit'` would
298
+ * otherwise mark (slot-carrying nodes, etc.); the two keys are mutually
299
+ * exclusive on any one node — a node never carries both.
300
+ *
301
+ * WHY A SEPARATE KEY FROM `origin`, not a third value like
302
+ * `origin: 'template'`. The FE has a live, already-consumed contract on
303
+ * `origin === 'kit'` meaning "vendor, not editable" (archie-app's
304
+ * `kit-screen-save.ts`, `runtime-compatibility.ts`, `edit-intent.ts`, the
305
+ * `shell-lane-telemetry.ts` capability round-trip). Reusing `origin` for a
306
+ * DIFFERENT truth ("template, but editable project source") would corrupt
307
+ * every one of those call sites silently; an additive key cannot.
308
+ *
309
+ * WHY A SEPARATE KEY FROM `movable`, the same reason `origin` needed one
310
+ * (see that field's docstring above): `movable` is ALSO `false` for
311
+ * map-callback roots, fragment children and expression-container siblings
312
+ * in ordinary screens (RFC E2.4 §2.7) — a consumer asking "is this a
313
+ * template frame I should walk past for identity, or refuse to drag" must
314
+ * not read `movable` for that, or it over-blocks real screen nodes.
315
+ *
316
+ * CONSUMER CONTRACT: every site that used to test `origin === 'kit'` alone
317
+ * to detect a template frame must test `origin === 'kit' || template ===
318
+ * true` instead (`isTemplateFrame` in `fiber-bridge.ts` is the shared TS
319
+ * predicate; `inspector.standalone.js` inlines the same check by hand,
320
+ * since it cannot import that module — keep the two in parity). Absent
321
+ * (`undefined`) on every node from a bundle built before F0b, and on any
322
+ * node that is not template-shaped.
323
+ */
324
+ template?: true;
228
325
  /**
229
326
  * Curated kit slot label, e.g. `"AuthFrame/aside/headline"` (SDD §2.6).
230
327
  *
package/dist/index.js CHANGED
@@ -259,7 +259,8 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
259
259
  fileExclude = /node_modules/,
260
260
  instrumentImported = true,
261
261
  includeHostSourceRef = false,
262
- origin = "screen"
262
+ origin = "screen",
263
+ templateOrigin = false
263
264
  } = opts;
264
265
  function getJSXTagName(el) {
265
266
  const { name } = el;
@@ -755,9 +756,15 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
755
756
  )
756
757
  ]);
757
758
  if (isKitOrigin) {
758
- metaObj.properties.push(
759
- t.objectProperty(t.identifier("origin"), t.stringLiteral("kit"))
760
- );
759
+ if (templateOrigin) {
760
+ metaObj.properties.push(
761
+ t.objectProperty(t.identifier("template"), t.booleanLiteral(true))
762
+ );
763
+ } else {
764
+ metaObj.properties.push(
765
+ t.objectProperty(t.identifier("origin"), t.stringLiteral("kit"))
766
+ );
767
+ }
761
768
  }
762
769
  if (slot !== null) {
763
770
  metaObj.properties.push(
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __dirname,
3
3
  babelPluginEditorMeta
4
- } from "./chunk-B7TNA5GJ.mjs";
4
+ } from "./chunk-6QB35SLQ.mjs";
5
5
 
6
6
  // src/index.ts
7
7
  import { readFileSync } from "fs";
@@ -323,17 +323,28 @@
323
323
  return hosts.length === 1 ? hosts[0] : null
324
324
  }
325
325
 
326
- // The fiber owning the nearest NON-kit __editorMeta for `el` — the usage
327
- // written in a screen, reached across however many kit template frames
328
- // sit in between. Vanilla twin of getUsageMetaOwnerFiber
329
- // (fiber-bridge.ts); see that docstring for why the nearest meta is the
330
- // wrong one once the kit itself is instrumented.
326
+ // Vanilla twin of isTemplateFrame (fiber-bridge.ts). True when `meta`
327
+ // names a TEMPLATE frame — a kit-vendor one (`origin === "kit"`) or an
328
+ // EJECTED project one (`template === true`, ARCH-1367 F0b, CREW r1) —
329
+ // one source position standing behind N usages either way. Inlined
330
+ // because this script cannot import the TS module: keep this in
331
+ // parity with fiber-bridge.ts's `isTemplateFrame` if that ever changes.
332
+ function isTemplateFrame(meta) {
333
+ return !!meta && (meta.origin === "kit" || meta.template === true)
334
+ }
335
+
336
+ // The fiber owning the nearest __editorMeta for `el` that is NOT a
337
+ // template frame — the usage written in a screen, reached across
338
+ // however many template frames (kit-vendor or ejected-project) sit in
339
+ // between. Vanilla twin of getUsageMetaOwnerFiber (fiber-bridge.ts);
340
+ // see that docstring for why the nearest meta is the wrong one once
341
+ // the kit itself is instrumented.
331
342
  function getUsageMetaOwnerFiberFromElement(el) {
332
343
  var fiber = getFiberFromElement(el)
333
344
  while (fiber) {
334
345
  if (fiber.memoizedProps && fiber.memoizedProps.__editorMeta) {
335
346
  var meta = resolveEditorMetaValue(fiber.memoizedProps.__editorMeta)
336
- if (meta && meta.origin !== "kit") return fiber
347
+ if (meta && !isTemplateFrame(meta)) return fiber
337
348
  }
338
349
  fiber = fiber["return"]
339
350
  }
@@ -351,10 +362,11 @@
351
362
  // `el` is the SINGLE host root a component usage renders and that usage's
352
363
  // meta never reached the host fiber — because the component destructured
353
364
  // its props, or because the plugin sanitized it out of the spread (which
354
- // is every kit component, once the kit is instrumented).
365
+ // is every kit component, once the kit is instrumented, and every ejected
366
+ // project template once it opts into `templateOrigin`).
355
367
  function projectUsageMetaFromElement(el) {
356
368
  var direct = getDirectEditorMetaFromElement(el)
357
- if (direct && direct.origin !== "kit") return null
369
+ if (direct && !isTemplateFrame(direct)) return null
358
370
 
359
371
  var usage = projectFromOwnerFiber(
360
372
  getUsageMetaOwnerFiberFromElement(el),
@@ -398,14 +410,15 @@
398
410
  // position: stable across N repeats of the SAME position (the
399
411
  // DataTable-row case — same usage, same template row id, correctly still
400
412
  // a collection) while distinct across different positions under one
401
- // usage (span vs p). Mirrors ownNodeId in instance-identity.ts — if this
413
+ // usage (span vs p). Mirrors ownNodeId in instance-identity.ts (which
414
+ // tests `isTemplateFrame(direct)`, ARCH-1367 F0b, CREW r1) — if this
402
415
  // changes, update that copy to maintain parity.
403
416
  function ownMetaNodeId(el) {
404
417
  if (!el || el.nodeType !== 1) return null
405
418
  var projected = projectUsageMetaFromElement(el)
406
419
  if (projected && projected.nodeId) return projected.nodeId
407
420
  var direct = getDirectEditorMetaFromElement(el)
408
- if (direct && direct.origin === "kit") {
421
+ if (direct && isTemplateFrame(direct)) {
409
422
  var usageOwner = getUsageMetaOwnerFiberFromElement(el)
410
423
  var usage =
411
424
  usageOwner &&
@@ -1637,13 +1650,32 @@
1637
1650
  // `inspectorSource`/`editorInstance` on an instrumented-kit page
1638
1651
  // reproduce the exact F1 bug this version fixes.
1639
1652
  //
1653
+ // ARCH-1367 F0b (CREW r1, unversioned — no new wire behavior, same
1654
+ // bit): the SAME projection functions now ALSO resolve
1655
+ // `EditorMeta.template === true` — the ejected-project-template
1656
+ // frame this package's `templateOrigin` plugin option emits — via
1657
+ // this file's own `isTemplateFrame` (mirrors `fiber-bridge.ts`'s
1658
+ // export of the same name). A host already gating on
1659
+ // `kitOriginIdentity` before trusting `origin`/instance-identity
1660
+ // gets the SAME correctness for `template` for free: there is no
1661
+ // NEW thing to feature-detect, because the gate was always "this
1662
+ // script resolves identity across a template boundary," not "...
1663
+ // a KIT boundary specifically." The bit keeps its name —
1664
+ // `kitOriginIdentity` is a stable, already-consumed wire field
1665
+ // (archie-app's `runtime-compatibility.ts`, `kit-screen-save.ts`,
1666
+ // `shell-lane-telemetry.ts`, `edit-intent.ts`) and its SHAPE
1667
+ // (boolean, same key) is unchanged; only its documented meaning
1668
+ // widens, from this version forward, to "kit-vendor OR
1669
+ // ejected-project template," never "kit only."
1670
+ //
1640
1671
  // What this bit CANNOT tell the host: whether the PAGE's own build
1641
- // emitted `origin` on its metas in the first place — that is the
1642
- // `@archie/devtools` version the acg/kitgen esbuild path had
1643
- // installed when the screen (or the kit) was built, a completely
1644
- // separate axis from this CDN script's version (see the `origin`
1645
- // field's docstring in `types/editor-meta.ts` for the full
1646
- // reasoning, and why NO per-node signal can close that gap).
1672
+ // emitted `origin`/`template` on its metas in the first place —
1673
+ // that is the `@archie/devtools` version the acg/kitgen esbuild
1674
+ // path had installed when the screen (or the kit) was built, a
1675
+ // completely separate axis from this CDN script's version (see
1676
+ // the `origin`/`template` fields' docstrings in
1677
+ // `types/editor-meta.ts` for the full reasoning, and why NO
1678
+ // per-node signal can close that gap).
1647
1679
  kitOriginIdentity: true,
1648
1680
  // v2.5.0 — right-click menu in navigate AND inspect mode, emitting
1649
1681
  // `CHAT_CONTEXT_REQUESTED` (with the node's full metadata) and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archie/devtools",
3
- "version": "0.0.39",
3
+ "version": "0.0.40",
4
4
  "description": "DevTools for Archie generated applications - Route synchronization and editor communication",
5
5
  "repository": {
6
6
  "type": "git",