@jay-framework/stack-server-build 0.25.3 → 0.25.5

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.
package/dist/index.d.ts CHANGED
@@ -117,6 +117,9 @@ interface GenerateClientScriptOptions {
117
117
  slowViewState?: object;
118
118
  /** Route pattern (e.g., /products/kitan{/:category}) for freeze entries */
119
119
  routePattern?: string;
120
+ /** Project-relative path of the page's jay-html (DL#219) — reported by the source-editing
121
+ * inspector as the file the matched `lines` live in. */
122
+ sourceFile?: string;
120
123
  }
121
124
  /**
122
125
  * Shared fragments generated by buildScriptFragments().
package/dist/index.js CHANGED
@@ -420,8 +420,9 @@ function buildPageReloadHmrScript() {
420
420
  });
421
421
  }`;
422
422
  }
423
- function buildFreezeScript(routePattern) {
423
+ function buildFreezeScript(routePattern, sourceFile) {
424
424
  const routePatternLiteral = routePattern ? `'${routePattern}'` : "undefined";
425
+ const sourceFileLiteral = sourceFile ? JSON.stringify(sourceFile) : "undefined";
425
426
  return `
426
427
  // Page Freeze (DL#127, DL#128 iframe addendum)
427
428
  // Sticky embed mode: URL param sets a session cookie so in-iframe navigation preserves it
@@ -476,13 +477,152 @@ function buildFreezeScript(routePattern) {
476
477
  }
477
478
  }
478
479
 
480
+ // Source-editing inspector (DL#219). Resolves a viewport point/rect to the jay-dev-* source
481
+ // markers emitted when the dev server runs with sourceEditing on. Harmless without markers —
482
+ // it simply finds no jay-dev-src and replies node:null. Reply carries source LOCATORS (line
483
+ // ranges + route) plus runtime identities (forEach record id, region name + static props);
484
+ // the agent reads bound expressions / collection / if-conditions from the jay-html itself.
485
+ function __jayParseLines(s) {
486
+ if (!s) return null;
487
+ const parts = s.split(':');
488
+ return { from: parseInt(parts[0], 10), to: parseInt(parts[1], 10) };
489
+ }
490
+ function __jayLines(el) {
491
+ return __jayParseLines(el.getAttribute('jay-dev-src'));
492
+ }
493
+ // A significant enclosing frame: a forEach item root or a region boundary. Returns null for
494
+ // plain wrapper elements (the agent reconstructs those from source via node.lines).
495
+ function __jayFrame(el) {
496
+ const item = el.getAttribute('jay-dev-item');
497
+ if (item) return { kind: 'forEach', lines: __jayLines(el), itemId: item };
498
+ const region = el.getAttribute('jay-dev-region');
499
+ if (region) {
500
+ // Region lines live in jay-dev-region-src (the <jay:…> tag's range), distinct from the
501
+ // root element's own jay-dev-src.
502
+ const frame = { kind: 'region', lines: __jayParseLines(el.getAttribute('jay-dev-region-src')), name: region };
503
+ const rawProps = el.getAttribute('jay-dev-region-props');
504
+ if (rawProps) { try { frame.props = JSON.parse(decodeURIComponent(rawProps)); } catch (e) {} }
505
+ return frame;
506
+ }
507
+ return null;
508
+ }
509
+ // One-level HTML of an element: its open tag (authored attrs only, jay-* stripped), its direct
510
+ // children shown as empty tags (grandchildren elided with …), and its text — so the agent sees
511
+ // exactly what was clicked inside generated content without the whole subtree.
512
+ function __jayOneLevelHtml(el) {
513
+ if (!el || !el.tagName) return '';
514
+ const tag = el.tagName.toLowerCase();
515
+ let attrs = '';
516
+ for (let i = 0; i < el.attributes.length; i++) {
517
+ const a = el.attributes[i];
518
+ if (a.name.indexOf('jay-') === 0) continue; // strip jay-dev-* / jay-coordinate
519
+ attrs += ' ' + a.name + '="' + a.value + '"';
520
+ }
521
+ let inner = '';
522
+ const kids = el.childNodes || [];
523
+ for (let i = 0; i < kids.length; i++) {
524
+ const c = kids[i];
525
+ if (c.nodeType === 3) inner += (c.textContent || '');
526
+ else if (c.nodeType === 1) {
527
+ const ct = c.tagName.toLowerCase();
528
+ inner += '<' + ct + '>' + (c.childNodes && c.childNodes.length ? '…' : '') + '</' + ct + '>';
529
+ }
530
+ }
531
+ if (inner.length > 300) inner = inner.slice(0, 300) + '…';
532
+ return '<' + tag + attrs + '>' + inner + '</' + tag + '>';
533
+ }
534
+ function __jayResolveElement(el) {
535
+ if (!el) return null;
536
+ // Walk el up to (but excluding) <body>.
537
+ const entries = [];
538
+ let cur = el;
539
+ while (cur && cur.nodeType === 1 && cur.tagName && cur.tagName.toLowerCase() !== 'body') {
540
+ entries.push(cur);
541
+ cur = cur.parentElement;
542
+ }
543
+ // Source anchor = nearest element at/above el carrying a source marker (its line range).
544
+ let sourceEl = null;
545
+ for (let i = 0; i < entries.length; i++) {
546
+ if (entries[i].getAttribute('jay-dev-src')) { sourceEl = entries[i]; break; }
547
+ }
548
+ if (!sourceEl) return null;
549
+ // Stack = only the significant enclosing frames (forEach + region), innermost → outermost.
550
+ const stack = [];
551
+ for (let i = 0; i < entries.length; i++) {
552
+ const f = __jayFrame(entries[i]);
553
+ if (f) stack.push(f);
554
+ }
555
+ // node describes the ACTUAL element el (what the pointer hit / the rect chose). html — a
556
+ // one-level rendering (open tag with authored attrs, direct children as empty tags, inline
557
+ // text; grandchildren + long text elided) — is the single content field: it carries the tag,
558
+ // attributes (src/href/value), and value, bounded in size. lines come from the source anchor;
559
+ // for bound/generated content (an html-string value) el has no jay-dev-src of its own, so
560
+ // lines point to the ENCLOSING binding (the editable source) and contentFromBinding flags it.
561
+ const r = el.getBoundingClientRect();
562
+ const node = {
563
+ lines: __jayLines(sourceEl),
564
+ tag: el.tagName ? el.tagName.toLowerCase() : '',
565
+ html: __jayOneLevelHtml(el),
566
+ rect: { x: r.x, y: r.y, width: r.width, height: r.height },
567
+ };
568
+ const childEls = Array.prototype.slice.call(el.children || []);
569
+ const childrenGenerated =
570
+ childEls.length > 0 && childEls.every((c) => !c.getAttribute('jay-dev-src'));
571
+ if (!el.getAttribute('jay-dev-src') || childrenGenerated) {
572
+ node.contentFromBinding = true;
573
+ }
574
+ return { node, stack };
575
+ }
576
+ function __jayInspectPoint(x, y) {
577
+ return __jayResolveElement(document.elementFromPoint(x, y));
578
+ }
579
+ function __jayInspectRect(x, y, w, h) {
580
+ const sel = { left: x, top: y, right: x + w, bottom: y + h };
581
+ const marked = Array.prototype.slice.call(document.querySelectorAll('[jay-dev-src]'));
582
+ let best = null, bestArea = -1;
583
+ for (let i = 0; i < marked.length; i++) {
584
+ const el = marked[i];
585
+ const r = el.getBoundingClientRect();
586
+ const elArea = r.width * r.height;
587
+ if (elArea <= 0) continue;
588
+ const ix = Math.max(0, Math.min(sel.right, r.right) - Math.max(sel.left, r.left));
589
+ const iy = Math.max(0, Math.min(sel.bottom, r.bottom) - Math.max(sel.top, r.top));
590
+ // Rect rule (DL#219): keep elements the box mostly encloses (overlap over the ELEMENT's
591
+ // own area >= 0.8), then pick the LARGEST — the element the box was drawn around. A tight
592
+ // box only clears the threshold for the small element; a big box clears it for the
593
+ // container too, and largest-area returns the container (not its first child).
594
+ if ((ix * iy) / elArea >= 0.8 && elArea > bestArea) {
595
+ bestArea = elArea;
596
+ best = el;
597
+ }
598
+ }
599
+ if (best) return __jayResolveElement(best);
600
+ return __jayInspectPoint(x + w / 2, y + h / 2);
601
+ }
602
+
479
603
  if (__jayEmbedMode) {
480
604
  // Notify parent of the current route on load (DL#128 route addendum)
481
605
  window.parent.postMessage({ type: 'jay:route', route: window.location.pathname, routePattern: ${routePatternLiteral} }, '*');
482
606
 
483
- // Embed mode: parent triggers freeze via postMessage
607
+ // Embed mode: parent triggers freeze / inspect via postMessage
484
608
  window.addEventListener('message', (e) => {
485
- if (e.data?.type === 'jay:requestFreeze') __jayDoFreeze();
609
+ const d = e.data;
610
+ if (!d) return;
611
+ if (d.type === 'jay:requestFreeze') { __jayDoFreeze(); return; }
612
+ if (d.type === 'jay:inspectPoint' || d.type === 'jay:inspectRect') {
613
+ const result = d.type === 'jay:inspectPoint'
614
+ ? __jayInspectPoint(d.x, d.y)
615
+ : __jayInspectRect(d.x, d.y, d.width, d.height);
616
+ window.parent.postMessage({
617
+ type: 'jay:inspectResult',
618
+ requestId: d.requestId,
619
+ file: ${sourceFileLiteral},
620
+ route: window.location.pathname,
621
+ routePattern: ${routePatternLiteral},
622
+ node: result ? result.node : null, // node.rect is the matched element's on-screen box
623
+ stack: result ? result.stack : [],
624
+ }, '*');
625
+ }
486
626
  });
487
627
  } else {
488
628
  // Standalone: Alt+S / Option+S keyboard shortcut
@@ -507,7 +647,7 @@ function buildAutomationWrap(options, mode) {
507
647
  }
508
648
  const appendLine = appendDom ? `
509
649
  target.appendChild(wrapped.element.dom);` : "";
510
- const freezeScript = buildFreezeScript(options.routePattern);
650
+ const freezeScript = buildFreezeScript(options.routePattern, options.sourceFile);
511
651
  if (hasSlowViewState) {
512
652
  return `
513
653
  // Wrap with automation for dev tooling
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/stack-server-build",
3
- "version": "0.25.3",
3
+ "version": "0.25.5",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.mts",
@@ -24,22 +24,22 @@
24
24
  "test:watch": "vitest"
25
25
  },
26
26
  "dependencies": {
27
- "@jay-framework/compiler-jay-html": "^0.25.3",
28
- "@jay-framework/compiler-shared": "^0.25.3",
29
- "@jay-framework/component": "^0.25.3",
30
- "@jay-framework/fullstack-component": "^0.25.3",
31
- "@jay-framework/logger": "^0.25.3",
32
- "@jay-framework/runtime": "^0.25.3",
33
- "@jay-framework/ssr-runtime": "^0.25.3",
34
- "@jay-framework/stack-route-scanner": "^0.25.3",
35
- "@jay-framework/stack-server-runtime": "^0.25.3",
36
- "@jay-framework/view-state-merge": "^0.25.3",
27
+ "@jay-framework/compiler-jay-html": "^0.25.5",
28
+ "@jay-framework/compiler-shared": "^0.25.5",
29
+ "@jay-framework/component": "^0.25.5",
30
+ "@jay-framework/fullstack-component": "^0.25.5",
31
+ "@jay-framework/logger": "^0.25.5",
32
+ "@jay-framework/runtime": "^0.25.5",
33
+ "@jay-framework/ssr-runtime": "^0.25.5",
34
+ "@jay-framework/stack-route-scanner": "^0.25.5",
35
+ "@jay-framework/stack-server-runtime": "^0.25.5",
36
+ "@jay-framework/view-state-merge": "^0.25.5",
37
37
  "node-html-parser": "^6.1.12",
38
38
  "yaml": "^2.3.4"
39
39
  },
40
40
  "devDependencies": {
41
- "@jay-framework/dev-environment": "^0.25.3",
42
- "@jay-framework/rollup-plugin": "^0.25.3",
41
+ "@jay-framework/dev-environment": "^0.25.5",
42
+ "@jay-framework/rollup-plugin": "^0.25.5",
43
43
  "@types/node": "^22.15.21",
44
44
  "rimraf": "^5.0.5",
45
45
  "tsup": "^8.0.1",