k8s-av 1.0.6 → 1.0.7

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/dist/cli/start.js CHANGED
@@ -44,7 +44,7 @@ const exec = util.promisify(child_process_1.exec);
44
44
  const ROOT = path.resolve(__dirname, '..', '..');
45
45
  const UI_DIR = path.join(ROOT, 'ui');
46
46
  const BACKEND_URL = 'http://localhost:3001';
47
- const FRONTEND_URL = 'http://localhost:5173';
47
+ const DEFAULT_FRONTEND_URL = 'http://localhost:5174';
48
48
  const log = (msg) => console.log(` ${msg}`);
49
49
  const warn = (msg) => console.log(` ! ${msg}`);
50
50
  function waitFor(url, timeoutMs = 30000) {
@@ -87,6 +87,10 @@ function openBrowser(url) {
87
87
  ['xdg-open', [url]];
88
88
  (0, child_process_1.spawn)(cmd, args, { shell: false, detached: true, stdio: 'ignore' }).unref();
89
89
  }
90
+ function extractFrontendUrl(line) {
91
+ const match = line.match(/https?:\/\/(?:localhost|127\.0\.0\.1|\[[^\]]+\]):\d+/i);
92
+ return match?.[0] ?? null;
93
+ }
90
94
  async function ensureUiDeps() {
91
95
  const nmDir = path.join(UI_DIR, 'node_modules');
92
96
  const requiredPackages = [
@@ -114,6 +118,7 @@ async function runStart(opts) {
114
118
  console.log(' Kubernetes Attack Path Visualizer');
115
119
  console.log(' ' + (opts.source === 'mock' ? 'Demo mode (mock data)' : 'Live cluster mode'));
116
120
  console.log('='.repeat(62));
121
+ let frontendUrl = DEFAULT_FRONTEND_URL;
117
122
  if (opts.source === 'mock') {
118
123
  console.log('\n Info: Mock mode - skipping Docker and Neo4j preflight.');
119
124
  }
@@ -132,7 +137,7 @@ async function runStart(opts) {
132
137
  cwd: ROOT,
133
138
  stdio: ['ignore', 'pipe', 'pipe'],
134
139
  shell: false,
135
- env: { ...process.env, CORS_ORIGIN: FRONTEND_URL },
140
+ env: { ...process.env, CORS_ORIGIN: frontendUrl },
136
141
  });
137
142
  let backendExited = false;
138
143
  backend.stdout?.on('data', (d) => {
@@ -192,28 +197,36 @@ async function runStart(opts) {
192
197
  });
193
198
  frontend.stdout?.on('data', (d) => {
194
199
  const line = d.toString().trim();
195
- if (line)
196
- process.stdout.write(` [ui] ${line}\n`);
200
+ if (!line)
201
+ return;
202
+ const detectedUrl = extractFrontendUrl(line);
203
+ if (detectedUrl)
204
+ frontendUrl = detectedUrl;
205
+ process.stdout.write(` [ui] ${line}\n`);
197
206
  });
198
207
  frontend.stderr?.on('data', (d) => {
199
208
  const line = d.toString().trim();
200
- if (line)
201
- process.stderr.write(` [ui] ${line}\n`);
209
+ if (!line)
210
+ return;
211
+ const detectedUrl = extractFrontendUrl(line);
212
+ if (detectedUrl)
213
+ frontendUrl = detectedUrl;
214
+ process.stderr.write(` [ui] ${line}\n`);
202
215
  });
203
216
  try {
204
- await waitFor(FRONTEND_URL, 45000);
217
+ await waitFor(frontendUrl, 45000);
205
218
  }
206
219
  catch {
207
220
  warn('Vite did not respond in time - opening browser anyway.');
208
221
  }
209
222
  if (!opts.skipBrowser) {
210
223
  log('Opening browser...');
211
- openBrowser(FRONTEND_URL);
224
+ openBrowser(frontendUrl);
212
225
  }
213
226
  console.log('\n ' + '-'.repeat(58));
214
227
  console.log(' System ready');
215
228
  console.log(` Backend -> ${BACKEND_URL}`);
216
- console.log(` UI -> ${FRONTEND_URL}`);
229
+ console.log(` UI -> ${frontendUrl}`);
217
230
  console.log(' ' + '-'.repeat(58));
218
231
  console.log('\n Press Ctrl+C to stop.\n');
219
232
  const shutdown = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "k8s-av",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Kubernetes RBAC Attack Path Visualizer — scan your cluster, detect attack paths, visualize in a local UI",
5
5
  "keywords": [
6
6
  "kubernetes",
@@ -13,6 +13,7 @@
13
13
  "jspdf": "^4.2.1",
14
14
  "react": "^18.3.1",
15
15
  "react-dom": "^18.3.1",
16
+ "vite": "^5.2.11",
16
17
  "zustand": "^4.5.2"
17
18
  },
18
19
  "devDependencies": {
@@ -3025,15 +3026,6 @@
3025
3026
  "browserslist": ">= 4.21.0"
3026
3027
  }
3027
3028
  },
3028
- "node_modules/use-sync-external-store": {
3029
- "version": "1.6.0",
3030
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
3031
- "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
3032
- "license": "MIT",
3033
- "peerDependencies": {
3034
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
3035
- }
3036
- },
3037
3029
  "node_modules/util-deprecate": {
3038
3030
  "version": "1.0.2",
3039
3031
  "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -3145,6 +3137,15 @@
3145
3137
  "optional": true
3146
3138
  }
3147
3139
  }
3140
+ },
3141
+ "node_modules/zustand/node_modules/use-sync-external-store": {
3142
+ "version": "1.2.2",
3143
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
3144
+ "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
3145
+ "license": "MIT",
3146
+ "peerDependencies": {
3147
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
3148
+ }
3148
3149
  }
