x-openapi-flow 1.2.1 → 1.2.3
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/README.md
CHANGED
|
@@ -36,9 +36,15 @@ Use a GitHub PAT with `read:packages` (install) and `write:packages` (publish).
|
|
|
36
36
|
## Quick Start
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
x-openapi-flow
|
|
40
|
-
x-openapi-flow
|
|
41
|
-
|
|
39
|
+
npx x-openapi-flow init openapi.yaml
|
|
40
|
+
npx x-openapi-flow apply openapi.yaml
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Optional checks:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx x-openapi-flow validate openapi.yaml --profile strict
|
|
47
|
+
npx x-openapi-flow graph openapi.yaml
|
|
42
48
|
```
|
|
43
49
|
|
|
44
50
|
## CLI Commands
|
|
@@ -100,7 +106,7 @@ Field reference format:
|
|
|
100
106
|
### Swagger UI
|
|
101
107
|
|
|
102
108
|
- There is no Swagger UI-based automated test in this repo today (tests are CLI-only).
|
|
103
|
-
- For UI interpretation of `x-openapi-flow`, use `showExtensions: true` plus the
|
|
109
|
+
- For UI interpretation of `x-openapi-flow`, use `showExtensions: true` plus the plugin at `lib/swagger-ui/x-openapi-flow-plugin.js`.
|
|
104
110
|
- A ready HTML example is available at `examples/swagger-ui/index.html`.
|
|
105
111
|
- The plugin renders a global **Flow Overview** (Mermaid image) near the top of the docs, plus operation-level flow cards.
|
|
106
112
|
|
package/bin/x-openapi-flow.js
CHANGED
|
@@ -44,7 +44,7 @@ function printHelp() {
|
|
|
44
44
|
Usage:
|
|
45
45
|
x-openapi-flow validate <openapi-file> [--format pretty|json] [--profile core|relaxed|strict] [--strict-quality] [--config path]
|
|
46
46
|
x-openapi-flow init [openapi-file] [--flows path]
|
|
47
|
-
x-openapi-flow apply [openapi-file] [--flows path] [--out path]
|
|
47
|
+
x-openapi-flow apply [openapi-file] [--flows path] [--out path] [--in-place]
|
|
48
48
|
x-openapi-flow graph <openapi-file> [--format mermaid|json]
|
|
49
49
|
x-openapi-flow doctor [--config path]
|
|
50
50
|
x-openapi-flow --help
|
|
@@ -56,12 +56,19 @@ Examples:
|
|
|
56
56
|
x-openapi-flow init openapi.yaml --flows openapi-openapi-flow.yaml
|
|
57
57
|
x-openapi-flow init
|
|
58
58
|
x-openapi-flow apply openapi.yaml
|
|
59
|
+
x-openapi-flow apply openapi.yaml --in-place
|
|
59
60
|
x-openapi-flow apply openapi.yaml --out openapi.flow.yaml
|
|
60
61
|
x-openapi-flow graph examples/order-api.yaml
|
|
61
62
|
x-openapi-flow doctor
|
|
62
63
|
`);
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
function deriveFlowOutputPath(openApiFile) {
|
|
67
|
+
const parsed = path.parse(openApiFile);
|
|
68
|
+
const extension = parsed.ext || ".yaml";
|
|
69
|
+
return path.join(parsed.dir, `${parsed.name}.flow${extension}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
function getOptionValue(args, optionName) {
|
|
66
73
|
const index = args.indexOf(optionName);
|
|
67
74
|
if (index === -1) {
|
|
@@ -199,7 +206,7 @@ function parseInitArgs(args) {
|
|
|
199
206
|
}
|
|
200
207
|
|
|
201
208
|
function parseApplyArgs(args) {
|
|
202
|
-
const unknown = findUnknownOptions(args, ["--flows", "--out"], []);
|
|
209
|
+
const unknown = findUnknownOptions(args, ["--flows", "--out"], ["--in-place"]);
|
|
203
210
|
if (unknown) {
|
|
204
211
|
return { error: `Unknown option: ${unknown}` };
|
|
205
212
|
}
|
|
@@ -214,8 +221,13 @@ function parseApplyArgs(args) {
|
|
|
214
221
|
return { error: outOpt.error };
|
|
215
222
|
}
|
|
216
223
|
|
|
224
|
+
const inPlace = args.includes("--in-place");
|
|
225
|
+
if (inPlace && outOpt.found) {
|
|
226
|
+
return { error: "Options --in-place and --out cannot be used together." };
|
|
227
|
+
}
|
|
228
|
+
|
|
217
229
|
const positional = args.filter((token, index) => {
|
|
218
|
-
if (token === "--flows" || token === "--out") {
|
|
230
|
+
if (token === "--flows" || token === "--out" || token === "--in-place") {
|
|
219
231
|
return false;
|
|
220
232
|
}
|
|
221
233
|
if (index > 0 && (args[index - 1] === "--flows" || args[index - 1] === "--out")) {
|
|
@@ -232,6 +244,7 @@ function parseApplyArgs(args) {
|
|
|
232
244
|
openApiFile: positional[0] ? path.resolve(positional[0]) : undefined,
|
|
233
245
|
flowsPath: flowsOpt.found ? path.resolve(flowsOpt.value) : undefined,
|
|
234
246
|
outPath: outOpt.found ? path.resolve(outOpt.value) : undefined,
|
|
247
|
+
inPlace,
|
|
235
248
|
};
|
|
236
249
|
}
|
|
237
250
|
|
|
@@ -656,7 +669,9 @@ function runApply(parsed) {
|
|
|
656
669
|
}
|
|
657
670
|
|
|
658
671
|
const appliedCount = applyFlowsToOpenApi(api, flowsDoc);
|
|
659
|
-
const outputPath = parsed.
|
|
672
|
+
const outputPath = parsed.inPlace
|
|
673
|
+
? targetOpenApiFile
|
|
674
|
+
: (parsed.outPath || deriveFlowOutputPath(targetOpenApiFile));
|
|
660
675
|
saveOpenApi(outputPath, api);
|
|
661
676
|
|
|
662
677
|
console.log(`OpenAPI source: ${targetOpenApiFile}`);
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<script>
|
|
19
19
|
window.XOpenApiFlowGraphImageUrl = "https://raw.githubusercontent.com/tiago-marques/x-openapi-flow/main/docs/assets/graph-order-guided.svg";
|
|
20
20
|
</script>
|
|
21
|
-
<script src="
|
|
21
|
+
<script src="../../lib/swagger-ui/x-openapi-flow-plugin.js"></script>
|
|
22
22
|
<script>
|
|
23
23
|
window.ui = SwaggerUIBundle({
|
|
24
24
|
url: "../payment-api.yaml",
|
|
@@ -256,6 +256,10 @@ window.XOpenApiFlowPlugin = function () {
|
|
|
256
256
|
return result;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
function hasFlowData(spec) {
|
|
260
|
+
return extractFlowsFromSpec(spec).length > 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
259
263
|
function buildOverviewMermaid(flows) {
|
|
260
264
|
const lines = ['stateDiagram-v2'];
|
|
261
265
|
const states = new Set();
|
|
@@ -331,6 +335,8 @@ window.XOpenApiFlowPlugin = function () {
|
|
|
331
335
|
}
|
|
332
336
|
|
|
333
337
|
let overviewRenderedHash = null;
|
|
338
|
+
let overviewRenderInProgress = false;
|
|
339
|
+
let overviewPendingHash = null;
|
|
334
340
|
async function renderOverview() {
|
|
335
341
|
const spec = getSpecFromUi();
|
|
336
342
|
const flows = extractFlowsFromSpec(spec);
|
|
@@ -339,6 +345,7 @@ window.XOpenApiFlowPlugin = function () {
|
|
|
339
345
|
const mermaid = buildOverviewMermaid(flows);
|
|
340
346
|
const currentHash = `${flows.length}:${mermaid}`;
|
|
341
347
|
if (overviewRenderedHash === currentHash) return;
|
|
348
|
+
if (overviewRenderInProgress && overviewPendingHash === currentHash) return;
|
|
342
349
|
|
|
343
350
|
const infoContainer = document.querySelector('.swagger-ui .information-container');
|
|
344
351
|
if (!infoContainer) return;
|
|
@@ -352,6 +359,8 @@ window.XOpenApiFlowPlugin = function () {
|
|
|
352
359
|
}
|
|
353
360
|
|
|
354
361
|
holder.innerHTML = '<div class="xof-title">x-openapi-flow — Flow Overview</div><div class="xof-empty">Rendering Mermaid graph...</div>';
|
|
362
|
+
overviewRenderInProgress = true;
|
|
363
|
+
overviewPendingHash = currentHash;
|
|
355
364
|
|
|
356
365
|
try {
|
|
357
366
|
const mermaidLib = await ensureMermaid();
|
|
@@ -374,6 +383,8 @@ window.XOpenApiFlowPlugin = function () {
|
|
|
374
383
|
<div class="xof-empty">Could not render Mermaid image in this environment.</div>
|
|
375
384
|
<div class="xof-overview-code">${mermaid.replace(/</g, '<').replace(/>/g, '>')}</div>
|
|
376
385
|
`;
|
|
386
|
+
} finally {
|
|
387
|
+
overviewRenderInProgress = false;
|
|
377
388
|
}
|
|
378
389
|
|
|
379
390
|
overviewRenderedHash = currentHash;
|
|
@@ -410,18 +421,35 @@ window.XOpenApiFlowPlugin = function () {
|
|
|
410
421
|
}
|
|
411
422
|
|
|
412
423
|
function enhanceAll() {
|
|
424
|
+
const spec = getSpecFromUi();
|
|
425
|
+
if (!hasFlowData(spec)) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
413
429
|
injectStyles();
|
|
414
430
|
const opblocks = document.querySelectorAll('.opblock');
|
|
415
431
|
opblocks.forEach((opblock) => enhanceOperation(opblock));
|
|
416
|
-
renderOverview()
|
|
432
|
+
renderOverview().catch(() => {
|
|
433
|
+
// keep plugin resilient in environments where async rendering fails
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
let enhanceScheduled = false;
|
|
438
|
+
function scheduleEnhance() {
|
|
439
|
+
if (enhanceScheduled) return;
|
|
440
|
+
enhanceScheduled = true;
|
|
441
|
+
window.requestAnimationFrame(() => {
|
|
442
|
+
enhanceScheduled = false;
|
|
443
|
+
enhanceAll();
|
|
444
|
+
});
|
|
417
445
|
}
|
|
418
446
|
|
|
419
447
|
const observer = new MutationObserver(() => {
|
|
420
|
-
|
|
448
|
+
scheduleEnhance();
|
|
421
449
|
});
|
|
422
450
|
|
|
423
451
|
window.addEventListener('load', () => {
|
|
424
|
-
|
|
452
|
+
scheduleEnhance();
|
|
425
453
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
426
454
|
});
|
|
427
455
|
})();
|