shokupan 0.7.0 → 0.9.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.
Files changed (40) hide show
  1. package/README.md +53 -0
  2. package/dist/context.d.ts +50 -15
  3. package/dist/{http-server-DFhwlK8e.cjs → http-server-BEMPIs33.cjs} +4 -2
  4. package/dist/http-server-BEMPIs33.cjs.map +1 -0
  5. package/dist/{http-server-0xH174zz.js → http-server-CCeagTyU.js} +4 -2
  6. package/dist/http-server-CCeagTyU.js.map +1 -0
  7. package/dist/index.cjs +998 -136
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +996 -135
  11. package/dist/index.js.map +1 -1
  12. package/dist/plugins/application/dashboard/metrics-collector.d.ts +12 -0
  13. package/dist/plugins/application/dashboard/plugin.d.ts +14 -8
  14. package/dist/plugins/application/dashboard/static/charts.js +328 -0
  15. package/dist/plugins/application/dashboard/static/failures.js +85 -0
  16. package/dist/plugins/application/dashboard/static/graph.mjs +523 -0
  17. package/dist/plugins/application/dashboard/static/poll.js +146 -0
  18. package/dist/plugins/application/dashboard/static/reactflow.css +18 -0
  19. package/dist/plugins/application/dashboard/static/registry.css +131 -0
  20. package/dist/plugins/application/dashboard/static/registry.js +269 -0
  21. package/dist/plugins/application/dashboard/static/requests.js +118 -0
  22. package/dist/plugins/application/dashboard/static/scrollbar.css +24 -0
  23. package/dist/plugins/application/dashboard/static/styles.css +175 -0
  24. package/dist/plugins/application/dashboard/static/tables.js +92 -0
  25. package/dist/plugins/application/dashboard/static/tabs.js +113 -0
  26. package/dist/plugins/application/dashboard/static/tabulator.css +66 -0
  27. package/dist/plugins/application/dashboard/template.eta +246 -0
  28. package/dist/plugins/application/socket-io.d.ts +14 -0
  29. package/dist/router.d.ts +12 -0
  30. package/dist/shokupan.d.ts +21 -1
  31. package/dist/util/datastore.d.ts +4 -3
  32. package/dist/util/decorators.d.ts +5 -0
  33. package/dist/util/http-error.d.ts +38 -0
  34. package/dist/util/http-status.d.ts +30 -0
  35. package/dist/util/request.d.ts +1 -1
  36. package/dist/util/symbol.d.ts +19 -0
  37. package/dist/util/types.d.ts +30 -1
  38. package/package.json +6 -3
  39. package/dist/http-server-0xH174zz.js.map +0 -1
  40. package/dist/http-server-DFhwlK8e.cjs.map +0 -1
