@bhooai/nexus-graphql 2.0.12 → 2.0.16
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/package.json
CHANGED
|
@@ -52,7 +52,21 @@ const AURORA_CSS = `.agent-shell { display:flex; flex-direction:column; height:1
|
|
|
52
52
|
.agent-brand-name { color:#f1f8ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.8rem; font-weight:800; letter-spacing:.16em; text-transform:uppercase; }
|
|
53
53
|
.agent-brand-name em { color:#63bdff; font-style:normal; text-shadow:0 0 18px rgba(84,183,255,.5); }
|
|
54
54
|
.agent-brand-sub { color:#4f6b8c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.55rem; letter-spacing:.14em; text-transform:uppercase; }
|
|
55
|
-
.agent-spacer { flex:1; }
|
|
55
|
+
.agent-spacer { flex:1; }
|
|
56
|
+
|
|
57
|
+
/* ── Unified tooltip — single style for every [data-tip] element on the page ── */
|
|
58
|
+
[data-tip]{ position:relative; }
|
|
59
|
+
[data-tip]::after{ position:absolute; left:50%; bottom:calc(100% + 8px); transform:translateX(-50%) translateY(2px); z-index:120; padding:.35rem .6rem; border:1px solid rgba(112,168,228,.28); border-radius:.45rem; background:linear-gradient(160deg, rgba(10,28,55,.97), rgba(6,16,36,.98)); color:#dcecff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; font-weight:600; letter-spacing:.03em; line-height:1.4; white-space:nowrap; content:attr(data-tip); opacity:0; visibility:hidden; pointer-events:none; box-shadow:0 12px 32px rgba(0,0,0,.4), inset 0 1px rgba(255,255,255,.06); backdrop-filter:blur(18px) saturate(140%); -webkit-backdrop-filter:blur(18px) saturate(140%); transition:opacity .16s ease, transform .16s ease, visibility .16s; }
|
|
60
|
+
[data-tip]::before{ position:absolute; left:50%; bottom:calc(100% + 4px); transform:translateX(-50%) rotate(45deg); z-index:119; width:.5rem; height:.5rem; border-right:1px solid rgba(112,168,228,.28); border-bottom:1px solid rgba(112,168,228,.28); background:rgba(10,28,55,.98); content:""; opacity:0; visibility:hidden; pointer-events:none; transition:opacity .16s ease, visibility .16s; }
|
|
61
|
+
[data-tip]:hover::after, [data-tip]:hover::before,
|
|
62
|
+
[data-tip]:focus-visible::after, [data-tip]:focus-visible::before{ opacity:1; visibility:visible; transform:translateX(-50%) translateY(0); }
|
|
63
|
+
[data-tip]:hover::before, [data-tip]:focus-visible::before{ transform:translateX(-50%) rotate(45deg); }
|
|
64
|
+
/* Tooltip below for elements near the top edge (e.g. header buttons). */
|
|
65
|
+
[data-tip="bottom"]::after, [data-tip-bottom]::after{ bottom:auto; top:calc(100% + 8px); transform:translateX(-50%) translateY(-2px); }
|
|
66
|
+
[data-tip="bottom"]::before, [data-tip-bottom]::before{ bottom:auto; top:calc(100% + 4px); transform:translateX(-50%) rotate(45deg); }
|
|
67
|
+
[data-tip="bottom"]:hover::after, [data-tip-bottom]:hover::after{ transform:translateX(-50%) translateY(0); }
|
|
68
|
+
[data-theme="light"] [data-tip]::after{ background:linear-gradient(160deg, rgba(255,255,255,.98), rgba(238,245,255,.98)); color:#0b1220; border-color:rgba(60,110,180,.24); box-shadow:0 12px 32px rgba(20,50,100,.16), inset 0 1px rgba(255,255,255,.7); }
|
|
69
|
+
[data-theme="light"] [data-tip]::before{ background:#f2f7ff; border-color:rgba(60,110,180,.24); }
|
|
56
70
|
.agent-status { display:inline-flex; align-items:center; gap:.42rem; padding:.4rem .7rem; border:1px solid rgba(52,211,153,.32); border-radius:999px; background:rgba(16,185,129,.12); color:#7ee8bd; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; font-weight:700; letter-spacing:.1em; text-transform:uppercase; }
|
|
57
71
|
.agent-status-dot { width:.42rem; height:.42rem; border-radius:50%; background:#5ef0b3; box-shadow:0 0 10px rgba(94,240,179,.9); animation:agent-pulse 2.4s ease-in-out infinite; }
|
|
58
72
|
.agent-select-wrap { display:inline-flex; align-items:center; gap:.42rem; padding:.22rem .3rem .22rem .6rem; border:1px solid rgba(112,168,228,.2); border-radius:.6rem; background:rgba(20,46,79,.3); transition:border-color .14s, background .14s, box-shadow .14s; }
|
|
@@ -380,7 +394,28 @@ const AURORA_CSS = `.agent-shell { display:flex; flex-direction:column; height:1
|
|
|
380
394
|
.agent-dots i:nth-child(3) { animation-delay:.32s; }
|
|
381
395
|
@keyframes agent-bounce { 0%,60%,100% { transform:translateY(0); opacity:.5; } 30% { transform:translateY(-4px); opacity:1; } }
|
|
382
396
|
|
|
383
|
-
.agent-toast { position:fixed; top:4.5rem; right:1.5rem; z-index:60; padding:.6rem .9rem; border:1px solid rgba(84,183,255,.35); border-radius:.6rem; color:#dcecff; background:linear-gradient(160deg, rgba(16,38,70,.85), rgba(8,20,42,.9)); box-shadow:0 14px 40px rgba(0,0,0,.4); backdrop-filter:blur(20px) saturate(140%); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.68rem; animation:agent-open .22s cubic-bezier(.22,1,.36,1); }
|
|
397
|
+
.agent-toast { position:fixed; top:4.5rem; right:1.5rem; z-index:60; padding:.6rem .9rem; border:1px solid rgba(84,183,255,.35); border-radius:.6rem; color:#dcecff; background:linear-gradient(160deg, rgba(16,38,70,.85), rgba(8,20,42,.9)); box-shadow:0 14px 40px rgba(0,0,0,.4); backdrop-filter:blur(20px) saturate(140%); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.68rem; animation:agent-open .22s cubic-bezier(.22,1,.36,1); }
|
|
398
|
+
|
|
399
|
+
/* ── Alert toast (top-right) — success / error / info states ── */
|
|
400
|
+
.agent-alert { position:fixed; top:4.2rem; right:1.25rem; z-index:200; display:none; align-items:flex-start; gap:.6rem; max-width:340px; padding:.65rem .8rem; border:1px solid rgba(84,183,255,.35); border-radius:.7rem; color:#dcecff; background:linear-gradient(160deg, rgba(16,38,70,.94), rgba(8,20,42,.97)); box-shadow:0 14px 44px rgba(0,0,0,.5), inset 0 1px rgba(255,255,255,.06); backdrop-filter:blur(20px) saturate(140%); -webkit-backdrop-filter:blur(20px) saturate(140%); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; }
|
|
401
|
+
.agent-alert.show { display:flex; animation:agent-rise .24s cubic-bezier(.22,1,.36,1); }
|
|
402
|
+
.agent-alert-mark { flex:0 0 auto; display:grid; place-items:center; width:1.5rem; height:1.5rem; border-radius:.45rem; color:#6df0bb; font-size:.85rem; font-weight:800; border:1px solid rgba(52,211,153,.45); background:linear-gradient(135deg, rgba(16,185,129,.3), rgba(6,95,70,.22)); box-shadow:0 0 12px rgba(52,211,153,.25); }
|
|
403
|
+
.agent-alert-body { flex:1; min-width:0; display:flex; flex-direction:column; gap:.15rem; }
|
|
404
|
+
.agent-alert-title { color:#7ee8bd; font-size:.62rem; font-weight:800; letter-spacing:.1em; text-transform:uppercase; }
|
|
405
|
+
.agent-alert-msg { color:#cfe3fb; font-size:.64rem; line-height:1.5; word-break:break-word; }
|
|
406
|
+
.agent-alert-close { flex:0 0 auto; display:grid; place-items:center; width:1.2rem; height:1.2rem; border:0; border-radius:.35rem; color:#7d94b2; background:transparent; font-size:.9rem; line-height:1; cursor:pointer; transition:color .12s, background .12s; }
|
|
407
|
+
.agent-alert-close:hover { color:#fecdd3; background:rgba(244,63,94,.16); }
|
|
408
|
+
.agent-alert.hide { display:none; }
|
|
409
|
+
/* Error state */
|
|
410
|
+
.agent-alert.is-error { border-color:rgba(251,113,133,.5); }
|
|
411
|
+
.agent-alert.is-error .agent-alert-mark { color:#fda4af; border-color:rgba(251,113,133,.5); background:linear-gradient(135deg, rgba(244,63,94,.3), rgba(159,18,57,.22)); box-shadow:0 0 12px rgba(244,63,94,.28); }
|
|
412
|
+
.agent-alert.is-error .agent-alert-title { color:#fda4af; }
|
|
413
|
+
.agent-alert.is-error .agent-alert-msg { color:#fecdd3; }
|
|
414
|
+
/* Info state */
|
|
415
|
+
.agent-alert.is-info { border-color:rgba(84,183,255,.5); }
|
|
416
|
+
.agent-alert.is-info .agent-alert-mark { color:#8ed6ff; border-color:rgba(84,183,255,.5); background:linear-gradient(135deg, rgba(38,130,225,.3), rgba(90,67,183,.24)); box-shadow:0 0 12px rgba(84,183,255,.28); }
|
|
417
|
+
.agent-alert.is-info .agent-alert-title { color:#8ed6ff; }
|
|
418
|
+
.agent-alert.is-info .agent-alert-msg { color:#cfe3fb; }
|
|
384
419
|
.agent-scroll-down { position:fixed; right:1.6rem; bottom:6.5rem; z-index:55; display:grid; place-items:center; width:2.5rem; height:2.5rem; border:1px solid rgba(84,183,255,.4); border-radius:50%; color:#bfe9ff; background:linear-gradient(160deg, rgba(16,38,70,.85), rgba(8,20,42,.9)); box-shadow:0 10px 30px rgba(0,0,0,.35), 0 0 16px rgba(84,183,255,.2); backdrop-filter:blur(18px); font-size:1rem; cursor:pointer; transition:transform .14s, filter .14s; animation:agent-rise .2s cubic-bezier(.22,1,.36,1) both; }
|
|
385
420
|
.agent-scroll-down:hover { filter:brightness(1.15); transform:translateY(-2px); }
|
|
386
421
|
.agent-scroll-down:active { transform:scale(.92); }
|
|
@@ -747,14 +782,6 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
747
782
|
.graphiql-container .graphiql-history-item { color:#cfe3fb; border-color: rgba(112,168,228,.14); background: rgba(8,25,48,.55); }
|
|
748
783
|
.graphiql-container .graphiql-history-item:hover { background: rgba(84,183,255,.1); }
|
|
749
784
|
.graphiql-container .graphiql-history-header { color: #5f7b9c; border-color: rgba(112,168,228,.14); }
|
|
750
|
-
/* History button in GraphiQL sidebar */
|
|
751
|
-
.graphiql-container .graphiql-history-btn {
|
|
752
|
-
position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
|
|
753
|
-
background: none; border: none; color: #6e8aaa; cursor: pointer; padding: 4px 8px;
|
|
754
|
-
border-radius: .4rem; font-size: .7rem; transition: color .14s, background .14s;
|
|
755
|
-
}
|
|
756
|
-
.graphiql-container .graphiql-history-btn:hover { color: #dcecff; background: rgba(84,183,255,.12); }
|
|
757
|
-
.graphiql-container .graphiql-history-btn.active { color: #8ed6ff; background: rgba(84,183,255,.16); box-shadow: 0 0 8px rgba(84,183,255,.25); }
|
|
758
785
|
/* dropdown menus + buttons */
|
|
759
786
|
.graphiql-container .graphiql-dropdown-content { background: linear-gradient(160deg, rgba(12,34,64,.97), rgba(6,16,36,.98)); border:1px solid rgba(99,189,255,.3); box-shadow: 0 16px 40px rgba(0,0,0,.5); }
|
|
760
787
|
.graphiql-container .graphiql-dropdown-item { color: #cfe3fb; }
|
|
@@ -802,11 +829,16 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
802
829
|
.graphiql-container .graphiql-execute-button:hover { filter:brightness(1.15); }
|
|
803
830
|
/* dialogs + tooltips + autocomplete */
|
|
804
831
|
.graphiql-dialog, .graphiql-dialog-overlay { background:rgba(2,8,20,.62); }
|
|
805
|
-
.graphiql-dialog-container { background:linear-gradient(160deg, rgba(
|
|
832
|
+
.graphiql-dialog-container { background:linear-gradient(160deg, rgba(23,55,96,.97), rgba(14,32,62,.98)) !important; border:1px solid rgba(99,189,255,.4); box-shadow:0 30px 90px rgba(0,0,0,.55); backdrop-filter:blur(22px); }
|
|
833
|
+
.graphiql-dialog-close { display:grid; place-items:center; width:1.9rem; height:1.9rem; border:1px solid rgba(251,113,133,.55); border-radius:.55rem; background:linear-gradient(135deg, rgba(244,63,94,.85), rgba(159,18,57,.8)); color:#fff !important; cursor:pointer; box-shadow:0 6px 18px rgba(244,63,94,.4); transition:filter .14s, transform .14s, box-shadow .14s; }
|
|
834
|
+
.graphiql-dialog-close:hover { filter:brightness(1.15); transform:rotate(90deg); box-shadow:0 8px 22px rgba(244,63,94,.55); }
|
|
835
|
+
.graphiql-dialog-close svg { fill:none; stroke:currentColor; stroke-width:2; }
|
|
836
|
+
.graphiql-dialog-title { color:#bfe9ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.7rem; font-weight:800; letter-spacing:.12em; text-transform:uppercase; }
|
|
837
|
+
.graphiql-dialog-section-title { color:#bfe9ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase; }
|
|
806
838
|
.graphiql-container .CodeMirror-hints { background:linear-gradient(160deg, rgba(12,34,64,.97), rgba(6,16,36,.98)); border:1px solid rgba(99,189,255,.3); box-shadow:0 16px 40px rgba(0,0,0,.5); }
|
|
807
839
|
.graphiql-container .CodeMirror-hint { color:#cfe3fb; }
|
|
808
840
|
.graphiql-container .CodeMirror-hint-active { background:rgba(84,183,255,.22); color:#fff; }
|
|
809
|
-
.graphiql-tooltip { background:linear-gradient(160deg, rgba(
|
|
841
|
+
.graphiql-tooltip { background:linear-gradient(160deg, rgba(10,28,55,.97), rgba(6,16,36,.98)); color:#dcecff; border:1px solid rgba(112,168,228,.28); border-radius:.45rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; font-weight:600; letter-spacing:.03em; line-height:1.4; padding:.35rem .6rem; box-shadow:0 12px 32px rgba(0,0,0,.4), inset 0 1px rgba(255,255,255,.06); backdrop-filter:blur(18px) saturate(140%); -webkit-backdrop-filter:blur(18px) saturate(140%); }
|
|
810
842
|
/* GraphiQL variables/editor inputs */
|
|
811
843
|
.graphiql-container input[type=text], .graphiql-container input[type=search], .graphiql-container textarea { background:rgba(255,255,255,.05); border-color:rgba(112,168,228,.24); color:#e8f2ff; }
|
|
812
844
|
.graphiql-container input:focus, .graphiql-container textarea:focus { border-color:rgba(84,183,255,.6); box-shadow:0 0 0 3px rgba(84,183,255,.14); }
|
|
@@ -822,6 +854,10 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
822
854
|
--popover-box-shadow: 0 14px 40px rgba(20,50,100,.14);
|
|
823
855
|
--popover-border: 1px solid rgba(60,110,180,.18);
|
|
824
856
|
}
|
|
857
|
+
[data-theme="light"] .graphiql-tooltip { background:linear-gradient(160deg, rgba(255,255,255,.98), rgba(238,245,255,.98)); color:#0b1220; border-color:rgba(60,110,180,.24); box-shadow:0 12px 32px rgba(20,50,100,.16), inset 0 1px rgba(255,255,255,.7); }
|
|
858
|
+
[data-theme="light"] .graphiql-dialog-close { color:#fff !important; border-color:rgba(225,29,72,.55); background:linear-gradient(135deg, rgba(225,29,72,.85), rgba(159,18,57,.8)); box-shadow:0 6px 18px rgba(225,29,72,.35); }
|
|
859
|
+
[data-theme="light"] .graphiql-dialog-close:hover { filter:brightness(1.12); }
|
|
860
|
+
[data-theme="light"] .graphiql-dialog-title, [data-theme="light"] .graphiql-dialog-section-title { color:#1d6fd0; }
|
|
825
861
|
[data-theme="light"] .graphiql-container .graphiql-editors { background: rgba(255,255,255,.72); border-color: rgba(60,110,180,.16); box-shadow: inset 0 1px rgba(255,255,255,.6), 0 18px 50px rgba(20,50,100,.1); }
|
|
826
862
|
[data-theme="light"] .graphiql-container .graphiql-sidebar { background: rgba(255,255,255,.6); border-right-color: rgba(60,110,180,.14); }
|
|
827
863
|
[data-theme="light"] .graphiql-container .graphiql-toolbar { background: rgba(255,255,255,.55); border-right-color: rgba(60,110,180,.12); }
|
|
@@ -844,8 +880,14 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
844
880
|
[data-theme="light"] .graphiql-container input[type=text], [data-theme="light"] .graphiql-container textarea { background:rgba(255,255,255,.9); color:#16233c; border-color:rgba(60,110,180,.2); }
|
|
845
881
|
/* ── explorer-specific glass (reuses agent tokens) ── */
|
|
846
882
|
.graphiql-panel { display:flex; flex-direction:column; min-width:0; min-height:0; border:1px solid rgba(112,168,228,.18); border-radius:.9rem; background:linear-gradient(160deg, rgba(16,38,70,.45), rgba(8,20,42,.5)); box-shadow:inset 0 1px rgba(255,255,255,.05), 0 18px 50px rgba(0,0,0,.24); backdrop-filter:blur(18px) saturate(135%); -webkit-backdrop-filter:blur(18px) saturate(135%); overflow:hidden; animation:agent-rise .3s cubic-bezier(.22,1,.36,1) both; }
|
|
847
|
-
#graphiql{flex:1; min-height:0}
|
|
848
|
-
.graphiql-container{height:100% !important}
|
|
883
|
+
#graphiql{flex:1; min-height:0; min-width:0; overflow:hidden}
|
|
884
|
+
.graphiql-container{height:100% !important; width:100%; min-width:0; overflow:hidden}
|
|
885
|
+
.graphiql-container .graphiql-editors{min-width:0}
|
|
886
|
+
/* Hide GraphiQL/CodeMirror off-screen measurement nodes (left:-1000px) that
|
|
887
|
+
bleed over the sidebar and block the History toggle button. */
|
|
888
|
+
#graphiql [style*="left: -1000px"], #graphiql [style*="left:-1000px"],
|
|
889
|
+
#graphiql .CodeMirror-measure, #graphiql .CodeMirror-measure *{pointer-events:none !important}
|
|
890
|
+
#graphiql [style*="left: -1000px"], #graphiql [style*="left:-1000px"]{visibility:hidden !important}
|
|
849
891
|
.panel-head{padding:.6rem .85rem; font-size:.6rem; letter-spacing:.13em; text-transform:uppercase; color:#5f7b9c; border-bottom:1px solid rgba(112,168,228,.12); display:flex; justify-content:space-between; align-items:center; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
|
|
850
892
|
.ai-hint{font-size:.6rem; color:#5f7b9c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
|
|
851
893
|
.ops{padding:.6rem .7rem; display:flex; flex-direction:column; gap:.4rem}
|
|
@@ -854,21 +896,45 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
854
896
|
.ops .op b{font-size:.66rem; color:#dcecff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-weight:600}
|
|
855
897
|
.ops .op > span{font-size:.52rem; padding:.14rem .45rem; border-radius:999px; background:rgba(84,183,255,.15); color:#8ed6ff; border:1px solid rgba(84,183,255,.25); font-weight:700; letter-spacing:.06em; text-transform:uppercase; flex:0 0 auto}
|
|
856
898
|
.ops .op.mutation > span{background:rgba(243,110,184,.15); color:#fda4c7; border-color:rgba(243,110,184,.25)}
|
|
857
|
-
.history{padding:.5rem .7rem; border-top:1px solid rgba(112,168,228,.12); max-height:9rem; overflow:auto}
|
|
858
|
-
.history div{font-size:.6rem; padding:.35rem .5rem; border-radius:.45rem; cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#9fb3d0; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
|
|
859
|
-
.history div:hover{background:rgba(84,183,255,.1)}
|
|
899
|
+
.history{padding:.5rem .7rem; border-top:1px solid rgba(112,168,228,.12); max-height:9rem; overflow:auto}
|
|
900
|
+
.history div{font-size:.6rem; padding:.35rem .5rem; border-radius:.45rem; cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#9fb3d0; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
|
|
901
|
+
.history div:hover{background:rgba(84,183,255,.1)}
|
|
902
|
+
.graphiql-history-clear{border:0; background:transparent; color:#5f7b9c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; cursor:pointer; padding:.1rem .3rem; border-radius:.35rem; transition:color .12s, background .12s}
|
|
903
|
+
.graphiql-history-clear:hover{color:#fda4af; background:rgba(244,63,94,.12)}
|
|
904
|
+
.panel-head-actions{display:inline-flex; align-items:center; gap:.25rem}
|
|
905
|
+
.graphiql-history-btn{border:1px solid rgba(112,168,228,.22); background:rgba(84,183,255,.08); color:#8fb8e4; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; cursor:pointer; padding:.12rem .4rem; border-radius:.4rem; transition:color .12s, border-color .12s, background .12s}
|
|
906
|
+
.graphiql-history-btn:hover{color:#bfe9ff; border-color:rgba(84,183,255,.55); background:rgba(84,183,255,.16)}
|
|
907
|
+
.graphiql-history-btn.active{color:#8ed6ff; border-color:rgba(84,183,255,.6); background:rgba(84,183,255,.2); box-shadow:0 0 8px rgba(84,183,255,.25)}
|
|
860
908
|
/* Type details */
|
|
861
|
-
.type-details{padding:0 .7rem .
|
|
862
|
-
.type-details details{border:1px solid rgba(112,168,228,.
|
|
863
|
-
.type-details
|
|
864
|
-
.type-details summary
|
|
865
|
-
.type-details summary
|
|
866
|
-
.type-details
|
|
867
|
-
.type-details .type-
|
|
868
|
-
.type-details .type-
|
|
869
|
-
.type-details .type-
|
|
870
|
-
.type-details .type-
|
|
871
|
-
.type-details .type-
|
|
909
|
+
.type-details{padding:0 .7rem .7rem; display:flex; flex-direction:column; gap:.5rem; min-width:0}
|
|
910
|
+
.type-details details{border:1px solid rgba(112,168,228,.17); border-radius:.55rem; background:rgba(8,25,48,.6); box-shadow:inset 0 1px rgba(255,255,255,.04); overflow:hidden; transition:border-color .14s, background .14s, transform .14s}
|
|
911
|
+
.type-details details:hover{border-color:rgba(84,183,255,.5); background:rgba(84,183,255,.1); transform:translateY(-1px)}
|
|
912
|
+
.type-details summary{cursor:pointer; padding:.5rem .65rem; list-style:none; display:flex; justify-content:space-between; align-items:center; gap:.6rem; font-size:.66rem; color:#dcecff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-weight:700; transition:background .14s}
|
|
913
|
+
.type-details summary::-webkit-details-marker{display:none}
|
|
914
|
+
.type-details summary:hover{background:rgba(84,183,255,.1)}
|
|
915
|
+
.type-details summary .type-name{min-width:0; display:inline-flex; align-items:center; gap:.45rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
|
|
916
|
+
.type-details summary .type-name b{overflow:hidden; text-overflow:ellipsis}
|
|
917
|
+
.type-details .type-icon{flex:0 0 auto; display:grid; place-items:center; width:1.25rem; height:1.25rem; border-radius:.4rem; font-size:.55rem; font-weight:800; letter-spacing:.04em; border:1px solid rgba(84,183,255,.35); background:linear-gradient(135deg, rgba(38,130,225,.3), rgba(90,67,183,.26)); color:#9fd4ff; box-shadow:0 0 10px rgba(84,183,255,.18)}
|
|
918
|
+
.type-details .type-icon.kind-input{color:#c4b5fd; border-color:rgba(139,92,246,.4); background:linear-gradient(135deg, rgba(124,58,237,.3), rgba(90,67,183,.26))}
|
|
919
|
+
.type-details .type-icon.kind-enum{color:#ffd479; border-color:rgba(245,158,11,.4); background:linear-gradient(135deg, rgba(245,158,11,.26), rgba(180,83,9,.2))}
|
|
920
|
+
.type-details .type-icon.kind-scalar{color:#6df0bb; border-color:rgba(52,211,153,.4); background:linear-gradient(135deg, rgba(16,185,129,.26), rgba(6,95,70,.2))}
|
|
921
|
+
.type-details .type-kind{flex:0 0 auto; font-size:.5rem; padding:.12rem .4rem; border-radius:999px; background:rgba(159,123,255,.15); color:#c4b5fd; border:1px solid rgba(139,92,246,.3); font-weight:800; letter-spacing:.08em; text-transform:uppercase}
|
|
922
|
+
.type-details .type-chevron{flex:0 0 auto; display:grid; place-items:center; width:1.1rem; height:1.1rem; border-radius:.35rem; color:#5f7b9c; font-size:.6rem; transition:transform .18s, color .14s, background .14s}
|
|
923
|
+
.type-details summary:hover .type-chevron{color:#9fd4ff; background:rgba(84,183,255,.14)}
|
|
924
|
+
.type-details details[open] .type-chevron{transform:rotate(90deg)}
|
|
925
|
+
.type-details .type-body{padding:.6rem .7rem .7rem .85rem; display:flex; flex-direction:column; gap:.5rem}
|
|
926
|
+
.type-details .type-desc{margin:0; font-size:.58rem; color:#5f7b9c; line-height:1.55; padding:.35rem .6rem; border-radius:.45rem; background:rgba(84,183,255,.05); border-left:2px solid rgba(84,183,255,.3)}
|
|
927
|
+
.type-details .type-subhead{font-size:.5rem; letter-spacing:.13em; text-transform:uppercase; color:#5f7b9c; font-weight:800; padding-left:.15rem}
|
|
928
|
+
.type-details .type-fields{display:flex; flex-direction:column; gap:.4rem; min-width:0}
|
|
929
|
+
.type-details .type-field{font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; padding:.34rem .55rem; border-radius:.45rem; background:rgba(8,25,48,.6); color:#dcecff; display:flex; justify-content:space-between; align-items:center; gap:.5rem; min-width:0; border:1px solid rgba(112,168,228,.17); box-shadow:inset 0 1px rgba(255,255,255,.04); transition:border-color .14s, transform .14s, background .14s}
|
|
930
|
+
.type-details .type-field:hover{border-color:rgba(84,183,255,.5); transform:translateY(-1px); background:rgba(84,183,255,.1)}
|
|
931
|
+
.type-details .type-field > span{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
|
|
932
|
+
.type-details .type-field code.hljs{display:inline; background:transparent; padding:0; color:#e8f2ff; white-space:nowrap}
|
|
933
|
+
.type-details .type-field b{color:#8ed6ff; font-weight:600}
|
|
934
|
+
.type-details .type-field .tok-arg{color:#ffd479}
|
|
935
|
+
.type-details .type-field .tok-type{color:#c4b5fd}
|
|
936
|
+
.type-details .type-field-arrow{flex:0 0 auto; display:inline-flex; align-items:center; gap:.28rem; font-size:.54rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:#9fd4ff; cursor:pointer; padding:.22rem .45rem; border-radius:.4rem; border:1px solid rgba(84,183,255,.35); background:rgba(84,183,255,.1); transition:border-color .12s, background .12s, color .12s, transform .12s}
|
|
937
|
+
.type-details .type-field-arrow:hover{color:#eaf7ff; border-color:rgba(84,183,255,.65); background:rgba(40,137,235,.24); transform:translateX(2px)}
|
|
872
938
|
.type-filter{margin:.45rem .7rem 0; padding:.42rem .6rem; border-radius:.5rem; border:1px solid rgba(112,168,228,.22); background:rgba(20,46,79,.32); color:#e5f4ff; font-size:.62rem; width:calc(100% - 1.4rem); outline:none; transition:border-color .14s, box-shadow .14s}
|
|
873
939
|
.type-filter::placeholder{color:#536c8c}
|
|
874
940
|
.type-filter:focus{border-color:rgba(84,183,255,.55); box-shadow:0 0 0 2px rgba(84,183,255,.15)}
|
|
@@ -887,8 +953,8 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
887
953
|
.ai-chat .msg.ai{align-self:flex-start; align-items:flex-start}
|
|
888
954
|
.ai-chat .bubble{padding:.6rem .8rem; border:1px solid rgba(112,168,228,.22); border-radius:.8rem; color:#dcecff; background:linear-gradient(160deg, rgba(16,38,70,.5), rgba(8,20,42,.56)); font-size:.78rem; line-height:1.65; white-space:pre-wrap; overflow-wrap:anywhere; word-break:break-word; box-shadow:inset 0 1px rgba(255,255,255,.05), 0 14px 42px rgba(0,0,0,.2); backdrop-filter:blur(18px) saturate(135%); -webkit-backdrop-filter:blur(18px) saturate(135%)}
|
|
889
955
|
.ai-chat .user .bubble{border-color:rgba(84,183,255,.42); background:linear-gradient(160deg, rgba(22,70,122,.58), rgba(46,41,112,.5)); color:#eaf7ff}
|
|
890
|
-
.ai-chat .bubble pre{margin:.4rem 0 0; padding:.55rem .7rem; border-radius:.55rem; background
|
|
891
|
-
.ai-chat .bubble pre .hljs{background:transparent; padding:0}
|
|
956
|
+
.ai-chat .bubble pre{margin:.4rem 0 0; padding:.55rem .7rem; border-radius:.55rem; background:#000; border:1px solid rgba(112,168,228,.12); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.64rem; overflow:auto; white-space:pre-wrap}
|
|
957
|
+
.ai-chat .bubble pre .hljs{background:transparent; padding:0; color:#e8f2ff}
|
|
892
958
|
.ai-chat .insert{margin-top:.4rem; padding:.28rem .6rem; border-radius:.45rem; border:1px solid rgba(84,183,255,.4); background:rgba(84,183,255,.12); color:#9fd4ff; font-size:.58rem; font-weight:700; cursor:pointer; transition:border-color .14s, background .14s, transform .14s; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
|
|
893
959
|
.ai-chat .insert:hover{border-color:rgba(84,183,255,.75); color:#fff; background:rgba(40,137,235,.28); transform:translateY(-1px)}
|
|
894
960
|
.ai-chat .typing{font-size:.6rem; color:#6e8aaa; font-style:italic}
|
|
@@ -899,9 +965,7 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
899
965
|
.ai-form button{padding:.55rem 1rem; border:1px solid rgba(84,183,255,.55); border-radius:.55rem; color:#fff; background:linear-gradient(135deg, rgba(38,130,225,.75), rgba(90,67,183,.65)); box-shadow:0 8px 24px rgba(84,183,255,.28); font-size:.7rem; font-weight:800; cursor:pointer; white-space:nowrap; transition:transform .15s, filter .15s}
|
|
900
966
|
.ai-form button:hover:not(:disabled){filter:brightness(1.1); transform:translateY(-1px)}
|
|
901
967
|
.ai-form button:disabled{opacity:.4; cursor:not-allowed}
|
|
902
|
-
/* Theme
|
|
903
|
-
.theme-toggle{display:grid; place-items:center; width:2.1rem; height:2.1rem; padding:0; border:1px solid rgba(112,168,228,.24); border-radius:.6rem; color:#8fb8e4; background:rgba(84,183,255,.08); font-size:.9rem; line-height:1; cursor:pointer; transition:border-color .15s, color .15s, background .15s, transform .15s}
|
|
904
|
-
.theme-toggle:hover{border-color:rgba(84,183,255,.55); color:#bfe9ff; background:rgba(84,183,255,.16); transform:translateY(-1px)}
|
|
968
|
+
/* Theme (dark only) */
|
|
905
969
|
/* Light theme overrides */
|
|
906
970
|
[data-theme="light"] body{background:#eef3fb; color:#0b1220}
|
|
907
971
|
[data-theme="light"] .agent-shell{background:#eef3fb}
|
|
@@ -915,16 +979,22 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
915
979
|
[data-theme="light"] .ops .op{background:rgba(255,255,255,.72); border-color:rgba(60,110,180,.16); box-shadow:inset 0 1px rgba(255,255,255,.6)}
|
|
916
980
|
[data-theme="light"] .ops .op b{color:#0b1220}
|
|
917
981
|
[data-theme="light"] .ops .op:hover{background:rgba(84,183,255,.14)}
|
|
918
|
-
[data-theme="light"] .type-details details{background:rgba(255,255,255,.72); border-color:rgba(60,110,180,.16)}
|
|
919
|
-
[data-theme="light"] .type-details
|
|
920
|
-
[data-theme="light"] .type-details
|
|
921
|
-
[data-theme="light"] .type-details
|
|
982
|
+
[data-theme="light"] .type-details details{background:rgba(255,255,255,.72); border-color:rgba(60,110,180,.16); box-shadow:inset 0 1px rgba(255,255,255,.6)}
|
|
983
|
+
[data-theme="light"] .type-details details:hover{background:rgba(84,183,255,.14); border-color:rgba(60,110,180,.3)}
|
|
984
|
+
[data-theme="light"] .type-details summary{color:#0b1220}
|
|
985
|
+
[data-theme="light"] .type-details summary:hover{background:rgba(84,183,255,.14)}
|
|
986
|
+
[data-theme="light"] .type-details .type-desc{color:#5a6b84; background:rgba(60,110,180,.06); border-left-color:rgba(60,110,180,.35)}
|
|
987
|
+
[data-theme="light"] .type-details .type-field{background:rgba(255,255,255,.72); color:#0b1220; border-color:rgba(60,110,180,.16); box-shadow:inset 0 1px rgba(255,255,255,.6)}
|
|
988
|
+
[data-theme="light"] .type-details .type-field:hover{background:rgba(84,183,255,.14); border-color:rgba(60,110,180,.3)}
|
|
989
|
+
[data-theme="light"] .type-details .type-field b{color:#1d6fd0}
|
|
990
|
+
[data-theme="light"] .type-details .type-field .hljs{color:#0b1220}
|
|
922
991
|
[data-theme="light"] .ai-hint, [data-theme="light"] .type-details .type-desc{color:#5a6b84}
|
|
923
992
|
[data-theme="light"] .history div{color:#3a4c68}
|
|
924
993
|
[data-theme="light"] .ai-popup{background:linear-gradient(160deg, rgba(255,255,255,.96), rgba(238,245,255,.97)); border-color:rgba(60,110,180,.2)}
|
|
925
994
|
[data-theme="light"] .ai-chat .bubble{background:rgba(240,246,255,.8); color:#16233c; border-color:rgba(60,110,180,.18)}
|
|
926
995
|
[data-theme="light"] .ai-chat .user .bubble{background:linear-gradient(160deg, rgba(84,183,255,.35), rgba(120,110,230,.3)); color:#0b1220}
|
|
927
|
-
[data-theme="light"] .ai-chat .bubble pre{background:rgba(
|
|
996
|
+
[data-theme="light"] .ai-chat .bubble pre{background:#000; border-color:rgba(112,168,228,.15)}
|
|
997
|
+
[data-theme="light"] .ai-chat .bubble pre .hljs{color:#e8f2ff}
|
|
928
998
|
[data-theme="light"] .type-filter{background:rgba(255,255,255,.8); color:#0b1220; border-color:rgba(60,110,180,.2)}
|
|
929
999
|
@media (max-width: 960px){ .ai-popup{right:.75rem; left:.75rem; width:auto} }
|
|
930
1000
|
</style>
|
|
@@ -944,15 +1014,14 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
944
1014
|
</span>
|
|
945
1015
|
<span class="agent-spacer"></span>
|
|
946
1016
|
<span class="agent-status"><span class="agent-status-dot"></span>Live</span>
|
|
947
|
-
<span class="agent-select-wrap"><span class="agent-select-label">POST</span><span class="agent-select-label" style="color:#9fd4ff">${escapeHtml(endpoint)}</span></span>
|
|
948
|
-
|
|
949
|
-
</div>
|
|
1017
|
+
<span class="agent-select-wrap"><span class="agent-select-label">POST</span><span class="agent-select-label" style="color:#9fd4ff">${escapeHtml(endpoint)}</span></span>
|
|
1018
|
+
</div>
|
|
950
1019
|
<div class="agent-urlbar">
|
|
951
1020
|
<span class="agent-urlbar-lock" title="Secure connection">🔒</span>
|
|
952
1021
|
<span class="agent-urlbar-input">
|
|
953
1022
|
<input class="agent-urlbar-field" id="urlbar" type="text" aria-label="GraphQL endpoint URL" spellcheck="false" />
|
|
954
1023
|
</span>
|
|
955
|
-
<button class="agent-urlbar-btn" id="urlbar-copy" aria-label="Copy URL" title="Copy URL"
|
|
1024
|
+
<button class="agent-urlbar-btn" id="urlbar-copy" aria-label="Copy URL" title="Copy URL"><svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
|
|
956
1025
|
<button class="agent-urlbar-btn" id="urlbar-refresh" aria-label="Reload explorer" title="Reload">↻</button>
|
|
957
1026
|
<span class="agent-urlbar-note">POST</span>
|
|
958
1027
|
</div>
|
|
@@ -973,9 +1042,12 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
973
1042
|
<span>${op.type}</span>
|
|
974
1043
|
</div>`).join('') : `<div class="ai-hint" style="padding:8px">No typedefs discovered yet. <b>hello</b> is always available — try <code>{ hello }</code> below.<br>Add more: <code>npx nexus make:subgraph post --app backend</code> (creates a real Post model).<br><br>SDL preview:<br><pre style="white-space:pre-wrap; font-size:11px; opacity:.7">${escapeHtml(rawSdl.slice(0,600) || 'empty')}</pre></div>`}
|
|
975
1044
|
</div>
|
|
976
|
-
<div class="ai-hint" style="padding:0 .8rem .6rem">Click a field to insert its example. Open <b>Docs</b> in GraphiQL (left) to browse full schema. Builtin: <code>hello</code> + <code>ping(message)</code> always visible; added subgraphs appear live via introspection.</div>
|
|
977
|
-
|
|
978
|
-
<div class="panel-head" style="margin-top:.4rem; border-radius:.6rem .6rem 0 0"><span>
|
|
1045
|
+
<div class="ai-hint" style="padding:0 .8rem .6rem">Click a field to insert its example. Open <b>Docs</b> in GraphiQL (left) to browse full schema. Builtin: <code>hello</code> + <code>ping(message)</code> always visible; added subgraphs appear live via introspection.</div>
|
|
1046
|
+
|
|
1047
|
+
<div class="panel-head" style="margin-top:.4rem; border-radius:.6rem .6rem 0 0"><span>History</span><span class="panel-head-actions"><button id="history-clear" class="graphiql-history-clear" title="Clear history">Clear</button><button id="history-toggle" class="graphiql-history-btn" title="Toggle history">Show</button></span></div>
|
|
1048
|
+
<div class="history" id="history" hidden><div class="ai-hint" style="padding:8px">No history yet</div></div>
|
|
1049
|
+
|
|
1050
|
+
<div class="panel-head" style="margin-top:.4rem; border-radius:.6rem .6rem 0 0"><span>Type Details</span><span style="opacity:.6" id="types-count">loading…</span></div>
|
|
979
1051
|
<input class="type-filter" id="type-filter" placeholder="Filter types/fields…" />
|
|
980
1052
|
<div class="type-details" id="types"><div class="ai-hint" style="padding:8px">Loading complete schema via introspection…</div></div>
|
|
981
1053
|
|
|
@@ -983,7 +1055,17 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
983
1055
|
</main>
|
|
984
1056
|
</div>
|
|
985
1057
|
|
|
986
|
-
<!--
|
|
1058
|
+
<!-- Alert toast (top-right corner) — shows URL copied / query copied / success / failure -->
|
|
1059
|
+
<div class="agent-alert hide" id="agent-alert" role="status">
|
|
1060
|
+
<div class="agent-alert-mark" id="agent-alert-mark">✓</div>
|
|
1061
|
+
<div class="agent-alert-body">
|
|
1062
|
+
<strong class="agent-alert-title" id="agent-alert-title">Success</strong>
|
|
1063
|
+
<span class="agent-alert-msg" id="agent-alert-msg"></span>
|
|
1064
|
+
</div>
|
|
1065
|
+
<button class="agent-alert-close" id="agent-alert-close" aria-label="Dismiss" title="Dismiss">×</button>
|
|
1066
|
+
</div>
|
|
1067
|
+
|
|
1068
|
+
<!-- AI Assistant FAB + Chat Popup -->
|
|
987
1069
|
<button id="ai-fab" class="fab" aria-label="AI Assistant" title="AI Assistant">✦</button>
|
|
988
1070
|
<div id="ai-popup" class="ai-popup" hidden>
|
|
989
1071
|
<div class="ai-popup-head"><span>AI Assistant <span style="opacity:.6" id="ai-model-label"></span></span><button id="ai-close" aria-label="Close">×</button></div>
|
|
@@ -1004,7 +1086,49 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
1004
1086
|
const sdlPreview = ${JSON.stringify(sdlPreview)};
|
|
1005
1087
|
const rawSdlClient = ${JSON.stringify(rawSdl.slice(0,4000))};
|
|
1006
1088
|
const STORAGE_KEY = 'nexus-graphql-history';
|
|
1007
|
-
|
|
1089
|
+
|
|
1090
|
+
// \u2192 Unified tooltips: convert every native title to a styled data-tip
|
|
1091
|
+
// so all tooltips share one look (header, urlbar, history, FAB, fields...).
|
|
1092
|
+
function initTooltips(root){
|
|
1093
|
+
(root || document).querySelectorAll('[title]').forEach(function(el){
|
|
1094
|
+
if (el.hasAttribute('data-tip') || el.getAttribute('title') === '') return;
|
|
1095
|
+
el.setAttribute('data-tip', el.getAttribute('title'));
|
|
1096
|
+
el.removeAttribute('title');
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
initTooltips(document);
|
|
1100
|
+
|
|
1101
|
+
// \u2192 Alert toast (top-right) — show success / error / info messages.
|
|
1102
|
+
const agentAlert = document.getElementById('agent-alert');
|
|
1103
|
+
const agentAlertClose = document.getElementById('agent-alert-close');
|
|
1104
|
+
const agentAlertMark = document.getElementById('agent-alert-mark');
|
|
1105
|
+
const agentAlertTitle = document.getElementById('agent-alert-title');
|
|
1106
|
+
const agentAlertMsg = document.getElementById('agent-alert-msg');
|
|
1107
|
+
let agentAlertTimer = null;
|
|
1108
|
+
function showAlert(title, msg, kind){
|
|
1109
|
+
if (!agentAlert || !agentAlertTitle || !agentAlertMsg) return;
|
|
1110
|
+
kind = kind || 'success';
|
|
1111
|
+
agentAlert.classList.remove('is-error', 'is-info');
|
|
1112
|
+
if (kind === 'error') agentAlert.classList.add('is-error');
|
|
1113
|
+
else if (kind === 'info') agentAlert.classList.add('is-info');
|
|
1114
|
+
agentAlertMark.textContent = kind === 'error' ? '!' : kind === 'info' ? 'i' : '✓';
|
|
1115
|
+
agentAlertTitle.textContent = title;
|
|
1116
|
+
agentAlertMsg.textContent = msg;
|
|
1117
|
+
agentAlert.classList.remove('hide');
|
|
1118
|
+
agentAlert.classList.add('show');
|
|
1119
|
+
if (agentAlertTimer) clearTimeout(agentAlertTimer);
|
|
1120
|
+
agentAlertTimer = setTimeout(function(){
|
|
1121
|
+
agentAlert.classList.remove('show');
|
|
1122
|
+
agentAlert.classList.add('hide');
|
|
1123
|
+
}, 3500);
|
|
1124
|
+
}
|
|
1125
|
+
if (agentAlertClose) {
|
|
1126
|
+
agentAlertClose.addEventListener('click', function(){
|
|
1127
|
+
agentAlert.classList.remove('show');
|
|
1128
|
+
agentAlert.classList.add('hide');
|
|
1129
|
+
if (agentAlertTimer) clearTimeout(agentAlertTimer);
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1008
1132
|
|
|
1009
1133
|
// \u2192 Editable/copyable URL bar (below the header) — shows the live GraphQL endpoint.
|
|
1010
1134
|
const urlbar = document.getElementById('urlbar');
|
|
@@ -1019,17 +1143,21 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
1019
1143
|
const val = urlbar.value;
|
|
1020
1144
|
function done() {
|
|
1021
1145
|
copyBtn.classList.add('copied');
|
|
1022
|
-
copyBtn.
|
|
1146
|
+
copyBtn.innerHTML = '<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>';
|
|
1147
|
+
showAlert('URL copied', 'GraphQL endpoint copied to clipboard.', 'success');
|
|
1023
1148
|
setTimeout(function () {
|
|
1024
1149
|
copyBtn.classList.remove('copied');
|
|
1025
|
-
copyBtn.
|
|
1150
|
+
copyBtn.innerHTML = '<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
|
|
1026
1151
|
}, 1200);
|
|
1027
1152
|
}
|
|
1153
|
+
function fail() {
|
|
1154
|
+
showAlert('Copy failed', 'Could not copy the URL to your clipboard.', 'error');
|
|
1155
|
+
}
|
|
1028
1156
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1029
|
-
navigator.clipboard.writeText(val).then(done, function () { urlbar.select(); try { document.execCommand('copy'); done(); } catch (e) {} });
|
|
1157
|
+
navigator.clipboard.writeText(val).then(done, function () { urlbar.select(); try { document.execCommand('copy'); done(); } catch (e) { fail(); } });
|
|
1030
1158
|
} else {
|
|
1031
1159
|
urlbar.select();
|
|
1032
|
-
try { document.execCommand('copy'); done(); } catch (e) {}
|
|
1160
|
+
try { document.execCommand('copy'); done(); } catch (e) { fail(); }
|
|
1033
1161
|
}
|
|
1034
1162
|
});
|
|
1035
1163
|
}
|
|
@@ -1046,48 +1174,12 @@ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
|
|
|
1046
1174
|
};
|
|
1047
1175
|
}
|
|
1048
1176
|
|
|
1049
|
-
let csrfToken = null;
|
|
1177
|
+
let csrfToken = null;
|
|
1050
1178
|
|
|
1051
|
-
// \
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
historyBtn.className = 'graphiql-history-btn'
|
|
1056
|
-
historyBtn.setAttribute('aria-label', 'Toggle history')
|
|
1057
|
-
historyBtn.textContent = 'History'
|
|
1058
|
-
graphiqlSidebar.appendChild(historyBtn)
|
|
1059
|
-
historyBtn.addEventListener('click', function() {
|
|
1060
|
-
const historyPanel = document.getElementById('history')
|
|
1061
|
-
if (historyPanel) {
|
|
1062
|
-
historyPanel.hidden = !historyPanel.hidden
|
|
1063
|
-
this.classList.toggle('active', !historyPanel.hidden)
|
|
1064
|
-
}
|
|
1065
|
-
})
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
// \u2192 Theme toggle (dark default, persisted, respects prefers-color-scheme) ──
|
|
1069
|
-
// Also flips body.graphiql-dark / graphiql-light so GraphiQL's own CDN
|
|
1070
|
-
// dark palette applies (it ignores [data-theme] otherwise).
|
|
1071
|
-
function applyTheme(t){
|
|
1072
|
-
try{ localStorage.setItem(THEME_KEY, t); }catch(e){}
|
|
1073
|
-
document.documentElement.setAttribute('data-theme', t);
|
|
1074
|
-
document.body.classList.toggle('graphiql-dark', t === 'dark');
|
|
1075
|
-
document.body.classList.toggle('graphiql-light', t === 'light');
|
|
1076
|
-
const b = document.getElementById('theme-toggle');
|
|
1077
|
-
if(b) b.textContent = t === 'light' ? '☀️' : '🌙';
|
|
1078
|
-
}
|
|
1079
|
-
(function initTheme(){
|
|
1080
|
-
let saved = null;
|
|
1081
|
-
try{ saved = localStorage.getItem(THEME_KEY); }catch(e){}
|
|
1082
|
-
if(saved === 'light' || saved === 'dark'){ applyTheme(saved); return; }
|
|
1083
|
-
if(window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches){ applyTheme('light'); return; }
|
|
1084
|
-
applyTheme('dark');
|
|
1085
|
-
})();
|
|
1086
|
-
const themeBtn = document.getElementById('theme-toggle');
|
|
1087
|
-
if(themeBtn) themeBtn.addEventListener('click', function(){
|
|
1088
|
-
const cur = document.documentElement.getAttribute('data-theme');
|
|
1089
|
-
applyTheme(cur === 'light' ? 'dark' : 'light');
|
|
1090
|
-
});
|
|
1179
|
+
// \u2192 Dark-only theme — force GraphiQL's dark palette (its CDN ignores
|
|
1180
|
+
// [data-theme] unless body.graphiql-dark is present).
|
|
1181
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
1182
|
+
document.body.classList.add('graphiql-dark');
|
|
1091
1183
|
|
|
1092
1184
|
// ── highlight.js helper (mirrors Agent.tsx highlightCode) ──
|
|
1093
1185
|
function escapeHtml(s){ return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
|
@@ -1144,9 +1236,17 @@ async function fetcher(params, opts) {
|
|
|
1144
1236
|
if(!r.ok) throw new Error('GraphiQL request failed: ' + r.status);
|
|
1145
1237
|
if (typeof window.__updateUrlbar === 'function' && params && params.query) window.__updateUrlbar(params.query, token);
|
|
1146
1238
|
return r.json();
|
|
1239
|
+
}).then(function(json){
|
|
1240
|
+
if (json && json.errors && json.errors.length) {
|
|
1241
|
+
showAlert('Query error', (json.errors[0] && json.errors[0].message) || 'The query returned errors.', 'error');
|
|
1242
|
+
} else if (params && params.query && params.query.indexOf('__schema') === -1) {
|
|
1243
|
+
showAlert('Success', 'Query executed successfully.', 'success');
|
|
1244
|
+
}
|
|
1245
|
+
return json;
|
|
1147
1246
|
});
|
|
1148
1247
|
} catch(e) {
|
|
1149
1248
|
console.error('GraphiQL fetcher error:', e);
|
|
1249
|
+
showAlert('Request failed', (e && e.message) || 'Could not reach the GraphQL endpoint.', 'error');
|
|
1150
1250
|
return Promise.reject(e);
|
|
1151
1251
|
}
|
|
1152
1252
|
}
|
|
@@ -1160,18 +1260,41 @@ async function fetcher(params, opts) {
|
|
|
1160
1260
|
renderHistory();
|
|
1161
1261
|
} catch {}
|
|
1162
1262
|
}
|
|
1163
|
-
function renderHistory() {
|
|
1164
|
-
const el = document.getElementById('history');
|
|
1165
|
-
if (!el) return;
|
|
1166
|
-
try {
|
|
1167
|
-
const list = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
|
1168
|
-
el.innerHTML = list.length ? list.map(q => \`<div title="\${q.replace(/"/g,'"')}">\${q.slice(0,80)}</div>\`).join('') : '<div style="opacity:.5">No history yet</div>';
|
|
1169
|
-
el.querySelectorAll('div').forEach(div => div.addEventListener('click', () => {
|
|
1170
|
-
const q = div.getAttribute('title');
|
|
1171
|
-
if (window._graphiql) window._graphiql.setQuery(q);
|
|
1172
|
-
}));
|
|
1173
|
-
} catch {}
|
|
1174
|
-
}
|
|
1263
|
+
function renderHistory() {
|
|
1264
|
+
const el = document.getElementById('history');
|
|
1265
|
+
if (!el) return;
|
|
1266
|
+
try {
|
|
1267
|
+
const list = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
|
1268
|
+
el.innerHTML = list.length ? list.map(q => \`<div data-tip="\${q.replace(/"/g,'"')}" title="\${q.replace(/"/g,'"')}">\${q.slice(0,80)}</div>\`).join('') : '<div style="opacity:.5">No history yet</div>';
|
|
1269
|
+
el.querySelectorAll('div').forEach(div => div.addEventListener('click', () => {
|
|
1270
|
+
const q = div.getAttribute('title');
|
|
1271
|
+
if (window._graphiql) window._graphiql.setQuery(q);
|
|
1272
|
+
}));
|
|
1273
|
+
} catch {}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
const historyClearBtn = document.getElementById('history-clear');
|
|
1277
|
+
if (historyClearBtn) {
|
|
1278
|
+
historyClearBtn.addEventListener('click', () => {
|
|
1279
|
+
try { localStorage.removeItem(STORAGE_KEY); } catch {}
|
|
1280
|
+
renderHistory();
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const historyToggleBtn = document.getElementById('history-toggle');
|
|
1285
|
+
const historyPanel = document.getElementById('history');
|
|
1286
|
+
if (historyToggleBtn && historyPanel) {
|
|
1287
|
+
const sync = () => {
|
|
1288
|
+
historyToggleBtn.textContent = historyPanel.hidden ? 'Show' : 'Hide';
|
|
1289
|
+
historyToggleBtn.classList.toggle('active', !historyPanel.hidden);
|
|
1290
|
+
};
|
|
1291
|
+
sync();
|
|
1292
|
+
historyToggleBtn.addEventListener('click', () => {
|
|
1293
|
+
historyPanel.hidden = !historyPanel.hidden;
|
|
1294
|
+
sync();
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1175
1298
|
|
|
1176
1299
|
// Mount GraphiQL
|
|
1177
1300
|
const defaultQuery = ${JSON.stringify(defaultQuery)};
|
|
@@ -1201,9 +1324,9 @@ async function fetcher(params, opts) {
|
|
|
1201
1324
|
});
|
|
1202
1325
|
});
|
|
1203
1326
|
}
|
|
1204
|
-
bindOps();
|
|
1205
|
-
renderHistory();
|
|
1206
|
-
|
|
1327
|
+
bindOps();
|
|
1328
|
+
renderHistory();
|
|
1329
|
+
|
|
1207
1330
|
// Live introspection refresh — discovers hello + any added subgraph and renders full type details
|
|
1208
1331
|
(function refreshOpsFromIntrospection(){
|
|
1209
1332
|
const INTROSPECTION_QUERY = '{ __schema { queryType { name fields { name description args { name description type { name kind ofType { name kind ofType { name kind } } } } type { name kind ofType { name kind ofType { name kind } } } } } mutationType { name fields { name description args { name description type { name kind ofType { name kind } } } type { name kind ofType { name kind } } } } subscriptionType { name fields { name description args { name description type { name kind ofType { name kind } } } type { name kind ofType { name kind } } } } types { name kind description fields(includeDeprecated:false) { name description args { name description type { name kind ofType { name kind } } } type { name kind ofType { name kind ofType { name kind } } } } inputFields { name description type { name kind ofType { name kind } } } enumValues { name description } } } }';
|
|
@@ -1269,31 +1392,55 @@ async function fetcher(params, opts) {
|
|
|
1269
1392
|
const q = (filter||'').toLowerCase();
|
|
1270
1393
|
const filtered = q ? userTypes.filter(function(t){ return t.name.toLowerCase().indexOf(q)!==-1 || (t.fields && t.fields.some(function(f){ return f.name.toLowerCase().indexOf(q)!==-1; })); }) : userTypes;
|
|
1271
1394
|
if (!filtered.length){ typesEl.innerHTML = '<div class="ai-hint" style="padding:8px">No types match “' + escapeTd(filter) + '”.</div>'; return; }
|
|
1272
|
-
typesEl.innerHTML = filtered.map(function(t){
|
|
1273
|
-
const kind = t.kind;
|
|
1274
|
-
const
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
fieldsHtml = '<
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1395
|
+
typesEl.innerHTML = filtered.map(function(t){
|
|
1396
|
+
const kind = t.kind || 'OBJECT';
|
|
1397
|
+
const kindIcon = kind==='INPUT_OBJECT' ? '( )' : kind==='ENUM' ? 'A-Z' : kind==='SCALAR' ? 'S' : 'T';
|
|
1398
|
+
const iconCls = kind==='INPUT_OBJECT' ? 'kind-input' : kind==='ENUM' ? 'kind-enum' : kind==='SCALAR' ? 'kind-scalar' : '';
|
|
1399
|
+
const kindLabel = kind.replace(/_/g,' ').toLowerCase();
|
|
1400
|
+
const desc = t.description ? '<div class="type-desc">' + escapeTd(t.description.slice(0,300)) + '</div>' : '';
|
|
1401
|
+
function fieldSig(f){
|
|
1402
|
+
const fType = typeToString(f.type);
|
|
1403
|
+
const fArgs = f.args && f.args.length ? '(' + f.args.map(function(a){ return '<span class="tok-arg">' + escapeTd(a.name) + '</span>: ' + '<span class="tok-type">' + escapeTd(typeToString(a.type)) + '</span>'; }).join(', ') + ')' : '';
|
|
1404
|
+
return escapeTd(f.name) + fArgs + ': ' + '<span class="tok-type">' + escapeTd(fType) + '</span>';
|
|
1405
|
+
}
|
|
1406
|
+
let fieldsHtml = '';
|
|
1407
|
+
if (t.fields && t.fields.length){
|
|
1408
|
+
fieldsHtml = '<span class="type-subhead">Fields</span><div class="type-fields">' + t.fields.map(function(f){
|
|
1409
|
+
const fDesc = f.description ? '<span style="opacity:.6"> — ' + escapeTd(f.description.slice(0,70)) + '</span>' : '';
|
|
1410
|
+
return '<div class="type-field"><span><code class="hljs">' + fieldSig(f) + '</code>' + fDesc + '</span><span class="type-field-arrow" data-field-query="' + escapeTd(f.name) + '" title="Insert ' + escapeTd(f.name) + '">Insert →</span></div>';
|
|
1411
|
+
}).join('') + '</div>';
|
|
1412
|
+
} else if (t.inputFields && t.inputFields.length){
|
|
1413
|
+
fieldsHtml = '<span class="type-subhead">Input Fields</span><div class="type-fields">' + t.inputFields.map(function(f){
|
|
1414
|
+
const fType = typeToString(f.type);
|
|
1415
|
+
return '<div class="type-field"><span><code class="hljs">' + escapeTd(f.name) + ': ' + '<span class="tok-type">' + escapeTd(fType) + '</span></code></span></div>';
|
|
1416
|
+
}).join('') + '</div>';
|
|
1417
|
+
} else if (t.enumValues && t.enumValues.length){
|
|
1418
|
+
fieldsHtml = '<span class="type-subhead">Enum Values</span><div class="type-fields">' + t.enumValues.map(function(v){
|
|
1419
|
+
return '<div class="type-field"><span><b>' + escapeTd(v.name) + '</b>' + (v.description ? '<span style="opacity:.6"> — ' + escapeTd(v.description.slice(0,50)) + '</span>' : '') + '</span></div>';
|
|
1420
|
+
}).join('') + '</div>';
|
|
1421
|
+
}
|
|
1422
|
+
const open = (t.name==='Query' || t.name==='Post' || t.name==='Message') ? ' open' : '';
|
|
1423
|
+
return '<details' + open + '><summary><span class="type-name"><span class="type-icon ' + iconCls + '">' + kindIcon + '</span><b>' + escapeTd(t.name) + '</b></span><span style="display:inline-flex;align-items:center;gap:.35rem;flex:0 0 auto"><span class="type-kind">' + escapeTd(kindLabel) + '</span><span class="type-chevron">›</span></span></summary><div class="type-body">' + desc + fieldsHtml + '</div></details>';
|
|
1424
|
+
}).join('');
|
|
1425
|
+
initTooltips(typesEl);
|
|
1296
1426
|
}
|
|
1427
|
+
renderTypes('');
|
|
1428
|
+
// Insert a field's name into the editor when its chip is clicked
|
|
1429
|
+
// (delegated so re-renders after filtering keep working).
|
|
1430
|
+
typesEl.addEventListener('click', function(ev){
|
|
1431
|
+
var t = ev.target;
|
|
1432
|
+
if (!t || !t.classList) return;
|
|
1433
|
+
if (t.classList.contains('type-field-arrow')){
|
|
1434
|
+
var name = t.getAttribute('data-field-query');
|
|
1435
|
+
if (name && window._graphiql){
|
|
1436
|
+
window._graphiql.setQuery('{ ' + name + ' }');
|
|
1437
|
+
showAlert('Query copied', 'Inserted { ' + name + ' } into the editor.', 'success');
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
if (filterEl){
|
|
1442
|
+
filterEl.addEventListener('input', function(){ renderTypes(filterEl.value); });
|
|
1443
|
+
}
|
|
1297
1444
|
}).catch(function(){
|
|
1298
1445
|
const el=document.getElementById('types'); if(el) el.innerHTML='<div class="ai-hint" style="padding:8px">Introspection failed. SDL preview:<pre style="white-space:pre-wrap; font-size:11px; opacity:.7">' + escapeTd(rawSdlClient.slice(0,800) || 'empty') + '</pre></div>';
|
|
1299
1446
|
const c=document.getElementById('types-count'); if(c) c.textContent='—';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parse, type DocumentNode, type ExecutionResult } from 'graphql';
|
|
2
2
|
import type { Subgraph, GraphQLContext, ExecuteParams } from '../types.js';
|
|
3
3
|
import { composeSupergraph, type Supergraph } from './composeSupergraph.js';
|
|
4
|
-
import { executeFederated, planQuery, type FetchPlan } from './executor.js';
|
|
4
|
+
import { executeFederated, subscribeFederated, planQuery, type FetchPlan } from './executor.js';
|
|
5
5
|
|
|
6
6
|
export interface FederatedGateway {
|
|
7
7
|
/** Client-facing merged schema. */
|
|
@@ -9,6 +9,7 @@ export interface FederatedGateway {
|
|
|
9
9
|
supergraph: Supergraph;
|
|
10
10
|
subgraphs: Subgraph[];
|
|
11
11
|
execute(params: Omit<ExecuteParams, 'schema' | 'resolvers'>): Promise<ExecutionResult>;
|
|
12
|
+
subscribe(params: Omit<ExecuteParams, 'schema' | 'resolvers'>): Promise<AsyncIterable<ExecutionResult> | ExecutionResult>;
|
|
12
13
|
/** Inspect the fetch plan for a query (golden tests / debugging). */
|
|
13
14
|
plan(document: DocumentNode): FetchPlan;
|
|
14
15
|
}
|
|
@@ -29,6 +30,10 @@ export function createFederatedGateway(subgraphs: Subgraph[]): FederatedGateway
|
|
|
29
30
|
const document: DocumentNode = params.document;
|
|
30
31
|
return executeFederated(supergraph, document, params.variableValues, (params.contextValue as GraphQLContext) ?? {});
|
|
31
32
|
},
|
|
33
|
+
async subscribe(params) {
|
|
34
|
+
const document: DocumentNode = params.document;
|
|
35
|
+
return subscribeFederated(supergraph, document, params.variableValues, (params.contextValue as GraphQLContext) ?? {});
|
|
36
|
+
},
|
|
32
37
|
plan(document) {
|
|
33
38
|
return planQuery(supergraph, document);
|
|
34
39
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execute, parse, isObjectType, isInterfaceType, type DocumentNode, type SelectionSetNode, type FieldNode, type InlineFragmentNode, type FragmentSpreadNode, type ExecutionResult } from 'graphql';
|
|
1
|
+
import { execute, subscribe, parse, isObjectType, isInterfaceType, type DocumentNode, type SelectionSetNode, type FieldNode, type InlineFragmentNode, type FragmentSpreadNode, type FragmentDefinitionNode, type ExecutionResult } from 'graphql';
|
|
2
2
|
|
|
3
3
|
/** Introspection root fields (`__schema`/`__type`) live on the merged schema, not any one subgraph. */
|
|
4
4
|
function isIntrospectionSelection(sel: SelectionSetNode): boolean {
|
|
@@ -8,7 +8,7 @@ function isIntrospectionSelection(sel: SelectionSetNode): boolean {
|
|
|
8
8
|
}
|
|
9
9
|
import type { Supergraph } from './composeSupergraph.js';
|
|
10
10
|
import type { Subgraph, GraphQLContext } from '../types.js';
|
|
11
|
-
import { createFieldResolver } from '../gateway/fieldResolver.js';
|
|
11
|
+
import { createFieldResolver, createSubscribeFieldResolver } from '../gateway/fieldResolver.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Naive sequential federated executor (Phase 6, v1).
|
|
@@ -74,7 +74,7 @@ export async function executeFederated(
|
|
|
74
74
|
contextValue: GraphQLContext,
|
|
75
75
|
): Promise<ExecutionResult> {
|
|
76
76
|
const op = document.definitions.find((d) => d.kind === 'OperationDefinition') as
|
|
77
|
-
| { selectionSet: SelectionSetNode; operation: string }
|
|
77
|
+
| { selectionSet: SelectionSetNode; operation: string; variableDefinitions?: readonly { variable?: { name?: { value?: string } } }[] }
|
|
78
78
|
| undefined;
|
|
79
79
|
if (!op) return { errors: [{ message: 'No operation in document' }] as unknown as NonNullable<ExecutionResult['errors']> };
|
|
80
80
|
const rootType = op.operation === 'mutation' ? 'Mutation' : op.operation === 'subscription' ? 'Subscription' : 'Query';
|
|
@@ -109,7 +109,7 @@ export async function executeFederated(
|
|
|
109
109
|
for (const [subName, fields] of rootGroups) {
|
|
110
110
|
const sub = subgraphByName.get(subName)!;
|
|
111
111
|
const sel = trimSelection(fields, rootType, subName, supergraph, true);
|
|
112
|
-
const sdl = `${op.operation} { ${sel} }`;
|
|
112
|
+
const sdl = `${op.operation}${varDefsToSdl(op.variableDefinitions)} { ${sel} }`;
|
|
113
113
|
const res = await execSubgraph(sub, parse(sdl), variableValues, contextValue);
|
|
114
114
|
if (res.errors) return { errors: res.errors };
|
|
115
115
|
Object.assign(data, res.data as Record<string, unknown>);
|
|
@@ -147,6 +147,43 @@ async function execSubgraph(sub: Subgraph, document: DocumentNode, variableValue
|
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Execute a federated subscription in-process. Subscriptions are not federated
|
|
152
|
+
* in v1 — a subscription's root field is owned by exactly one subgraph, so we
|
|
153
|
+
* route the whole operation to that subgraph and subscribe with its resolvers.
|
|
154
|
+
*/
|
|
155
|
+
export async function subscribeFederated(
|
|
156
|
+
supergraph: Supergraph,
|
|
157
|
+
document: DocumentNode,
|
|
158
|
+
variableValues: Record<string, any> | undefined,
|
|
159
|
+
contextValue: GraphQLContext,
|
|
160
|
+
): Promise<AsyncIterable<ExecutionResult> | ExecutionResult> {
|
|
161
|
+
const op = document.definitions.find((d) => d.kind === 'OperationDefinition') as
|
|
162
|
+
| { selectionSet: SelectionSetNode; operation: string }
|
|
163
|
+
| undefined;
|
|
164
|
+
if (!op) return { errors: [{ message: 'No operation in document' }] as unknown as NonNullable<ExecutionResult['errors']> };
|
|
165
|
+
if (op.operation !== 'subscription') return { errors: [{ message: 'subscribe() only supports subscription operations' }] as unknown as NonNullable<ExecutionResult['errors']> };
|
|
166
|
+
|
|
167
|
+
const field = op.selectionSet.selections.find((s) => s.kind === 'Field') as FieldNode | undefined;
|
|
168
|
+
if (!field) return { errors: [{ message: 'No subscription field found' }] as unknown as NonNullable<ExecutionResult['errors']> };
|
|
169
|
+
|
|
170
|
+
const owner = supergraph.ownership.get('Subscription')?.get(field.name.value);
|
|
171
|
+
if (!owner) return { errors: [{ message: `No subgraph owns Subscription.${field.name.value}` }] as unknown as NonNullable<ExecutionResult['errors']> };
|
|
172
|
+
|
|
173
|
+
const sub = supergraph.subgraphs.find((s) => s.name === owner);
|
|
174
|
+
if (!sub) return { errors: [{ message: `Subgraph "${owner}" not found` }] as unknown as NonNullable<ExecutionResult['errors']> };
|
|
175
|
+
|
|
176
|
+
return subscribe({
|
|
177
|
+
schema: sub.schema,
|
|
178
|
+
document,
|
|
179
|
+
rootValue: undefined,
|
|
180
|
+
contextValue,
|
|
181
|
+
variableValues,
|
|
182
|
+
subscribeFieldResolver: createSubscribeFieldResolver(sub.resolvers),
|
|
183
|
+
fieldResolver: createFieldResolver(sub.resolvers),
|
|
184
|
+
}) as Promise<AsyncIterable<ExecutionResult> | ExecutionResult>;
|
|
185
|
+
}
|
|
186
|
+
|
|
150
187
|
/** Unwrap a GraphQL type name to its base named type. */
|
|
151
188
|
function baseType(type: string): string {
|
|
152
189
|
return type.replace(/[\[\]!]/g, '');
|
|
@@ -207,9 +244,27 @@ function trimSelection(selections: readonly (FieldNode | InlineFragmentNode | Fr
|
|
|
207
244
|
return parts.join(' ');
|
|
208
245
|
}
|
|
209
246
|
|
|
247
|
+
/** Serialize a document's variable definitions back to SDL, e.g. `($id: ID!, $title: String!)`. */
|
|
248
|
+
function varDefsToSdl(defs: readonly { variable?: { name?: { value?: string } }; type?: import('graphql').TypeNode; defaultValue?: import('graphql').ConstValueNode }[] | undefined): string {
|
|
249
|
+
if (!defs || defs.length === 0) return '';
|
|
250
|
+
const parts = defs.map((d) => {
|
|
251
|
+
const name = d.variable?.name?.value;
|
|
252
|
+
if (!name || !d.type) return '';
|
|
253
|
+
return `$${name}: ${typeToSdl(d.type)}${d.defaultValue !== undefined ? ` = ${valueToSdl(d.defaultValue)}` : ''}`;
|
|
254
|
+
}).filter(Boolean);
|
|
255
|
+
return parts.length ? `(${parts.join(', ')})` : '';
|
|
256
|
+
}
|
|
257
|
+
function typeToSdl(t: import('graphql').TypeNode): string {
|
|
258
|
+
switch (t.kind) {
|
|
259
|
+
case 'NamedType': return t.name.value;
|
|
260
|
+
case 'ListType': return `[${typeToSdl(t.type)}]`;
|
|
261
|
+
case 'NonNullType': return `${typeToSdl(t.type)}!`;
|
|
262
|
+
default: return '';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
210
266
|
/** Serialize a field's arguments to SDL, e.g. `(id: "u1", limit: $limit)`. */
|
|
211
|
-
function argsToSdl(args: readonly import('graphql').ArgumentNode[] | undefined): string {
|
|
212
|
-
if (!args || args.length === 0) return '';
|
|
267
|
+
function argsToSdl(args: readonly import('graphql').ArgumentNode[] | undefined): string { if (!args || args.length === 0) return '';
|
|
213
268
|
return '(' + args.map((a) => `${a.name.value}: ${valueToSdl(a.value)}`).join(', ') + ')';
|
|
214
269
|
}
|
|
215
270
|
function valueToSdl(v: import('graphql').ValueNode): string {
|