x-openapi-flow 1.3.7 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,127 +1,656 @@
1
1
  (function () {
2
2
  "use strict";
3
3
 
4
- function createSectionTitle(text) {
5
- var title = document.createElement("h3");
6
- title.textContent = text;
7
- title.style.margin = "16px 0 8px";
8
- title.style.fontSize = "14px";
9
- title.style.fontWeight = "700";
10
- return title;
4
+ var VIEW_REFERENCE = "reference";
5
+ var VIEW_FLOW = "flow";
6
+ var STYLE_ID = "x-openapi-flow-redoc-style";
7
+ var jumpFeedbackTimeoutId = null;
8
+ var mermaidLoaderPromise = null;
9
+
10
+ function text(value) {
11
+ if (value === null || value === undefined || value === "") return "-";
12
+ if (Array.isArray(value)) return value.length ? value.join(", ") : "-";
13
+ return String(value);
11
14
  }
12
15
 
13
- function listItem(text) {
14
- var item = document.createElement("li");
15
- item.textContent = text;
16
- item.style.marginBottom = "6px";
17
- return item;
16
+ function escapeHtml(value) {
17
+ return text(value)
18
+ .replace(/&/g, "&")
19
+ .replace(/</g, "&lt;")
20
+ .replace(/>/g, "&gt;")
21
+ .replace(/\"/g, "&quot;")
22
+ .replace(/'/g, "&#39;");
18
23
  }
19
24
 
20
- function renderMermaidText(resource) {
21
- var lines = ["stateDiagram-v2", " direction LR"];
25
+ function slugify(value) {
26
+ return text(value)
27
+ .toLowerCase()
28
+ .replace(/[^a-z0-9]+/g, "-")
29
+ .replace(/^-+|-+$/g, "") || "item";
30
+ }
31
+
32
+ function normalizeText(value) {
33
+ return text(value)
34
+ .toLowerCase()
35
+ .replace(/\s+/g, " ")
36
+ .trim();
37
+ }
22
38
 
23
- var states = Array.from(new Set((resource.states || []).filter(Boolean))).sort();
24
- states.forEach(function (state) {
25
- lines.push(" state " + state);
39
+ function getPrerequisiteOperationIds(nextOperation) {
40
+ if (!nextOperation || typeof nextOperation !== "object") return [];
41
+ if (Array.isArray(nextOperation.prerequisites)) {
42
+ return nextOperation.prerequisites.filter(Boolean);
43
+ }
44
+ return [];
45
+ }
46
+
47
+ function flattenOperations(model) {
48
+ var operations = [];
49
+
50
+ ((model && model.resources) || []).forEach(function (resource) {
51
+ ((resource && resource.operations) || []).forEach(function (operation) {
52
+ if (!operation || !operation.hasFlow) return;
53
+ operations.push({
54
+ resource: resource,
55
+ operation: operation,
56
+ });
57
+ });
26
58
  });
27
59
 
28
- (resource.operations || []).forEach(function (operation) {
29
- (operation.nextOperations || []).forEach(function (next) {
30
- if (!operation.currentState || !next.targetState) return;
31
- var label = next.nextOperationId || operation.operationId;
32
- lines.push(" " + operation.currentState + " --> " + next.targetState + ": " + label);
60
+ return operations;
61
+ }
62
+
63
+ function findOperationInModel(model, operationId) {
64
+ var entries = flattenOperations(model);
65
+ for (var index = 0; index < entries.length; index += 1) {
66
+ if (entries[index].operation.operationId === operationId) {
67
+ return entries[index];
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+
73
+ function operationElementId(operationId) {
74
+ return "xofr-op-" + slugify(operationId);
75
+ }
76
+
77
+ function buildOverviewMermaid(model) {
78
+ var lines = ["stateDiagram-v2", " direction LR"];
79
+ var statesByName = new Map();
80
+ var seen = new Set();
81
+ var edgeLines = [];
82
+ var stateCounter = 0;
83
+
84
+ function getStateId(stateName) {
85
+ var normalized = text(stateName);
86
+ if (statesByName.has(normalized)) {
87
+ return statesByName.get(normalized);
88
+ }
89
+
90
+ stateCounter += 1;
91
+ var safeBase = normalized
92
+ .toLowerCase()
93
+ .replace(/[^a-z0-9]+/g, "_")
94
+ .replace(/^_+|_+$/g, "");
95
+ var candidate = safeBase ? "s_" + safeBase + "_" + stateCounter : "s_state_" + stateCounter;
96
+ statesByName.set(normalized, candidate);
97
+ return candidate;
98
+ }
99
+
100
+ function sanitizeLabel(label) {
101
+ return text(label)
102
+ .replace(/[|]/g, " / ")
103
+ .replace(/[\n\r]+/g, " ")
104
+ .replace(/\"/g, "'")
105
+ .trim();
106
+ }
107
+
108
+ flattenOperations(model).forEach(function (entry) {
109
+ var operation = entry.operation;
110
+ var current = text(operation.currentState);
111
+ if (!current || current === "-") return;
112
+
113
+ var fromId = getStateId(current);
114
+ (operation.nextOperations || []).forEach(function (nextOperation) {
115
+ var target = text(nextOperation.targetState);
116
+ if (!target || target === "-") return;
117
+
118
+ var toId = getStateId(target);
119
+ var labelParts = [];
120
+ if (nextOperation.nextOperationId) {
121
+ labelParts.push("next " + text(nextOperation.nextOperationId));
122
+ }
123
+ var prerequisiteOperationIds = getPrerequisiteOperationIds(nextOperation);
124
+ if (prerequisiteOperationIds.length) {
125
+ labelParts.push("requires " + prerequisiteOperationIds.join(","));
126
+ }
127
+
128
+ var label = sanitizeLabel(labelParts.join(" / "));
129
+ var key = fromId + "::" + toId + "::" + label;
130
+ if (seen.has(key)) return;
131
+ seen.add(key);
132
+ edgeLines.push(" " + fromId + " --> " + toId + (label ? ": " + label : ""));
33
133
  });
34
134
  });
35
135
 
136
+ statesByName.forEach(function (stateId, stateName) {
137
+ lines.push(" state \"" + stateName.replace(/\"/g, "'") + "\" as " + stateId);
138
+ });
139
+
140
+ lines.push.apply(lines, edgeLines);
36
141
  return lines.join("\n");
37
142
  }
38
143
 
144
+ function hasOverviewTransitions(model) {
145
+ return flattenOperations(model).some(function (entry) {
146
+ return Array.isArray(entry.operation.nextOperations) && entry.operation.nextOperations.length > 0;
147
+ });
148
+ }
149
+
150
+ function getMermaidFallbackMessage() {
151
+ return "Could not render Mermaid image. Check CDN/network access or load mermaid manually before ReDoc.";
152
+ }
153
+
154
+ function injectStyles() {
155
+ if (document.getElementById(STYLE_ID)) return;
156
+
157
+ var style = document.createElement("style");
158
+ style.id = STYLE_ID;
159
+ style.textContent = ""
160
+ + ".xofr-stack{display:flex;flex-direction:column;gap:18px;}"
161
+ + ".xofr-overview,.xofr-resource{border:1px solid #d1d5db;border-radius:20px;background:#fff;box-shadow:0 18px 40px rgba(15,23,42,.06);}"
162
+ + ".xofr-overview{padding:22px;}"
163
+ + ".xofr-resource{padding:20px;}"
164
+ + ".xofr-heading{margin:0 0 8px;font-size:30px;line-height:1.1;}"
165
+ + ".xofr-subtitle{margin:0 0 22px;color:#4b5563;font-size:15px;line-height:1.5;}"
166
+ + ".xofr-overview-head,.xofr-resource-head{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap;}"
167
+ + ".xofr-resource-title{margin:0;font-size:20px;line-height:1.2;}"
168
+ + ".xofr-resource-sub{margin:6px 0 0;color:#4b5563;font-size:13px;line-height:1.45;}"
169
+ + ".xofr-chips{display:flex;gap:8px;flex-wrap:wrap;}"
170
+ + ".xofr-chip{display:inline-flex;align-items:center;min-height:30px;padding:0 12px;border-radius:999px;background:#ecfeff;color:#155e75;font-size:12px;font-weight:700;}"
171
+ + ".xofr-overview-graph-wrap{display:flex;justify-content:center;align-items:flex-start;margin-top:14px;max-height:380px;overflow:auto;border:1px solid #cbd5e1;border-radius:16px;background:#fff;padding:12px;}"
172
+ + ".xofr-overview img{max-width:100%;height:auto;}"
173
+ + ".xofr-source-toggle{margin-top:12px;}"
174
+ + ".xofr-source-toggle summary{cursor:pointer;font-weight:700;color:#0f766e;}"
175
+ + ".xofr-code{margin-top:8px;padding:12px;border-radius:12px;background:#f8fafc;border:1px solid #e2e8f0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono',monospace;font-size:11px;line-height:1.45;white-space:pre-wrap;overflow:auto;}"
176
+ + ".xofr-empty{color:#6b7280;font-style:italic;}"
177
+ + ".xofr-resource-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px;margin-top:18px;}"
178
+ + ".xofr-operation-card{border:1px solid #dbe4ea;border-radius:18px;background:linear-gradient(180deg,#fff,#f8fafc);padding:16px;box-shadow:0 10px 24px rgba(15,23,42,.05);scroll-margin-top:96px;}"
179
+ + ".xofr-operation-top{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:12px;}"
180
+ + ".xofr-operation-title{margin:0;font-size:16px;line-height:1.25;}"
181
+ + ".xofr-operation-endpoint{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:6px 0 0;}"
182
+ + ".xofr-method{display:inline-flex;align-items:center;justify-content:center;min-width:56px;min-height:28px;padding:0 10px;border-radius:999px;background:#0f766e;color:#fff;font-size:12px;font-weight:700;text-transform:uppercase;}"
183
+ + ".xofr-path{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono',monospace;font-size:12px;color:#0f172a;word-break:break-word;}"
184
+ + ".xofr-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;}"
185
+ + ".xofr-button{border:1px solid #cbd5e1;background:#fff;color:#0f172a;border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;cursor:pointer;}"
186
+ + ".xofr-button:hover{border-color:#0f766e;color:#0f766e;}"
187
+ + ".xofr-button:focus-visible{outline:2px solid #0f766e;outline-offset:2px;}"
188
+ + ".xofr-meta{display:grid;grid-template-columns:130px 1fr;gap:6px 10px;font-size:12px;margin-top:12px;}"
189
+ + ".xofr-meta-label{color:#6b7280;}"
190
+ + ".xofr-section-title{margin:14px 0 8px;font-size:12px;font-weight:800;color:#0f172a;text-transform:uppercase;letter-spacing:.04em;}"
191
+ + ".xofr-list{margin:0;padding-left:18px;}"
192
+ + ".xofr-list li{margin:6px 0;line-height:1.5;}"
193
+ + ".xofr-inline-actions{display:inline-flex;gap:6px;flex-wrap:wrap;margin-left:8px;vertical-align:middle;}"
194
+ + ".xofr-inline-link{border:0;background:none;color:#0f766e;padding:0;font-size:11px;font-weight:700;text-decoration:underline;text-underline-offset:2px;cursor:pointer;}"
195
+ + ".xofr-inline-link:hover{opacity:.9;text-decoration-thickness:2px;}"
196
+ + ".xofr-mini-graph{padding:12px;border-radius:14px;background:#f8fafc;border:1px dashed #cbd5e1;}"
197
+ + ".xofr-edge{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono',monospace;font-size:12px;line-height:1.45;white-space:pre-wrap;}"
198
+ + "@keyframes xofr-target-pulse{0%{box-shadow:0 0 0 0 rgba(15,118,110,.35);}100%{box-shadow:0 0 0 12px rgba(15,118,110,0);}}"
199
+ + ".xofr-jump-target{animation:xofr-target-pulse .95s ease-out 1;border-color:#0f766e !important;}"
200
+ + ".xofr-jump-feedback{position:fixed;right:16px;bottom:16px;z-index:9999;max-width:380px;border:1px solid rgba(15,23,42,.22);border-radius:10px;background:rgba(15,23,42,.92);color:#fff;padding:9px 12px;font-size:12px;line-height:1.4;box-shadow:0 10px 24px rgba(0,0,0,.18);}"
201
+ + "@media (max-width: 720px){.xofr-overview,.xofr-resource{padding:16px;}.xofr-heading{font-size:26px;}.xofr-resource-grid{grid-template-columns:1fr;}.xofr-meta{grid-template-columns:1fr;}}";
202
+
203
+ document.head.appendChild(style);
204
+ }
205
+
206
+ function renderInlineOperationButtons(operationIds, attributeName, className, labelPrefix) {
207
+ if (!Array.isArray(operationIds) || operationIds.length === 0) return "";
208
+
209
+ return "<span class=\"xofr-inline-actions\">" + operationIds.map(function (operationId) {
210
+ return "<button class=\"" + className + "\" data-" + attributeName + "=\"" + escapeHtml(operationId) + "\" type=\"button\">" + escapeHtml(labelPrefix ? labelPrefix + operationId : operationId) + "</button>";
211
+ }).join("") + "</span>";
212
+ }
213
+
214
+ function renderTransitionList(operation) {
215
+ var transitions = Array.isArray(operation.nextOperations) ? operation.nextOperations : [];
216
+ if (!transitions.length) {
217
+ return "<div class=\"xofr-empty\">No transitions (terminal state)</div>";
218
+ }
219
+
220
+ return "<ul class=\"xofr-list\">" + transitions.map(function (nextOperation) {
221
+ var prerequisiteOperationIds = getPrerequisiteOperationIds(nextOperation);
222
+ var nextOperationId = nextOperation.nextOperationId ? [nextOperation.nextOperationId] : [];
223
+ return "<li><strong>" + escapeHtml(nextOperation.triggerType || "-") + "</strong> -> <strong>" + escapeHtml(nextOperation.targetState || "-") + "</strong>"
224
+ + (nextOperationId.length ? renderInlineOperationButtons(nextOperationId, "xofr-flow-jump", "xofr-inline-link", "next: ") : "")
225
+ + (nextOperationId.length ? renderInlineOperationButtons(nextOperationId, "xofr-reference", "xofr-inline-link", "ref: ") : "")
226
+ + (prerequisiteOperationIds.length ? renderInlineOperationButtons(prerequisiteOperationIds, "xofr-flow-jump", "xofr-inline-link", "requires: ") : "")
227
+ + "</li>";
228
+ }).join("") + "</ul>";
229
+ }
230
+
231
+ function renderMiniGraph(operation) {
232
+ var transitions = Array.isArray(operation.nextOperations) ? operation.nextOperations : [];
233
+ if (!transitions.length) {
234
+ return "<div class=\"xofr-edge\">" + escapeHtml(operation.currentState) + " [terminal]</div>";
235
+ }
236
+
237
+ return transitions.map(function (nextOperation) {
238
+ return "<div class=\"xofr-edge\">" + escapeHtml(operation.currentState)
239
+ + " --> " + escapeHtml(nextOperation.targetState)
240
+ + " [" + escapeHtml(nextOperation.triggerType || "-") + "]</div>";
241
+ }).join("");
242
+ }
243
+
244
+ function renderOperationCard(operation) {
245
+ var referenceLabel = text(operation.httpMethod).toUpperCase() + " " + text(operation.path);
246
+ var prerequisiteOperationIds = Array.isArray(operation.prerequisites) ? operation.prerequisites : [];
247
+ var nextOperationIds = (operation.nextOperations || []).map(function (nextOperation) {
248
+ return nextOperation.nextOperationId;
249
+ }).filter(Boolean);
250
+
251
+ return ""
252
+ + "<article class=\"xofr-operation-card\" id=\"" + operationElementId(operation.operationId) + "\" data-xofr-operation=\"" + escapeHtml(operation.operationId) + "\">"
253
+ + "<div class=\"xofr-operation-top\">"
254
+ + "<div>"
255
+ + "<h3 class=\"xofr-operation-title\">" + escapeHtml(operation.operationId) + "</h3>"
256
+ + "<div class=\"xofr-operation-endpoint\">"
257
+ + "<span class=\"xofr-method\">" + escapeHtml(operation.httpMethod || "-") + "</span>"
258
+ + "<span class=\"xofr-path\">" + escapeHtml(operation.path || "-") + "</span>"
259
+ + "</div>"
260
+ + "</div>"
261
+ + "</div>"
262
+ + "<div class=\"xofr-actions\">"
263
+ + "<button class=\"xofr-button\" type=\"button\" data-xofr-reference=\"" + escapeHtml(operation.operationId) + "\">View in API Reference</button>"
264
+ + (nextOperationIds.length ? "<button class=\"xofr-button\" type=\"button\" data-xofr-flow-jump=\"" + escapeHtml(nextOperationIds[0]) + "\">Jump to next operation</button>" : "")
265
+ + "</div>"
266
+ + "<div class=\"xofr-meta\">"
267
+ + "<div class=\"xofr-meta-label\">kind</div><div>" + escapeHtml(operation.kind || "-") + "</div>"
268
+ + "<div class=\"xofr-meta-label\">current_state</div><div>" + escapeHtml(operation.currentState || "-") + "</div>"
269
+ + "<div class=\"xofr-meta-label\">prerequisites</div><div>" + escapeHtml(prerequisiteOperationIds) + "</div>"
270
+ + "<div class=\"xofr-meta-label\">reference</div><div>" + escapeHtml(referenceLabel) + "</div>"
271
+ + "</div>"
272
+ + "<div class=\"xofr-section-title\">Transitions</div>"
273
+ + renderTransitionList(operation)
274
+ + "<div class=\"xofr-section-title\">Operation graph</div>"
275
+ + "<div class=\"xofr-mini-graph\">" + renderMiniGraph(operation) + "</div>"
276
+ + "</article>";
277
+ }
278
+
39
279
  function renderResourceBlock(resource) {
40
- var container = document.createElement("div");
41
- container.style.border = "1px solid #e5e7eb";
42
- container.style.borderRadius = "8px";
43
- container.style.padding = "12px";
44
- container.style.marginBottom = "14px";
45
-
46
- var title = document.createElement("h4");
47
- title.textContent = (resource.resourcePlural || resource.resource || "Resource") + " Lifecycle";
48
- title.style.margin = "0 0 8px";
49
- title.style.fontSize = "13px";
50
- container.appendChild(title);
51
-
52
- var mermaid = document.createElement("pre");
53
- mermaid.textContent = renderMermaidText(resource);
54
- mermaid.style.background = "#f8fafc";
55
- mermaid.style.border = "1px solid #e2e8f0";
56
- mermaid.style.padding = "8px";
57
- mermaid.style.borderRadius = "6px";
58
- mermaid.style.whiteSpace = "pre-wrap";
59
- mermaid.style.fontSize = "11px";
60
- container.appendChild(mermaid);
61
-
62
- var operationsTitle = createSectionTitle("Operations");
63
- operationsTitle.style.marginTop = "10px";
64
- operationsTitle.style.fontSize = "12px";
65
- container.appendChild(operationsTitle);
66
-
67
- var opList = document.createElement("ul");
68
- opList.style.paddingLeft = "16px";
69
-
70
- (resource.operations || []).forEach(function (operation) {
71
- if (!operation.hasFlow) return;
72
- var prerequisites = (operation.prerequisites || []).length > 0
73
- ? operation.prerequisites.join(", ")
74
- : "-";
75
- var nextOps = (operation.nextOperations || [])
76
- .map(function (next) { return next.nextOperationId; })
77
- .filter(Boolean);
78
- var nextText = nextOps.length > 0 ? nextOps.join(", ") : "-";
79
-
80
- opList.appendChild(
81
- listItem(
82
- operation.operationId
83
- + " | state=" + (operation.currentState || "-")
84
- + " | prerequisites=" + prerequisites
85
- + " | next=" + nextText
86
- )
87
- );
280
+ var operations = ((resource && resource.operations) || []).filter(function (operation) {
281
+ return operation && operation.hasFlow;
88
282
  });
89
283
 
90
- container.appendChild(opList);
91
- return container;
284
+ return ""
285
+ + "<section class=\"xofr-resource\">"
286
+ + "<div class=\"xofr-resource-head\">"
287
+ + "<div>"
288
+ + "<h2 class=\"xofr-resource-title\">" + escapeHtml(resource.resourcePlural || resource.resource || "Resource") + " Lifecycle</h2>"
289
+ + "<p class=\"xofr-resource-sub\">Detailed operation cards with transition helpers and API reference links.</p>"
290
+ + "</div>"
291
+ + "<div class=\"xofr-chips\">"
292
+ + "<span class=\"xofr-chip\">operations: " + escapeHtml(operations.length) + "</span>"
293
+ + "<span class=\"xofr-chip\">states: " + escapeHtml((resource.states || []).length) + "</span>"
294
+ + "</div>"
295
+ + "</div>"
296
+ + "<div class=\"xofr-resource-grid\">" + operations.map(renderOperationCard).join("") + "</div>"
297
+ + "</section>";
92
298
  }
93
299
 
94
- function mount(options) {
95
- var model = options && options.model;
96
- if (!model || !Array.isArray(model.resources) || model.resources.length === 0) {
97
- return;
300
+ function renderOverviewShell(model) {
301
+ var flowCount = model && model.flowCount ? model.flowCount : 0;
302
+ var resourceCount = Array.isArray(model && model.resources) ? model.resources.length : 0;
303
+ return ""
304
+ + "<section class=\"xofr-overview\">"
305
+ + "<div class=\"xofr-overview-head\">"
306
+ + "<div>"
307
+ + "<h2 class=\"xofr-resource-title\">Flow Overview</h2>"
308
+ + "<p class=\"xofr-resource-sub\">Mermaid overview plus endpoint-level lifecycle cards, aligned with the Swagger UI experience.</p>"
309
+ + "</div>"
310
+ + "<div class=\"xofr-chips\">"
311
+ + "<span class=\"xofr-chip\">resources: " + escapeHtml(resourceCount) + "</span>"
312
+ + "<span class=\"xofr-chip\">flow definitions: " + escapeHtml(flowCount) + "</span>"
313
+ + "</div>"
314
+ + "</div>"
315
+ + "<div id=\"xofr-overview-body\" class=\"xofr-overview-body\"></div>"
316
+ + "</section>";
317
+ }
318
+
319
+ function normalizeViewName(viewName) {
320
+ return viewName === VIEW_FLOW ? VIEW_FLOW : VIEW_REFERENCE;
321
+ }
322
+
323
+ function readHashView() {
324
+ if (!window.location || typeof window.location.hash !== "string") {
325
+ return null;
326
+ }
327
+
328
+ var hash = window.location.hash.replace(/^#/, "").trim();
329
+ if (!hash) {
330
+ return null;
331
+ }
332
+
333
+ return normalizeViewName(hash);
334
+ }
335
+
336
+ function updateNavItemState(navItem, isActive) {
337
+ if (navItem.classList && typeof navItem.classList.toggle === "function") {
338
+ navItem.classList.toggle("is-active", isActive);
339
+ } else if (typeof navItem.className === "string") {
340
+ var nextClassName = navItem.className.replace(/\bis-active\b/g, "").replace(/\s+/g, " ").trim();
341
+ navItem.className = isActive ? (nextClassName + " is-active").trim() : nextClassName;
342
+ }
343
+
344
+ navItem.setAttribute("aria-current", isActive ? "page" : "false");
345
+ }
346
+
347
+ function setActiveView(viewName, views, navItems) {
348
+ var activeViewName = normalizeViewName(viewName);
349
+
350
+ views.forEach(function (view) {
351
+ var isActive = view.getAttribute("data-x-openapi-flow-view") === activeViewName;
352
+ view.hidden = !isActive;
353
+ view.style.display = isActive ? "" : "none";
354
+ });
355
+
356
+ navItems.forEach(function (navItem) {
357
+ var isActive = navItem.getAttribute("data-x-openapi-flow-target") === activeViewName;
358
+ updateNavItemState(navItem, isActive);
359
+ });
360
+ }
361
+
362
+ function setView(viewName, context) {
363
+ var normalized = normalizeViewName(viewName);
364
+ if (window.location) {
365
+ window.location.hash = normalized;
366
+ }
367
+ setActiveView(normalized, context.views, context.navItems);
368
+ }
369
+
370
+ function highlightElement(element) {
371
+ if (!element || !element.classList) return;
372
+
373
+ element.classList.remove("xofr-jump-target");
374
+ window.requestAnimationFrame(function () {
375
+ element.classList.add("xofr-jump-target");
376
+ window.setTimeout(function () {
377
+ element.classList.remove("xofr-jump-target");
378
+ }, 980);
379
+ });
380
+ }
381
+
382
+ function scrollToElement(element) {
383
+ if (!element || typeof element.scrollIntoView !== "function") return;
384
+ element.scrollIntoView({ behavior: "smooth", block: "center" });
385
+ highlightElement(element);
386
+ }
387
+
388
+ function showJumpFeedback(message) {
389
+ injectStyles();
390
+
391
+ var feedback = document.getElementById("xofr-jump-feedback");
392
+ if (!feedback) {
393
+ feedback = document.createElement("div");
394
+ feedback.id = "xofr-jump-feedback";
395
+ feedback.className = "xofr-jump-feedback";
396
+ document.body.appendChild(feedback);
397
+ }
398
+
399
+ feedback.textContent = message;
400
+
401
+ if (jumpFeedbackTimeoutId) {
402
+ window.clearTimeout(jumpFeedbackTimeoutId);
403
+ }
404
+
405
+ jumpFeedbackTimeoutId = window.setTimeout(function () {
406
+ if (feedback && feedback.parentNode) {
407
+ feedback.parentNode.removeChild(feedback);
408
+ }
409
+ jumpFeedbackTimeoutId = null;
410
+ }, 2400);
411
+ }
412
+
413
+ function ensureMermaid() {
414
+ if (window.mermaid) {
415
+ return Promise.resolve(window.mermaid);
416
+ }
417
+
418
+ if (mermaidLoaderPromise) {
419
+ return mermaidLoaderPromise;
98
420
  }
99
421
 
100
- var target = document.querySelector(options.targetSelector || "#x-openapi-flow-panel");
101
- if (!target) {
422
+ mermaidLoaderPromise = new Promise(function (resolve, reject) {
423
+ var script = document.createElement("script");
424
+ script.src = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js";
425
+ script.async = true;
426
+ script.onload = function () {
427
+ if (window.mermaid) {
428
+ window.mermaid.initialize({
429
+ startOnLoad: false,
430
+ securityLevel: "loose",
431
+ theme: "neutral",
432
+ themeCSS: ""
433
+ + ".edgeLabel {"
434
+ + "background: rgba(255,255,255,0.96) !important;"
435
+ + "padding: 2px 6px !important;"
436
+ + "border-radius: 6px;"
437
+ + "font-size: 12px !important;"
438
+ + "line-height: 1.2;"
439
+ + "}"
440
+ + ".edgeLabel rect {"
441
+ + "fill: rgba(255,255,255,0.96) !important;"
442
+ + "rx: 6;"
443
+ + "ry: 6;"
444
+ + "}",
445
+ });
446
+ resolve(window.mermaid);
447
+ } else {
448
+ reject(new Error("Mermaid library not available after load"));
449
+ }
450
+ };
451
+ script.onerror = function () {
452
+ reject(new Error("Could not load Mermaid library"));
453
+ };
454
+ document.head.appendChild(script);
455
+ });
456
+
457
+ return mermaidLoaderPromise;
458
+ }
459
+
460
+ function svgToDataUri(svg) {
461
+ return "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(svg)));
462
+ }
463
+
464
+ function renderOverviewGraph(model) {
465
+ var holder = document.getElementById("xofr-overview-body");
466
+ if (!holder) return;
467
+
468
+ if (!hasOverviewTransitions(model)) {
469
+ holder.innerHTML = ""
470
+ + "<div class=\"xofr-empty\">No transitions found yet. Add transitions in the sidecar and regenerate to render the Mermaid overview.</div>"
471
+ + "<div class=\"xofr-code\">" + escapeHtml(buildOverviewMermaid(model)) + "</div>";
102
472
  return;
103
473
  }
104
474
 
105
- target.innerHTML = "";
475
+ var mermaid = buildOverviewMermaid(model);
476
+ holder.innerHTML = "<div class=\"xofr-empty\">Rendering Mermaid graph...</div>";
106
477
 
107
- var heading = document.createElement("h2");
108
- heading.textContent = "Flow / Lifecycle";
109
- heading.style.margin = "0 0 8px";
110
- heading.style.fontSize = "18px";
111
- target.appendChild(heading);
478
+ ensureMermaid().then(function (mermaidLib) {
479
+ return mermaidLib.render("xofr-overview-" + Date.now(), mermaid);
480
+ }).then(function (renderResult) {
481
+ var svg = renderResult && renderResult.svg ? renderResult.svg : renderResult;
482
+ holder.innerHTML = ""
483
+ + "<div class=\"xofr-overview-graph-wrap\"><img src=\"" + svgToDataUri(svg) + "\" alt=\"x-openapi-flow overview graph\" /></div>"
484
+ + "<details class=\"xofr-source-toggle\"><summary>Mermaid source</summary><div class=\"xofr-code\">" + escapeHtml(mermaid) + "</div></details>";
485
+ }).catch(function (error) {
486
+ var details = error && error.message ? error.message : "Unknown Mermaid error";
487
+ holder.innerHTML = ""
488
+ + "<div class=\"xofr-empty\">" + escapeHtml(getMermaidFallbackMessage()) + "</div>"
489
+ + "<div class=\"xofr-code\">Details: " + escapeHtml(details) + "\n\n" + escapeHtml(mermaid) + "</div>";
490
+ });
491
+ }
112
492
 
113
- var subtitle = document.createElement("p");
114
- subtitle.textContent = "Generated from x-openapi-flow metadata.";
115
- subtitle.style.margin = "0 0 12px";
116
- subtitle.style.color = "#4b5563";
117
- target.appendChild(subtitle);
493
+ function getReferenceRoot() {
494
+ return document.getElementById("x-openapi-flow-view-reference")
495
+ || document.querySelector("[data-x-openapi-flow-view=\"reference\"]")
496
+ || document.body;
497
+ }
498
+
499
+ function findReferenceElement(referenceEntry) {
500
+ if (!referenceEntry || !referenceEntry.operation) return null;
501
+
502
+ var operation = referenceEntry.operation;
503
+ var root = getReferenceRoot();
504
+ var operationId = normalizeText(operation.operationId);
505
+ var method = normalizeText(operation.httpMethod).toUpperCase();
506
+ var path = normalizeText(operation.path);
507
+ var selectors = ["[id]", "[data-section-id]", "section", "article", "div", "li", "h1", "h2", "h3", "h4", "h5", "button", "a", "span", "code"];
508
+ var candidates = root.querySelectorAll(selectors.join(","));
509
+ var bestElement = null;
510
+ var bestScore = 0;
511
+
512
+ Array.prototype.forEach.call(candidates, function (candidate) {
513
+ var haystack = normalizeText(candidate.textContent || candidate.innerText || "");
514
+ if (!haystack) return;
515
+
516
+ var score = 0;
517
+ if (operationId && haystack.indexOf(operationId) !== -1) score += 8;
518
+ if (path && haystack.indexOf(path) !== -1) score += 6;
519
+ if (method && haystack.indexOf(method.toLowerCase()) !== -1) score += 3;
520
+ if (score <= bestScore || score < 9) return;
118
521
 
119
- model.resources.forEach(function (resource) {
120
- target.appendChild(renderResourceBlock(resource));
522
+ bestScore = score;
523
+ bestElement = candidate.closest("section,article,li,div") || candidate;
121
524
  });
525
+
526
+ return bestElement;
527
+ }
528
+
529
+ function jumpToFlowOperation(operationId, context) {
530
+ if (!context || !context.target) return false;
531
+ setView(VIEW_FLOW, context);
532
+
533
+ var element = context.target.querySelector("[data-xofr-operation=\"" + operationId.replace(/\"/g, "&quot;") + "\"]");
534
+ if (!element) {
535
+ showJumpFeedback("Could not locate operation '" + operationId + "' in the Flow / Lifecycle view.");
536
+ return false;
537
+ }
538
+
539
+ scrollToElement(element);
540
+ return true;
541
+ }
542
+
543
+ function jumpToReferenceOperation(operationId, context) {
544
+ var referenceEntry = findOperationInModel(context.model, operationId);
545
+ if (!referenceEntry) {
546
+ showJumpFeedback("Could not resolve operation '" + operationId + "' from the lifecycle model.");
547
+ return false;
548
+ }
549
+
550
+ setView(VIEW_REFERENCE, context);
551
+
552
+ var attempts = 0;
553
+ function tryJump() {
554
+ attempts += 1;
555
+ var match = findReferenceElement(referenceEntry);
556
+ if (match) {
557
+ scrollToElement(match);
558
+ return;
559
+ }
560
+
561
+ if (attempts < 12) {
562
+ window.setTimeout(tryJump, 320);
563
+ return;
564
+ }
565
+
566
+ var operation = referenceEntry.operation;
567
+ showJumpFeedback(
568
+ "Could not locate operation '" + operationId + "' in the rendered ReDoc view. Reference: "
569
+ + text(operation.httpMethod).toUpperCase() + " " + text(operation.path)
570
+ );
571
+ }
572
+
573
+ window.setTimeout(tryJump, 180);
574
+ return true;
575
+ }
576
+
577
+ function bindFlowInteractions(target, context) {
578
+ target.addEventListener("click", function (event) {
579
+ var rawTarget = event.target;
580
+ if (!rawTarget || !rawTarget.closest) return;
581
+
582
+ var flowJump = rawTarget.closest("[data-xofr-flow-jump]");
583
+ if (flowJump) {
584
+ event.preventDefault();
585
+ jumpToFlowOperation(flowJump.getAttribute("data-xofr-flow-jump"), context);
586
+ return;
587
+ }
588
+
589
+ var referenceJump = rawTarget.closest("[data-xofr-reference]");
590
+ if (referenceJump) {
591
+ event.preventDefault();
592
+ jumpToReferenceOperation(referenceJump.getAttribute("data-xofr-reference"), context);
593
+ }
594
+ });
595
+ }
596
+
597
+ function mount(options) {
598
+ injectStyles();
599
+
600
+ var navItems = Array.prototype.slice.call(
601
+ document.querySelectorAll((options && options.navigationSelector) || "[data-x-openapi-flow-target]")
602
+ );
603
+ var views = Array.prototype.slice.call(
604
+ document.querySelectorAll((options && options.viewSelector) || "[data-x-openapi-flow-view]")
605
+ );
606
+ var defaultView = normalizeViewName(options && options.defaultView);
607
+ var target = document.querySelector((options && options.targetSelector) || "#x-openapi-flow-panel");
608
+ var model = options && options.model;
609
+ var context = {
610
+ model: model,
611
+ navItems: navItems,
612
+ views: views,
613
+ target: target,
614
+ };
615
+
616
+ function syncView() {
617
+ setActiveView(readHashView() || defaultView, views, navItems);
618
+ }
619
+
620
+ navItems.forEach(function (navItem) {
621
+ navItem.addEventListener("click", function () {
622
+ setView(navItem.getAttribute("data-x-openapi-flow-target"), context);
623
+ });
624
+ });
625
+
626
+ if (window.addEventListener) {
627
+ window.addEventListener("hashchange", syncView);
628
+ }
629
+
630
+ syncView();
631
+
632
+ if (!model || !Array.isArray(model.resources) || model.resources.length === 0 || !target) {
633
+ return;
634
+ }
635
+
636
+ target.innerHTML = ""
637
+ + "<div class=\"xofr-stack\">"
638
+ + renderOverviewShell(model)
639
+ + model.resources.map(renderResourceBlock).join("")
640
+ + "</div>";
641
+
642
+ bindFlowInteractions(target, context);
643
+ renderOverviewGraph(model);
122
644
  }
123
645
 
124
646
  window.XOpenApiFlowRedocPlugin = {
125
647
  mount: mount,
126
648
  };
649
+
650
+ window.XOpenApiFlowRedocInternals = {
651
+ buildOverviewMermaid: buildOverviewMermaid,
652
+ findOperationInModel: findOperationInModel,
653
+ hasOverviewTransitions: hasOverviewTransitions,
654
+ getMermaidFallbackMessage: getMermaidFallbackMessage,
655
+ };
127
656
  })();