3149
3150
  }
3150
3151
  }
package/ui/package.json CHANGED
@@ -27,5 +27,8 @@
27
27
  "tailwindcss": "^3.4.3",
28
28
  "typescript": "^5.4.5",
29
29
  "vite": "^5.2.11"
30
+ },
31
+ "overrides": {
32
+ "use-sync-external-store": "1.2.2"
30
33
  }
31
34
  }
@@ -1,4 +1,5 @@
1
- import { create } from 'zustand';
1
+ import { useSyncExternalStore } from 'react';
2
+ import { createStore } from 'zustand/vanilla';
2
3
  import {
3
4
  api,
4
5
  GraphNode, GraphEdge,
@@ -53,14 +54,20 @@ interface AppState {
53
54
  simulate: (nodeId: string) => Promise<void>;
54
55
  }
55
56
 
56
- function setLoading(set: (fn: (s: AppState) => Partial<AppState>) => void, key: string, val: boolean) {
57
+ type StoreSet = (
58
+ partial: Partial<AppState> | ((state: AppState) => Partial<AppState>),
59
+ ) => void;
60
+
61
+ const identity = <T,>(value: T) => value;
62
+
63
+ function setLoading(set: StoreSet, key: string, val: boolean) {
57
64
  set((s) => ({ loading: { ...s.loading, [key]: val } }));
58
65
  }
59
- function setError(set: (fn: (s: AppState) => Partial<AppState>) => void, key: string, msg: string | null) {
66
+ function setError(set: StoreSet, key: string, msg: string | null) {
60
67
  set((s) => ({ errors: { ...s.errors, [key]: msg } }));
61
68
  }
62
69
 
63
- export const useAppStore = create<AppState>((set, get) => ({
70
+ const appStore = createStore<AppState>((set, get) => ({
64
71
  graphNodes: [],
65
72
  graphEdges: [],
66
73
  graphMeta: null,
@@ -173,3 +180,15 @@ export const useAppStore = create<AppState>((set, get) => ({
173
180
  }
174
181
  },
175
182
  }));
183
+
184
+ export function useAppStore(): AppState;
185
+ export function useAppStore<T>(selector: (state: AppState) => T): T;
186
+ export function useAppStore<T>(selector?: (state: AppState) => T): T | AppState {
187
+ const select = selector ?? identity<AppState>;
188
+
189
+ return useSyncExternalStore(
190
+ appStore.subscribe,
191
+ () => select(appStore.getState()),
192
+ () => select(appStore.getInitialState()),
193
+ );
194
+ }