shokupan 0.7.0 → 0.10.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.
- package/README.md +53 -0
- package/dist/analyzer-BqIe1p0R.js +35 -0
- package/dist/analyzer-BqIe1p0R.js.map +1 -0
- package/dist/analyzer-CKLGLFtx.cjs +35 -0
- package/dist/analyzer-CKLGLFtx.cjs.map +1 -0
- package/dist/{analyzer-Ce_7JxZh.js → analyzer.impl-CV6W1Eq7.js} +238 -21
- package/dist/analyzer.impl-CV6W1Eq7.js.map +1 -0
- package/dist/{analyzer-Bei1sVWp.cjs → analyzer.impl-D9Yi1Hax.cjs} +237 -20
- package/dist/analyzer.impl-D9Yi1Hax.cjs.map +1 -0
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/context.d.ts +69 -22
- package/dist/{http-server-DFhwlK8e.cjs → http-server-BEMPIs33.cjs} +4 -2
- package/dist/http-server-BEMPIs33.cjs.map +1 -0
- package/dist/{http-server-0xH174zz.js → http-server-CCeagTyU.js} +4 -2
- package/dist/http-server-CCeagTyU.js.map +1 -0
- package/dist/index.cjs +2411 -329
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2390 -308
- package/dist/index.js.map +1 -1
- package/dist/plugins/application/api-explorer/plugin.d.ts +9 -0
- package/dist/plugins/application/api-explorer/static/explorer-client.mjs +880 -0
- package/dist/plugins/application/api-explorer/static/style.css +767 -0
- package/dist/plugins/application/api-explorer/static/theme.css +128 -0
- package/dist/plugins/application/asyncapi/generator.d.ts +3 -0
- package/dist/plugins/application/asyncapi/plugin.d.ts +15 -0
- package/dist/plugins/application/asyncapi/static/asyncapi-client.mjs +748 -0
- package/dist/plugins/application/asyncapi/static/style.css +565 -0
- package/dist/plugins/application/asyncapi/static/theme.css +128 -0
- package/dist/plugins/application/auth.d.ts +3 -1
- package/dist/plugins/application/dashboard/metrics-collector.d.ts +14 -0
- package/dist/plugins/application/dashboard/plugin.d.ts +25 -9
- package/dist/plugins/application/dashboard/static/charts.js +328 -0
- package/dist/plugins/application/dashboard/static/failures.js +85 -0
- package/dist/plugins/application/dashboard/static/graph.mjs +523 -0
- package/dist/plugins/application/dashboard/static/poll.js +146 -0
- package/dist/plugins/application/dashboard/static/reactflow.css +18 -0
- package/dist/plugins/application/dashboard/static/registry.css +78 -0
- package/dist/plugins/application/dashboard/static/registry.js +269 -0
- package/dist/plugins/application/dashboard/static/requests.js +118 -0
- package/dist/plugins/application/dashboard/static/styles.css +184 -0
- package/dist/plugins/application/dashboard/static/tables.js +92 -0
- package/dist/plugins/application/dashboard/static/tabs.js +113 -0
- package/dist/plugins/application/dashboard/static/tabulator.css +118 -0
- package/dist/plugins/application/dashboard/static/theme.css +128 -0
- package/dist/plugins/application/graphql-apollo.d.ts +33 -0
- package/dist/plugins/application/graphql-yoga.d.ts +25 -0
- package/dist/plugins/application/openapi/analyzer.d.ts +12 -119
- package/dist/plugins/application/openapi/analyzer.impl.d.ts +167 -0
- package/dist/plugins/application/scalar.d.ts +9 -2
- package/dist/plugins/application/socket-io.d.ts +14 -0
- package/dist/router.d.ts +92 -51
- package/dist/shokupan.d.ts +34 -8
- package/dist/util/datastore.d.ts +71 -6
- package/dist/util/decorators.d.ts +7 -2
- package/dist/util/http-error.d.ts +38 -0
- package/dist/util/http-status.d.ts +30 -0
- package/dist/util/request.d.ts +1 -1
- package/dist/util/symbol.d.ts +19 -0
- package/dist/util/types.d.ts +126 -4
- package/package.json +38 -15
- package/dist/analyzer-Bei1sVWp.cjs.map +0 -1
- package/dist/analyzer-Ce_7JxZh.js.map +0 -1
- package/dist/http-server-0xH174zz.js.map +0 -1
- package/dist/http-server-DFhwlK8e.cjs.map +0 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
|
|
2
|
+
// --- Table Config Helper ---
|
|
3
|
+
function createTable(id, columns, placeholder = "No data available") {
|
|
4
|
+
return new Tabulator(id, {
|
|
5
|
+
layout: "fitColumns",
|
|
6
|
+
height: "300px",
|
|
7
|
+
placeholder: placeholder,
|
|
8
|
+
data: [],
|
|
9
|
+
columns: columns,
|
|
10
|
+
layoutColumnsOnNewData: true,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// --- Top Requests Table ---
|
|
15
|
+
const topRequestsTable = createTable("#top-requests-table", [
|
|
16
|
+
{ title: "Count", field: "count", align: "center", width: 100, sorter: "number" },
|
|
17
|
+
{ title: "Method", field: "method", width: 110 },
|
|
18
|
+
{ title: "URL", field: "url" },
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
// --- Top Errors Table ---
|
|
22
|
+
const topErrorsTable = createTable("#top-errors-table", [
|
|
23
|
+
{ title: "Count", field: "count", align: "center", width: 100, sorter: "number" },
|
|
24
|
+
{ title: "Error Message", field: "error" },
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
// --- Failing Requests Table ---
|
|
28
|
+
const failingRequestsTable = createTable("#failing-requests-table", [
|
|
29
|
+
{ title: "Failures", field: "count", align: "center", width: 80, sorter: "number" },
|
|
30
|
+
{ title: "Method", field: "method", width: 110 },
|
|
31
|
+
{ title: "URL", field: "url" },
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
// --- Slowest Requests Table ---
|
|
35
|
+
const slowestRequestsTable = createTable("#slowest-requests-table", [
|
|
36
|
+
{ title: "Duration (ms)", field: "duration", width: 130, sorter: "number", formatter: (cell) => printDuration(cell.getValue()) },
|
|
37
|
+
{
|
|
38
|
+
title: "URL", formatter: (cell) => {
|
|
39
|
+
const data = cell.getData() ?? {};
|
|
40
|
+
return data.method?.toUpperCase() + ": " + data.url;
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
title: "Status", field: "status", width: 100, align: "center", formatter: function (cell) {
|
|
45
|
+
const val = cell.getValue();
|
|
46
|
+
return `<span style="color: ${val >= 400 ? 'red' : 'green'}">${val}</span>`;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{ title: "Time", field: "timestamp", width: 150, formatter: (cell) => new Date(cell.getValue()).toLocaleTimeString() }
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async function fetchTopStats() {
|
|
54
|
+
// Get request headers from global function if available
|
|
55
|
+
const headers = typeof getRequestHeaders !== 'undefined' ? getRequestHeaders() : {};
|
|
56
|
+
|
|
57
|
+
// Determine base path for API requests
|
|
58
|
+
const basePath = window.location.pathname.endsWith('/') ? window.location.pathname.slice(0, -1) : window.location.pathname;
|
|
59
|
+
const url = basePath + '/';
|
|
60
|
+
const interval = document.getElementById('time-range-selector')?.value || '1m';
|
|
61
|
+
try {
|
|
62
|
+
// Top Requests
|
|
63
|
+
fetch(url + 'requests/top?interval=' + interval, { headers }).then(r => r.json()).then(d => {
|
|
64
|
+
if (d.top) topRequestsTable.setData(d.top);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Top Errors
|
|
68
|
+
fetch(url + 'errors/top?interval=' + interval, { headers }).then(r => r.json()).then(d => {
|
|
69
|
+
if (d.top) topErrorsTable.setData(d.top);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Failing Requests
|
|
73
|
+
fetch(url + 'requests/failing?interval=' + interval, { headers }).then(r => r.json()).then(d => {
|
|
74
|
+
if (d.top) failingRequestsTable.setData(d.top);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Slowest Requests
|
|
78
|
+
fetch(url + 'requests/slowest?interval=' + interval, { headers }).then(r => r.json()).then(d => {
|
|
79
|
+
if (d.slowest) slowestRequestsTable.setData(d.slowest);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.error("Failed to fetch top stats", e);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
88
|
+
fetchTopStats();
|
|
89
|
+
// Refresh periodically
|
|
90
|
+
setInterval(fetchTopStats, 10000);
|
|
91
|
+
});
|
|
92
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// --- Tabs Logic ---
|
|
2
|
+
function switchTab(tabId) {
|
|
3
|
+
console.log('Switching to tab:', tabId);
|
|
4
|
+
// Update buttons
|
|
5
|
+
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
|
6
|
+
// Find the button that was clicked
|
|
7
|
+
const btn = Array.from(document.querySelectorAll('.tab-btn')).find(b => b.getAttribute('onclick') === `switchTab('${tabId}')`);
|
|
8
|
+
if (btn) btn.classList.add('active');
|
|
9
|
+
|
|
10
|
+
// Update content
|
|
11
|
+
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
|
12
|
+
document.getElementById('tab-' + tabId).classList.add('active');
|
|
13
|
+
|
|
14
|
+
if (tabId === 'overview') {
|
|
15
|
+
if (typeof fetchTopStats === 'function') fetchTopStats();
|
|
16
|
+
}
|
|
17
|
+
else if (tabId === 'graph') {
|
|
18
|
+
initGraph();
|
|
19
|
+
}
|
|
20
|
+
else if (tabId === 'requests') {
|
|
21
|
+
if (typeof fetchRequests === 'function') fetchRequests();
|
|
22
|
+
}
|
|
23
|
+
else if (tabId === 'failures') {
|
|
24
|
+
fetchFailures();
|
|
25
|
+
}
|
|
26
|
+
else if (tabId === 'middleware') {
|
|
27
|
+
fetchMiddleware();
|
|
28
|
+
}
|
|
29
|
+
else if (tabId === 'registry') {
|
|
30
|
+
if (typeof fetchRegistry === 'function') fetchRegistry();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Middleware fetch function
|
|
35
|
+
async function fetchMiddleware() {
|
|
36
|
+
try {
|
|
37
|
+
const headers = getRequestHeaders ? getRequestHeaders() : {};
|
|
38
|
+
const basePath = window.location.pathname.endsWith('/') ? '' : window.location.pathname;
|
|
39
|
+
const url = basePath + (basePath.endsWith('/') ? 'middleware' : '/middleware');
|
|
40
|
+
|
|
41
|
+
const res = await fetch(url, { headers });
|
|
42
|
+
if (!res.ok) return;
|
|
43
|
+
const data = await res.json();
|
|
44
|
+
|
|
45
|
+
// Initialize or update table
|
|
46
|
+
if (!window.middlewareTable) {
|
|
47
|
+
window.middlewareTable = new Tabulator("#middleware-table-container", {
|
|
48
|
+
layout: "fitColumns",
|
|
49
|
+
height: "500px",
|
|
50
|
+
placeholder: "No middleware executions tracked",
|
|
51
|
+
data: data.middleware || [],
|
|
52
|
+
columns: [
|
|
53
|
+
{
|
|
54
|
+
title: "Timestamp", field: "timestamp", width: 180, formatter: function (cell) {
|
|
55
|
+
return new Date(cell.getValue()).toLocaleString();
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
title: "Name", field: "name", width: 200,
|
|
60
|
+
formatter: function (cell) {
|
|
61
|
+
const row = cell.getRow().getData();
|
|
62
|
+
const isBuiltin = row.metadata?.isBuiltin;
|
|
63
|
+
const pluginName = row.metadata?.pluginName;
|
|
64
|
+
let badge = '';
|
|
65
|
+
if (isBuiltin) {
|
|
66
|
+
badge = ' <span style="background: #059669; color: white; padding: 2px 6px; border-radius: 4px; font-size: 0.7em;">BUILTIN</span>';
|
|
67
|
+
}
|
|
68
|
+
const plugin = pluginName ? ` <span style="color: #6ee7b7;">[${pluginName}]</span>` : '';
|
|
69
|
+
return cell.getValue() + badge + plugin;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{ title: "Path", field: "path", headerFilter: "input" },
|
|
73
|
+
{
|
|
74
|
+
title: "Duration (ms)", field: "duration", width: 120,
|
|
75
|
+
formatter: (cell) => cell.getValue() ? cell.getValue().toFixed(2) : 'N/A'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
title: "Status", field: "error", width: 100, formatter: function (cell) {
|
|
79
|
+
const error = cell.getValue();
|
|
80
|
+
if (error) {
|
|
81
|
+
return '<span style="color: #ef4444; font-weight: bold;">ERROR</span>';
|
|
82
|
+
}
|
|
83
|
+
return '<span style="color: #22c55e; font-weight: bold;">OK</span>';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
initialSort: [
|
|
88
|
+
{ column: "timestamp", dir: "desc" }
|
|
89
|
+
]
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
window.middlewareTable.replaceData(data.middleware || []);
|
|
93
|
+
}
|
|
94
|
+
} catch (e) {
|
|
95
|
+
console.error("Failed to fetch middleware", e);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function fetchFailures() {
|
|
100
|
+
try {
|
|
101
|
+
const headers = getRequestHeaders ? getRequestHeaders() : {};
|
|
102
|
+
// Handle relative path issue
|
|
103
|
+
const basePath = window.location.pathname.endsWith('/') ? '' : window.location.pathname;
|
|
104
|
+
const url = basePath + (basePath.endsWith('/') ? 'failures' : '/failures');
|
|
105
|
+
|
|
106
|
+
const res = await fetch(url, { headers });
|
|
107
|
+
if (!res.ok) return;
|
|
108
|
+
const data = await res.json();
|
|
109
|
+
failuresTable.replaceData(data.failures);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
console.error("Failed to fetch failures", e);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* Tabulator Theme Overrides - Merged with Layout */
|
|
2
|
+
.tabulator {
|
|
3
|
+
height: 100%;
|
|
4
|
+
width: 100%;
|
|
5
|
+
background-color: var(--bg-card);
|
|
6
|
+
border: 1px solid var(--card-border);
|
|
7
|
+
color: var(--text-primary);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Header */
|
|
11
|
+
.tabulator .tabulator-header {
|
|
12
|
+
background-color: var(--bg-card);
|
|
13
|
+
border-top: 0;
|
|
14
|
+
border-bottom: 2px solid var(--table-header-border-color);
|
|
15
|
+
color: var(--primary);
|
|
16
|
+
font-weight: bold;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.tabulator .tabulator-header .tabulator-col {
|
|
20
|
+
background-color: var(--bg-card);
|
|
21
|
+
border-right: 1px solid var(--card-border);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.tabulator .tabulator-header .tabulator-col:hover {
|
|
25
|
+
background-color: var(--bg-primary);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.tabulator .tabulator-header .tabulator-col-content {
|
|
29
|
+
padding: 12px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Sortable Columns */
|
|
33
|
+
.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter {
|
|
34
|
+
color: var(--text-secondary);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.tabulator .tabulator-header .tabulator-col.tabulator-sortable.tabulator-col-sorter-element:hover {
|
|
38
|
+
background-color: var(--bg-primary);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Filter Inputs */
|
|
42
|
+
.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input {
|
|
43
|
+
background-color: var(--bg-primary);
|
|
44
|
+
color: var(--text-primary);
|
|
45
|
+
border: 1px solid var(--card-border);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input:focus {
|
|
49
|
+
background-color: var(--bg-secondary);
|
|
50
|
+
color: var(--text-primary);
|
|
51
|
+
border-color: var(--primary);
|
|
52
|
+
outline: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Rows */
|
|
56
|
+
.tabulator .tabulator-row {
|
|
57
|
+
background-color: var(--bg-card);
|
|
58
|
+
border-bottom: 1px solid var(--card-border);
|
|
59
|
+
color: var(--text-primary);
|
|
60
|
+
transition: background-color 100ms ease;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Higher specificity to override Tabulator default themes */
|
|
64
|
+
.tabulator .tabulator-row.tabulator-row-even,
|
|
65
|
+
.tabulator-row.tabulator-row-even {
|
|
66
|
+
background-color: var(--bg-primary) !important;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.tabulator .tabulator-row.tabulator-row-odd,
|
|
70
|
+
.tabulator-row.tabulator-row-odd {
|
|
71
|
+
background-color: var(--bg-card) !important;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.tabulator .tabulator-row.tabulator-selected {
|
|
75
|
+
background-color: var(--bg-primary) !important;
|
|
76
|
+
color: var(--text-primary);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.tabulator .tabulator-row:hover {
|
|
80
|
+
background-color: var(--bg-primary) !important;
|
|
81
|
+
color: var(--text-primary);
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Cells */
|
|
86
|
+
.tabulator .tabulator-row .tabulator-cell {
|
|
87
|
+
border-right: 1px solid var(--card-border);
|
|
88
|
+
padding: 10px;
|
|
89
|
+
align-content: center;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Footer / Pagination */
|
|
93
|
+
.tabulator .tabulator-footer {
|
|
94
|
+
background-color: var(--bg-card);
|
|
95
|
+
border-top: 2px solid var(--table-header-border-color);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.tabulator .tabulator-footer .tabulator-page {
|
|
99
|
+
background-color: var(--bg-primary);
|
|
100
|
+
color: var(--text-primary);
|
|
101
|
+
border: 1px solid var(--border-color);
|
|
102
|
+
border-radius: 4px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.tabulator .tabulator-footer .tabulator-page:hover {
|
|
106
|
+
background-color: var(--table-header-border-color);
|
|
107
|
+
color: var(--text-primary);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.tabulator .tabulator-footer .tabulator-page.active {
|
|
111
|
+
background-color: var(--primary);
|
|
112
|
+
color: var(--bg-card);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Sorting Icons */
|
|
116
|
+
.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-arrow {
|
|
117
|
+
border-bottom-color: var(--text-secondary);
|
|
118
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
/* Raw Palette - Dark (Now Default) */
|
|
3
|
+
--palette-dark-primary: #FFB380;
|
|
4
|
+
/* Warm Filament */
|
|
5
|
+
--palette-dark-secondary: #7B5E7B;
|
|
6
|
+
/* Yeast Purple */
|
|
7
|
+
--palette-dark-accent: #D6D3D1;
|
|
8
|
+
/* Flour Dust */
|
|
9
|
+
--palette-dark-bg: #1A1614;
|
|
10
|
+
/* Oven Depth */
|
|
11
|
+
--palette-dark-card: #2A2421;
|
|
12
|
+
|
|
13
|
+
--palette-dark-header: rgb(39, 27, 19);
|
|
14
|
+
/* Proofing Drawer */
|
|
15
|
+
|
|
16
|
+
/* Universal defaults (tokens) */
|
|
17
|
+
--shokupan-font: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
18
|
+
|
|
19
|
+
/* Theme Variables - Enforced Dark Mode */
|
|
20
|
+
--bg-primary: var(--palette-dark-bg);
|
|
21
|
+
--bg-secondary: var(--palette-dark-card);
|
|
22
|
+
--bg-card: var(--palette-dark-card);
|
|
23
|
+
--bg-header: var(--palette-dark-header);
|
|
24
|
+
|
|
25
|
+
--text-primary: #F0F0F0;
|
|
26
|
+
--text-secondary: var(--palette-dark-accent);
|
|
27
|
+
--text-muted: rgba(214, 211, 209, 0.5);
|
|
28
|
+
|
|
29
|
+
--primary: var(--palette-dark-primary);
|
|
30
|
+
--secondary: var(--palette-dark-secondary);
|
|
31
|
+
--accent: var(--palette-dark-accent);
|
|
32
|
+
|
|
33
|
+
--border-color: rgba(214, 211, 209, 0.2);
|
|
34
|
+
--card-border: rgba(214, 211, 209, 0.2);
|
|
35
|
+
|
|
36
|
+
--table-header-border-color: rgba(214, 211, 209, 0.2);
|
|
37
|
+
|
|
38
|
+
--link-color: var(--palette-dark-primary);
|
|
39
|
+
--link-hover: #FFCBA0;
|
|
40
|
+
|
|
41
|
+
--button-bg: var(--palette-dark-primary);
|
|
42
|
+
--button-text: #1A1614;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
::-webkit-scrollbar {
|
|
46
|
+
width: 8px;
|
|
47
|
+
height: 8px;
|
|
48
|
+
background-color: var(--bg-primary);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
::-webkit-scrollbar-thumb {
|
|
52
|
+
border-radius: 10px;
|
|
53
|
+
background-color: var(--primary);
|
|
54
|
+
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
::-webkit-scrollbar-track {
|
|
58
|
+
border-radius: 10px;
|
|
59
|
+
background-color: var(--bg-secondary);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
::-webkit-scrollbar-corner {
|
|
63
|
+
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.badge-SEND {
|
|
67
|
+
background: rgba(59, 130, 246, 0.15);
|
|
68
|
+
color: #60a5fa;
|
|
69
|
+
border: 1px solid rgba(59, 130, 246, 0.3);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.badge-RECV {
|
|
73
|
+
background: rgba(16, 185, 129, 0.15);
|
|
74
|
+
color: #34d399;
|
|
75
|
+
border: 1px solid rgba(16, 185, 129, 0.3);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.badge-GET {
|
|
79
|
+
background: #0f172a;
|
|
80
|
+
color: #3b82f6;
|
|
81
|
+
border: 1px solid #3b82f6;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.badge-POST {
|
|
85
|
+
background: #0f172a;
|
|
86
|
+
color: #22c55e;
|
|
87
|
+
border: 1px solid #22c55e;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.badge-PUT {
|
|
91
|
+
background: #0f172a;
|
|
92
|
+
color: #eab308;
|
|
93
|
+
border: 1px solid #eab308;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.badge-PATCH {
|
|
97
|
+
background: #0f172a;
|
|
98
|
+
color: #08d3ea;
|
|
99
|
+
border: 1px solid #08eae6;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.badge-HEAD {
|
|
103
|
+
background: #0f172a;
|
|
104
|
+
color: #e3ea08;
|
|
105
|
+
border: 1px solid #e3ea08;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.badge-OPTIONS {
|
|
109
|
+
background: #0f172a;
|
|
110
|
+
color: #838383;
|
|
111
|
+
border: 1px solid #838383;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.badge-DELETE {
|
|
115
|
+
background: #0f172a;
|
|
116
|
+
color: #ef4444;
|
|
117
|
+
border: 1px solid #ef4444;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.badge-ROUTER {
|
|
121
|
+
background: #334155;
|
|
122
|
+
color: #f8fafc;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.badge-CONTROLLER {
|
|
126
|
+
background: #475569;
|
|
127
|
+
color: #f8fafc;
|
|
128
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ApolloServer } from '@apollo/server';
|
|
2
|
+
import { ShokupanRouter } from '../../router';
|
|
3
|
+
import { Shokupan } from '../../shokupan';
|
|
4
|
+
import { ShokupanPlugin, ShokupanPluginOptions } from '../../util/types';
|
|
5
|
+
export interface GraphQLPluginOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Path to mount the GraphQL endpoint to.
|
|
8
|
+
* @default '/graphql'
|
|
9
|
+
*/
|
|
10
|
+
path?: string;
|
|
11
|
+
/**
|
|
12
|
+
* GraphQL Type Definitions
|
|
13
|
+
*/
|
|
14
|
+
typeDefs: any;
|
|
15
|
+
/**
|
|
16
|
+
* GraphQL Resolvers
|
|
17
|
+
*/
|
|
18
|
+
resolvers: any;
|
|
19
|
+
/**
|
|
20
|
+
* Optional Apollo Server configuration
|
|
21
|
+
*/
|
|
22
|
+
apolloConfig?: Omit<ConstructorParameters<typeof ApolloServer>[0], 'typeDefs' | 'resolvers'>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* GraphQL Apollo Server Plugin for Shokupan.
|
|
26
|
+
* Enables serving GraphQL APIs using Apollo Server 4.
|
|
27
|
+
*/
|
|
28
|
+
export declare class GraphQLApolloPlugin extends ShokupanRouter<any> implements ShokupanPlugin {
|
|
29
|
+
private pluginOptions;
|
|
30
|
+
private apolloServer;
|
|
31
|
+
constructor(pluginOptions: GraphQLPluginOptions);
|
|
32
|
+
onInit(app: Shokupan, options?: ShokupanPluginOptions): Promise<void>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { YogaServerOptions } from 'graphql-yoga';
|
|
2
|
+
import { ShokupanRouter } from '../../router';
|
|
3
|
+
import { Shokupan } from '../../shokupan';
|
|
4
|
+
import { ShokupanPlugin, ShokupanPluginOptions } from '../../util/types';
|
|
5
|
+
export interface GraphQLYogaPluginOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Path to mount the GraphQL endpoint to.
|
|
8
|
+
* @default '/graphql'
|
|
9
|
+
*/
|
|
10
|
+
path?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Yoga Server configuration
|
|
13
|
+
*/
|
|
14
|
+
yogaConfig: YogaServerOptions<any, any>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* GraphQL Yoga Plugin for Shokupan.
|
|
18
|
+
* Enables serving GraphQL APIs using GraphQL Yoga.
|
|
19
|
+
*/
|
|
20
|
+
export declare class GraphQLYogaPlugin extends ShokupanRouter<any> implements ShokupanPlugin {
|
|
21
|
+
private pluginOptions;
|
|
22
|
+
private yoga;
|
|
23
|
+
constructor(pluginOptions: GraphQLYogaPluginOptions);
|
|
24
|
+
onInit(app: Shokupan, options?: ShokupanPluginOptions): Promise<void>;
|
|
25
|
+
}
|
|
@@ -1,133 +1,27 @@
|
|
|
1
|
+
import { ApplicationInstance } from './analyzer.impl';
|
|
2
|
+
export type { ApplicationInstance, RouteInfo } from './analyzer.impl';
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
handlerName?: string;
|
|
8
|
-
handlerSource?: string;
|
|
9
|
-
requestTypes?: {
|
|
10
|
-
body?: any;
|
|
11
|
-
query?: Record<string, string>;
|
|
12
|
-
params?: Record<string, string>;
|
|
13
|
-
headers?: Record<string, string>;
|
|
14
|
-
};
|
|
15
|
-
responseType?: string;
|
|
16
|
-
responseSchema?: any;
|
|
17
|
-
summary?: string;
|
|
18
|
-
description?: string;
|
|
19
|
-
tags?: string[];
|
|
20
|
-
operationId?: string;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Dependency information
|
|
24
|
-
*/
|
|
25
|
-
interface DependencyInfo {
|
|
26
|
-
packageName: string;
|
|
27
|
-
version?: string;
|
|
28
|
-
importPath: string;
|
|
29
|
-
isExternal: boolean;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Application/Router instance found in code
|
|
33
|
-
*/
|
|
34
|
-
export interface ApplicationInstance {
|
|
35
|
-
name: string;
|
|
36
|
-
filePath: string;
|
|
37
|
-
className: 'Shokupan' | 'ShokupanRouter' | 'Controller';
|
|
38
|
-
routes: RouteInfo[];
|
|
39
|
-
mounted: MountInfo[];
|
|
40
|
-
}
|
|
41
|
-
interface MountInfo {
|
|
42
|
-
prefix: string;
|
|
43
|
-
target: string;
|
|
44
|
-
dependency?: DependencyInfo;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Main analyzer class
|
|
4
|
+
* OpenAPI Analyzer Wrapper.
|
|
5
|
+
*
|
|
6
|
+
* This class wraps the actual OpenAPIAnalyzer implementation to facilitate
|
|
7
|
+
* lazy loading of the 'typescript' peer dependency. The actual implementation
|
|
8
|
+
* and the 'typescript' module are only loaded when `analyze()` is called.
|
|
48
9
|
*/
|
|
49
10
|
export declare class OpenAPIAnalyzer {
|
|
50
11
|
private rootDir;
|
|
51
|
-
private files;
|
|
52
|
-
private applications;
|
|
53
|
-
private program?;
|
|
54
12
|
private entrypoint?;
|
|
13
|
+
private analyzerImpl;
|
|
55
14
|
constructor(rootDir: string, entrypoint?: string);
|
|
56
15
|
/**
|
|
57
|
-
* Main analysis entry point
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Main analysis entry point
|
|
16
|
+
* Main analysis entry point.
|
|
17
|
+
* Dynamically imports the implementation and runs the analysis.
|
|
61
18
|
*/
|
|
62
19
|
analyze(): Promise<{
|
|
63
20
|
applications: ApplicationInstance[];
|
|
64
21
|
}>;
|
|
65
22
|
/**
|
|
66
|
-
*
|
|
67
|
-
|
|
68
|
-
private scanDirectory;
|
|
69
|
-
/**
|
|
70
|
-
* Process source maps to reconstruct TypeScript
|
|
71
|
-
*/
|
|
72
|
-
private processSourceMaps;
|
|
73
|
-
/**
|
|
74
|
-
* Parse TypeScript files and create AST
|
|
75
|
-
*/
|
|
76
|
-
private parseTypeScriptFiles;
|
|
77
|
-
/**
|
|
78
|
-
* Find all Shokupan/ShokupanRouter instances
|
|
79
|
-
*/
|
|
80
|
-
private findApplications;
|
|
81
|
-
/**
|
|
82
|
-
* Visit AST node to find application instances
|
|
83
|
-
*/
|
|
84
|
-
private visitNode;
|
|
85
|
-
/**
|
|
86
|
-
* Extract route information from applications
|
|
87
|
-
*/
|
|
88
|
-
private extractRoutes;
|
|
89
|
-
/**
|
|
90
|
-
* Extract routes from a Controller class
|
|
91
|
-
*/
|
|
92
|
-
private extractRoutesFromController;
|
|
93
|
-
/**
|
|
94
|
-
* Extract routes from a specific file
|
|
95
|
-
*/
|
|
96
|
-
private extractRoutesFromFile;
|
|
97
|
-
/**
|
|
98
|
-
* Extract route information from a route call (e.g., app.get('/path', handler))
|
|
99
|
-
*/
|
|
100
|
-
private extractRouteFromCall;
|
|
101
|
-
/**
|
|
102
|
-
* Analyze a route handler to extract type information
|
|
103
|
-
*/
|
|
104
|
-
private analyzeHandler;
|
|
105
|
-
/**
|
|
106
|
-
* Convert an Expression node to an OpenAPI schema (best effort)
|
|
107
|
-
*/
|
|
108
|
-
private convertExpressionToSchema;
|
|
109
|
-
/**
|
|
110
|
-
* Check if an expression is a call to ctx.body()
|
|
111
|
-
*/
|
|
112
|
-
private isCtxBodyCall;
|
|
113
|
-
/**
|
|
114
|
-
* Convert a TypeScript TypeNode to an OpenAPI schema
|
|
115
|
-
*/
|
|
116
|
-
private convertTypeNodeToSchema;
|
|
117
|
-
/**
|
|
118
|
-
* Extract mount information from mount call
|
|
119
|
-
*/
|
|
120
|
-
private extractMountFromCall;
|
|
121
|
-
/**
|
|
122
|
-
* Check if a reference is to an external dependency
|
|
123
|
-
*/
|
|
124
|
-
private checkIfExternalDependency;
|
|
125
|
-
/**
|
|
126
|
-
* Get package version from package.json
|
|
127
|
-
*/
|
|
128
|
-
private getPackageVersion;
|
|
129
|
-
/**
|
|
130
|
-
* Generate OpenAPI specification
|
|
23
|
+
* Generate OpenAPI specification.
|
|
24
|
+
* Must be called after analyze().
|
|
131
25
|
*/
|
|
132
26
|
generateOpenAPISpec(): any;
|
|
133
27
|
}
|
|
@@ -135,4 +29,3 @@ export declare class OpenAPIAnalyzer {
|
|
|
135
29
|
* Analyze a directory and generate OpenAPI spec
|
|
136
30
|
*/
|
|
137
31
|
export declare function analyzeDirectory(directory: string): Promise<any>;
|
|
138
|
-
export {};
|