@@ -0,0 +1,175 @@
1
+ :root {
2
+ --bg-primary: #0f172a;
3
+ --bg-secondary: #1e293b;
4
+ --text-primary: #f8fafc;
5
+ --text-secondary: #94a3b8;
6
+ --text-emphasis: #60a5fa;
7
+ --accent: #3b82f6;
8
+ --success: #22c55e;
9
+ --error: #ef4444;
10
+ --card-border: #334155;
11
+ }
12
+
13
+ * {
14
+ box-sizing: border-box;
15
+ margin: 0;
16
+ padding: 0;
17
+ }
18
+
19
+ html {
20
+ height: 100%;
21
+ width: 100%;
22
+ }
23
+
24
+ body {
25
+ display: flex;
26
+ flex-direction: column;
27
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
28
+ background-color: var(--bg-primary);
29
+ color: var(--text-primary);
30
+ min-height: 100vh;
31
+ padding: 2rem;
32
+ }
33
+
34
+ header {
35
+ margin-bottom: 2rem;
36
+ display: flex;
37
+ justify-content: space-between;
38
+ align-items: center;
39
+ }
40
+
41
+ h1 {
42
+ font-size: 2rem;
43
+ font-weight: 700;
44
+ background: linear-gradient(to right, var(--accent), #8b5cf6);
45
+ -webkit-background-clip: text;
46
+ background-clip: text;
47
+ color: transparent;
48
+ }
49
+
50
+ select {
51
+ outline: none;
52
+ }
53
+
54
+ .container {
55
+ display: flex;
56
+ flex: 1;
57
+ flex-direction: column;
58
+ max-width: 1200px;
59
+ margin: 0 auto;
60
+ overflow: auto
61
+ }
62
+
63
+ .metrics-grid {
64
+ display: grid;
65
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
66
+ gap: 1.5rem;
67
+ margin-bottom: 2rem;
68
+ }
69
+
70
+ .card {
71
+ display: flex;
72
+ flex-direction: column;
73
+ background-color: var(--bg-secondary);
74
+ border: 1px solid var(--card-border);
75
+ border-radius: 1rem;
76
+ overflow: hidden;
77
+ }
78
+
79
+ .card canvas {
80
+ padding: 0 .5rem .5rem .5rem;
81
+ }
82
+
83
+ .card-chart {
84
+ flex: 1;
85
+ }
86
+
87
+ .card-value {
88
+ padding: 1rem;
89
+ }
90
+
91
+ .card-title {
92
+ color: var(--text-secondary);
93
+ font-size: 0.875rem;
94
+ margin-bottom: 0.5rem;
95
+ text-transform: uppercase;
96
+ letter-spacing: 0.05em;
97
+ height: 48px;
98
+ line-height: 48px;
99
+ padding-left: 16px;
100
+ font-weight: 600;
101
+ }
102
+
103
+ .card-value {
104
+ font-size: 2.5rem;
105
+ font-weight: 700;
106
+ }
107
+
108
+ .text-success {
109
+ color: var(--success);
110
+ }
111
+
112
+ .text-error {
113
+ color: var(--error);
114
+ }
115
+
116
+ #chart-container,
117
+ #table-container {
118
+ background-color: var(--bg-secondary);
119
+ border: 1px solid var(--card-border);
120
+ border-radius: 1rem;
121
+ padding: 1.5rem;
122
+ margin-bottom: 2rem;
123
+ overflow: hidden;
124
+ }
125
+
126
+ /* Tabs */
127
+ .tabs {
128
+ display: flex;
129
+ gap: 1rem;
130
+ border-bottom: 1px solid var(--card-border);
131
+ margin-bottom: 2rem;
132
+ }
133
+
134
+ .tab-btn {
135
+ background: none;
136
+ border: none;
137
+ color: var(--text-secondary);
138
+ padding: 1rem;
139
+ cursor: pointer;
140
+ font-size: 1rem;
141
+ border-bottom: 2px solid transparent;
142
+ transition: all 0.2s;
143
+ }
144
+
145
+ .tab-btn:hover {
146
+ color: var(--text-primary);
147
+ }
148
+
149
+ .tab-btn.active {
150
+ color: var(--accent);
151
+ border-bottom-color: var(--accent);
152
+ }
153
+
154
+ .tab-content {
155
+ display: none;
156
+ }
157
+
158
+ .tab-content.active {
159
+ display: block;
160
+ }
161
+
162
+ /* Graph */
163
+ #cy {
164
+ width: 100%;
165
+ height: 600px;
166
+ background-color: var(--bg-secondary);
167
+ border-radius: 1rem;
168
+ border: 1px solid var(--card-border);
169
+ }
170
+
171
+ .card-container {
172
+ display: grid;
173
+ gap: 1rem;
174
+ grid-template-columns: 1fr;
175
+ }
@@ -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,66 @@
1
+ .tabulator {
2
+ height: 100%;
3
+ width: 100%;
4
+ background-color: #0f1322;
5
+ }
6
+
7
+ .tabulator .tabulator-tableholder .tabulator-table {
8
+ color: #ccc;
9
+ background-color: #0f1322;
10
+ }
11
+
12
+ .tabulator .tabulator-header {
13
+ color: #ddd;
14
+ background-color: #0f1322;
15
+ border-top-color: #0b0b18;
16
+ border-bottom-color: #0b0b18;
17
+ }
18
+
19
+ .tabulator .tabulator-header .tabulator-col {
20
+ border-right-color: #15162f;
21
+ background-color: #0f1322;
22
+ }
23
+
24
+ .tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter {
25
+ color: #0d0f20;
26
+ }
27
+
28
+ .tabulator .tabulator-header .tabulator-col.tabulator-sortable.tabulator-col-sorter-element:hover {
29
+ background-color: #111427;
30
+ }
31
+
32
+ .tabulator .tabulator-row {
33
+ border-bottom-color: #0c0d23;
34
+ transition: background-color 100ms ease;
35
+ }
36
+
37
+ .tabulator .tabulator-row .tabulator-cell {
38
+ border-right-color: #11152c;
39
+ align-content: center;
40
+ }
41
+
42
+ .tabulator .tabulator-row:hover {
43
+ background-color: #202540 !important;
44
+ }
45
+
46
+ .tabulator .tabulator-row.tabulator-row-even {
47
+ background-color: #1c1e2c;
48
+ }
49
+
50
+ .tabulator .tabulator-row.tabulator-row-odd {
51
+ background-color: #171824;
52
+ }
53
+
54
+ .tabulator .tabulator-row.tabulator-selected {
55
+ background-color: #252a43;
56
+ }
57
+
58
+ .tabulator .tabulator-header .tabulator-col .tabulator-header-filter input {
59
+ background-color: #0e1016ff;
60
+ color: #ccc;
61
+ }
62
+
63
+ .tabulator .tabulator-header .tabulator-col .tabulator-header-filter input:focus {
64
+ background-color: #212637ff;
65
+ color: #fff
66
+ }