@prorobotech/openapi-k8s-toolkit 1.0.3 → 1.1.0-alpha.2

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,5 +1,5 @@
1
1
  import styled, { createGlobalStyle } from 'styled-components';
2
- import K, { useState, useRef, useLayoutEffect, useEffect, Fragment, createContext, useContext, useMemo, createElement, isValidElement, cloneElement, useCallback, useInsertionEffect, useSyncExternalStore, memo, Suspense, useReducer } from 'react';
2
+ import K, { useState, useRef, useReducer, useEffect, useCallback, useMemo, useLayoutEffect, Fragment, createContext, useContext, createElement, isValidElement, cloneElement, useInsertionEffect, useSyncExternalStore, memo, Suspense } from 'react';
3
3
  import { Input, Tree, Modal, Alert, theme, Select, Tag, Breadcrumb, Spin, Menu, Tooltip, Space, Button, Flex, Typography, Card as Card$1, Row, Col, Tabs, Form, Popover, notification, Dropdown, Table, Slider, InputNumber, Switch, Result, Progress, Checkbox, Empty } from 'antd';
4
4
  import { LoadingOutlined, ExclamationCircleFilled, CloseCircleFilled, CheckCircleFilled, PlusOutlined, ClearOutlined, MinusOutlined, CaretDownOutlined, CaretRightOutlined, InfoCircleOutlined, EyeOutlined, EyeInvisibleOutlined, SearchOutlined, MoreOutlined, CheckOutlined, CloseOutlined, BugOutlined, EllipsisOutlined } from '@ant-design/icons';
5
5
  import { useQuery, useQueries, useQueryClient } from '@tanstack/react-query';
@@ -8343,6 +8343,649 @@ const useDirectUnknownResource = ({
8343
8343
  });
8344
8344
  };
8345
8345
 
8346
+ const useK8sVerbs = ({
8347
+ cluster,
8348
+ group,
8349
+ version,
8350
+ plural,
8351
+ isEnabled = true
8352
+ }) => {
8353
+ const uri = `/api/clusters/${cluster}/openapi-bff/verbs/getResourceVerbs?${new URLSearchParams({
8354
+ ...group ? { group } : {},
8355
+ version,
8356
+ plural
8357
+ }).toString()}`;
8358
+ const { data, isError, isLoading, error } = useDirectUnknownResource({
8359
+ uri,
8360
+ queryKey: ["k8s-verbs", group || "", version, plural],
8361
+ refetchInterval: false,
8362
+ isEnabled
8363
+ });
8364
+ const verbs = data?.verbs || [];
8365
+ const canList = verbs.includes("list");
8366
+ const canWatch = verbs.includes("watch");
8367
+ return {
8368
+ canList,
8369
+ canWatch,
8370
+ isError,
8371
+ isLoading,
8372
+ error
8373
+ };
8374
+ };
8375
+
8376
+ const eventKey$1 = (e) => {
8377
+ const n = e.metadata?.name ?? "";
8378
+ const ns = e.metadata?.namespace ?? "";
8379
+ return `${ns}/${n}`;
8380
+ };
8381
+ const compareRV$1 = (a, b) => {
8382
+ if (a.length !== b.length) return a.length > b.length ? 1 : -1;
8383
+ return a > b ? 1 : a < b ? -1 : 0;
8384
+ };
8385
+ const getRV$1 = (item) => item?.metadata?.resourceVersion;
8386
+
8387
+ const reducer$1 = (state, action) => {
8388
+ switch (action.type) {
8389
+ case "RESET": {
8390
+ const order = action.items.map(eventKey$1);
8391
+ const byKey = {};
8392
+ action.items.forEach((it) => byKey[eventKey$1(it)] = it);
8393
+ return { order, byKey };
8394
+ }
8395
+ case "APPEND_PAGE": {
8396
+ const next = { ...state.byKey };
8397
+ const addKeys = [];
8398
+ action.items.forEach((it) => {
8399
+ const k = eventKey$1(it);
8400
+ if (!next[k]) addKeys.push(k);
8401
+ next[k] = it;
8402
+ });
8403
+ return { order: [...state.order, ...addKeys], byKey: next };
8404
+ }
8405
+ case "UPSERT": {
8406
+ const k = eventKey$1(action.item);
8407
+ const exists = Boolean(state.byKey[k]);
8408
+ const byKey = { ...state.byKey, [k]: action.item };
8409
+ const order = exists ? state.order : [k, ...state.order];
8410
+ return { order, byKey };
8411
+ }
8412
+ case "REMOVE": {
8413
+ if (!state.byKey[action.key]) return state;
8414
+ const byKey = { ...state.byKey };
8415
+ delete byKey[action.key];
8416
+ return { order: state.order.filter((k) => k !== action.key), byKey };
8417
+ }
8418
+ default:
8419
+ return state;
8420
+ }
8421
+ };
8422
+
8423
+ const isRecord = (v) => typeof v === "object" && v !== null;
8424
+ const readString = (obj, key) => {
8425
+ const val = obj[key];
8426
+ return typeof val === "string" ? val : void 0;
8427
+ };
8428
+ const itemRV = (it) => {
8429
+ const fromUtil = getRV$1(it);
8430
+ if (fromUtil) return fromUtil;
8431
+ if (!isRecord(it)) return void 0;
8432
+ const rvTop = readString(it, "resourceVersion");
8433
+ const mdRaw = isRecord(it["metadata"]) ? it["metadata"] : void 0;
8434
+ const rvMeta = mdRaw ? readString(mdRaw, "resourceVersion") : void 0;
8435
+ return rvTop ?? rvMeta;
8436
+ };
8437
+ const getMaxRV$1 = (items) => (items ?? []).reduce((max, it) => {
8438
+ const rv = itemRV(it);
8439
+ return rv && (!max || compareRV$1(rv, max) > 0) ? rv : max;
8440
+ }, void 0);
8441
+ const makeResId = (q) => `${q.apiGroup ?? ""}|${q.apiVersion}|${q.plural}|${q.namespace ?? ""}|${q.fieldSelector ?? ""}|${q.labelSelector ?? ""}`;
8442
+ const useListWatch = ({
8443
+ wsUrl,
8444
+ pageSize,
8445
+ paused = false,
8446
+ ignoreRemove = false,
8447
+ onStatus,
8448
+ onError,
8449
+ autoDrain = false,
8450
+ preserveStateOnUrlChange = true,
8451
+ isEnabled = true,
8452
+ // NEW default: socket gated by this flag
8453
+ query
8454
+ }) => {
8455
+ const resId = `${query.apiGroup ?? ""}|${query.apiVersion}|${query.plural}|${query.namespace ?? ""}|${query.fieldSelector ?? ""}|${query.labelSelector ?? ""}`;
8456
+ const resIdRef = useRef(resId);
8457
+ const [state, dispatch] = useReducer(reducer$1, { order: [], byKey: {} });
8458
+ const [contToken, setContToken] = useState();
8459
+ const [hasMore, setHasMore] = useState(false);
8460
+ const [status, setStatus] = useState(isEnabled ? "connecting" : "closed");
8461
+ const [lastError, setLastError] = useState(void 0);
8462
+ const [isPaused, setIsPaused] = useState(paused);
8463
+ const [isRemoveIgnored, setIsRemoveIgnored] = useState(ignoreRemove);
8464
+ const queryRef = useRef(query);
8465
+ const wsRef = useRef(null);
8466
+ const connectingRef = useRef(false);
8467
+ const mountedRef = useRef(true);
8468
+ const startedRef = useRef(false);
8469
+ const reconnectTimerRef = useRef(null);
8470
+ const backoffRef = useRef(750);
8471
+ const urlRef = useRef(wsUrl);
8472
+ const onMessageRef = useRef(() => {
8473
+ });
8474
+ const connectRef = useRef(() => {
8475
+ });
8476
+ const fetchingRef = useRef(false);
8477
+ const anchorRVRef = useRef(void 0);
8478
+ const haveAnchorRef = useRef(false);
8479
+ const enabledRef = useRef(isEnabled);
8480
+ const intentionalCloseRef = useRef(false);
8481
+ const suppressErrorsRef = useRef(false);
8482
+ const pausedRef = useRef(isPaused);
8483
+ const ignoreRemoveRef = useRef(isRemoveIgnored);
8484
+ useEffect(() => {
8485
+ pausedRef.current = isPaused;
8486
+ }, [isPaused]);
8487
+ useEffect(() => {
8488
+ ignoreRemoveRef.current = isRemoveIgnored;
8489
+ }, [isRemoveIgnored]);
8490
+ useEffect(() => {
8491
+ enabledRef.current = isEnabled;
8492
+ }, [isEnabled]);
8493
+ const clearErrorSafe = useCallback(() => {
8494
+ setLastError(void 0);
8495
+ }, []);
8496
+ const setStatusSafe = useCallback(
8497
+ (s) => {
8498
+ setStatus(s);
8499
+ onStatus?.(s);
8500
+ },
8501
+ [onStatus]
8502
+ );
8503
+ const setErrorSafe = useCallback(
8504
+ (msg) => {
8505
+ setLastError(msg);
8506
+ if (msg) onError?.(msg);
8507
+ },
8508
+ [onError]
8509
+ );
8510
+ const applyParam = (sp, key, v) => {
8511
+ if (v === void 0 || v === null || v === "") {
8512
+ sp.delete(key);
8513
+ return;
8514
+ }
8515
+ sp.set(key, String(v));
8516
+ };
8517
+ const buildWsUrl = useCallback((raw) => {
8518
+ let u;
8519
+ const base = window.location.origin;
8520
+ try {
8521
+ const hasScheme = /^[a-z]+:/i.test(raw);
8522
+ u = hasScheme ? new URL(raw) : new URL(raw.startsWith("/") ? raw : `/${raw}`, base);
8523
+ if (u.protocol === "http:") u.protocol = "ws:";
8524
+ if (u.protocol === "https:") u.protocol = "wss:";
8525
+ if (u.protocol !== "ws:" && u.protocol !== "wss:") {
8526
+ u = new URL(u.pathname + u.search + u.hash, base);
8527
+ u.protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
8528
+ }
8529
+ } catch {
8530
+ const origin = window.location.origin.replace(/^http/, "ws");
8531
+ u = new URL(raw.startsWith("/") ? raw : `/${raw}`, origin);
8532
+ }
8533
+ const q = queryRef.current;
8534
+ applyParam(u.searchParams, "namespace", q.namespace);
8535
+ applyParam(u.searchParams, "limit", q.initialLimit);
8536
+ applyParam(u.searchParams, "_continue", q.initialContinue);
8537
+ applyParam(u.searchParams, "apiGroup", q.apiGroup);
8538
+ applyParam(u.searchParams, "apiVersion", q.apiVersion);
8539
+ applyParam(u.searchParams, "plural", q.plural);
8540
+ applyParam(u.searchParams, "fieldSelector", q.fieldSelector);
8541
+ applyParam(u.searchParams, "labelSelector", q.labelSelector);
8542
+ if (haveAnchorRef.current && anchorRVRef.current) {
8543
+ u.searchParams.set("sinceRV", anchorRVRef.current);
8544
+ } else {
8545
+ u.searchParams.delete("sinceRV");
8546
+ }
8547
+ return u.toString();
8548
+ }, []);
8549
+ const closeWS = useCallback(() => {
8550
+ try {
8551
+ wsRef.current?.close();
8552
+ } catch {
8553
+ }
8554
+ wsRef.current = null;
8555
+ }, []);
8556
+ const scheduleReconnect = useCallback(() => {
8557
+ if (intentionalCloseRef.current) {
8558
+ intentionalCloseRef.current = false;
8559
+ return;
8560
+ }
8561
+ if (!enabledRef.current) {
8562
+ setStatusSafe("closed");
8563
+ connectingRef.current = false;
8564
+ return;
8565
+ }
8566
+ setStatusSafe("closed");
8567
+ connectingRef.current = false;
8568
+ const baseDelay = Math.min(backoffRef.current, 8e3);
8569
+ const jitter = Math.random() * 0.4 + 0.8;
8570
+ const wait = Math.floor(baseDelay * jitter);
8571
+ const next = Math.min(baseDelay * 2, 12e3);
8572
+ backoffRef.current = next;
8573
+ if (reconnectTimerRef.current) {
8574
+ window.clearTimeout(reconnectTimerRef.current);
8575
+ reconnectTimerRef.current = null;
8576
+ }
8577
+ reconnectTimerRef.current = window.setTimeout(() => {
8578
+ if (!mountedRef.current || !enabledRef.current) return;
8579
+ connectRef.current();
8580
+ }, wait);
8581
+ }, [setStatusSafe]);
8582
+ const connect = useCallback(() => {
8583
+ if (!mountedRef.current) return;
8584
+ if (!enabledRef.current) {
8585
+ setStatusSafe("closed");
8586
+ return;
8587
+ }
8588
+ if (connectingRef.current) return;
8589
+ if (wsRef.current && (wsRef.current.readyState === WebSocket.OPEN || wsRef.current.readyState === WebSocket.CONNECTING)) {
8590
+ return;
8591
+ }
8592
+ connectingRef.current = true;
8593
+ setStatusSafe("connecting");
8594
+ setErrorSafe(void 0);
8595
+ const url = buildWsUrl(urlRef.current);
8596
+ console.debug("[useListWatch] connecting to", url);
8597
+ const ws = new WebSocket(url);
8598
+ wsRef.current = ws;
8599
+ ws.addEventListener("open", () => {
8600
+ if (!mountedRef.current || !enabledRef.current) return;
8601
+ backoffRef.current = 750;
8602
+ fetchingRef.current = false;
8603
+ setStatusSafe("open");
8604
+ connectingRef.current = false;
8605
+ suppressErrorsRef.current = false;
8606
+ });
8607
+ ws.addEventListener("message", (ev) => onMessageRef.current(ev));
8608
+ ws.addEventListener("close", scheduleReconnect);
8609
+ ws.addEventListener("error", () => {
8610
+ if (intentionalCloseRef.current || suppressErrorsRef.current) return;
8611
+ setErrorSafe("WebSocket error");
8612
+ });
8613
+ }, [buildWsUrl, scheduleReconnect, setErrorSafe, setStatusSafe]);
8614
+ useEffect(() => {
8615
+ connectRef.current = connect;
8616
+ }, [connect]);
8617
+ const reconnect = useCallback(() => {
8618
+ if (!enabledRef.current) {
8619
+ closeWS();
8620
+ setStatusSafe("closed");
8621
+ return;
8622
+ }
8623
+ if (reconnectTimerRef.current) {
8624
+ window.clearTimeout(reconnectTimerRef.current);
8625
+ reconnectTimerRef.current = null;
8626
+ }
8627
+ intentionalCloseRef.current = true;
8628
+ try {
8629
+ wsRef.current?.close();
8630
+ } catch {
8631
+ }
8632
+ wsRef.current = null;
8633
+ connect();
8634
+ }, [closeWS, connect, setStatusSafe]);
8635
+ useEffect(() => {
8636
+ if (!mountedRef.current) return;
8637
+ if (isEnabled) {
8638
+ connect();
8639
+ } else {
8640
+ if (reconnectTimerRef.current) {
8641
+ window.clearTimeout(reconnectTimerRef.current);
8642
+ reconnectTimerRef.current = null;
8643
+ }
8644
+ closeWS();
8645
+ setStatusSafe("closed");
8646
+ }
8647
+ }, [isEnabled, closeWS, connect, setStatusSafe]);
8648
+ const setUrl = useCallback(
8649
+ (next) => {
8650
+ const changed = next !== urlRef.current;
8651
+ urlRef.current = next;
8652
+ if (changed) {
8653
+ clearErrorSafe();
8654
+ suppressErrorsRef.current = true;
8655
+ if (!preserveStateOnUrlChange) {
8656
+ dispatch({ type: "RESET", items: [] });
8657
+ setContToken(void 0);
8658
+ setHasMore(false);
8659
+ anchorRVRef.current = void 0;
8660
+ haveAnchorRef.current = false;
8661
+ }
8662
+ if (enabledRef.current) reconnect();
8663
+ }
8664
+ },
8665
+ [preserveStateOnUrlChange, reconnect, clearErrorSafe]
8666
+ );
8667
+ const setQuery = useCallback(
8668
+ (q) => {
8669
+ clearErrorSafe();
8670
+ suppressErrorsRef.current = true;
8671
+ const prev = queryRef.current;
8672
+ const prevId = makeResId(prev);
8673
+ const nextId = makeResId(q);
8674
+ queryRef.current = q;
8675
+ if (!preserveStateOnUrlChange) {
8676
+ dispatch({ type: "RESET", items: [] });
8677
+ setContToken(void 0);
8678
+ setHasMore(false);
8679
+ }
8680
+ if (prevId !== nextId) {
8681
+ anchorRVRef.current = void 0;
8682
+ haveAnchorRef.current = false;
8683
+ }
8684
+ if (enabledRef.current && prevId !== nextId) {
8685
+ reconnect();
8686
+ }
8687
+ },
8688
+ [clearErrorSafe, preserveStateOnUrlChange, reconnect]
8689
+ );
8690
+ const total = state.order.length;
8691
+ const continueToken = contToken;
8692
+ useEffect(() => {
8693
+ onMessageRef.current = (ev) => {
8694
+ let frame;
8695
+ try {
8696
+ frame = JSON.parse(String(ev.data));
8697
+ } catch {
8698
+ return;
8699
+ }
8700
+ if (!frame) return;
8701
+ if (frame.type === "INITIAL") {
8702
+ dispatch({ type: "RESET", items: frame.items });
8703
+ setContToken(frame.continue);
8704
+ setHasMore(Boolean(frame.continue));
8705
+ setErrorSafe(void 0);
8706
+ fetchingRef.current = false;
8707
+ suppressErrorsRef.current = false;
8708
+ const snapshotRV = frame.resourceVersion || getMaxRV$1(frame.items);
8709
+ if (snapshotRV) {
8710
+ anchorRVRef.current = snapshotRV;
8711
+ haveAnchorRef.current = true;
8712
+ }
8713
+ return;
8714
+ }
8715
+ if (frame.type === "PAGE") {
8716
+ dispatch({ type: "APPEND_PAGE", items: frame.items });
8717
+ setContToken(frame.continue);
8718
+ setHasMore(Boolean(frame.continue));
8719
+ fetchingRef.current = false;
8720
+ const batchRV = getMaxRV$1(frame.items);
8721
+ if (batchRV && (!anchorRVRef.current || compareRV$1(batchRV, anchorRVRef.current) > 0)) {
8722
+ anchorRVRef.current = batchRV;
8723
+ }
8724
+ return;
8725
+ }
8726
+ if (frame.type === "PAGE_ERROR") {
8727
+ setErrorSafe(frame.error || "Failed to load next page");
8728
+ fetchingRef.current = false;
8729
+ return;
8730
+ }
8731
+ if (frame.type === "ADDED" || frame.type === "MODIFIED" || frame.type === "DELETED") {
8732
+ const rv = itemRV(frame.item);
8733
+ if (rv && (!anchorRVRef.current || compareRV$1(rv, anchorRVRef.current) > 0)) {
8734
+ anchorRVRef.current = rv;
8735
+ }
8736
+ }
8737
+ if (!pausedRef.current) {
8738
+ if (frame.type === "ADDED" || frame.type === "MODIFIED") {
8739
+ dispatch({ type: "UPSERT", item: frame.item });
8740
+ }
8741
+ if (!ignoreRemoveRef.current && frame.type === "DELETED") {
8742
+ dispatch({ type: "REMOVE", key: eventKey$1(frame.item) });
8743
+ }
8744
+ }
8745
+ };
8746
+ }, [setErrorSafe]);
8747
+ useEffect(() => {
8748
+ if (startedRef.current) return void 0;
8749
+ startedRef.current = true;
8750
+ mountedRef.current = true;
8751
+ if (isEnabled) {
8752
+ connect();
8753
+ } else {
8754
+ setStatusSafe("closed");
8755
+ }
8756
+ return () => {
8757
+ mountedRef.current = false;
8758
+ startedRef.current = false;
8759
+ if (reconnectTimerRef.current) {
8760
+ window.clearTimeout(reconnectTimerRef.current);
8761
+ reconnectTimerRef.current = null;
8762
+ }
8763
+ closeWS();
8764
+ wsRef.current = null;
8765
+ connectingRef.current = false;
8766
+ };
8767
+ }, []);
8768
+ useEffect(() => {
8769
+ if (wsUrl !== urlRef.current) setUrl(wsUrl);
8770
+ }, [wsUrl, setUrl]);
8771
+ useEffect(() => {
8772
+ if (resIdRef.current !== resId) {
8773
+ clearErrorSafe();
8774
+ suppressErrorsRef.current = true;
8775
+ anchorRVRef.current = void 0;
8776
+ haveAnchorRef.current = false;
8777
+ resIdRef.current = resId;
8778
+ queryRef.current = query;
8779
+ if (enabledRef.current) reconnect();
8780
+ }
8781
+ }, [resId, query, reconnect, clearErrorSafe]);
8782
+ const pageSizeRef = useRef(pageSize);
8783
+ useEffect(() => {
8784
+ pageSizeRef.current = pageSize;
8785
+ }, [pageSize]);
8786
+ const sendScroll = useCallback(() => {
8787
+ if (!enabledRef.current) return;
8788
+ const token = contToken;
8789
+ if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return;
8790
+ if (!token || fetchingRef.current) return;
8791
+ fetchingRef.current = true;
8792
+ const msg = { type: "SCROLL", continue: token, limit: pageSizeRef.current };
8793
+ wsRef.current.send(JSON.stringify(msg));
8794
+ }, [contToken]);
8795
+ const drainAll = useCallback(
8796
+ async (opts) => {
8797
+ if (!enabledRef.current) return 0;
8798
+ const maxPages = opts?.maxPages ?? 999;
8799
+ const maxItems = opts?.maxItems ?? Number.POSITIVE_INFINITY;
8800
+ let pages = 0;
8801
+ let added = 0;
8802
+ const awaitOnce = () => new Promise((resolve) => {
8803
+ const handler = (ev) => {
8804
+ try {
8805
+ const f = JSON.parse(String(ev.data));
8806
+ if (f.type === "PAGE") {
8807
+ const newCount = (f.items || []).reduce((acc, it) => {
8808
+ const k = eventKey$1(it);
8809
+ return state.byKey[k] ? acc : acc + 1;
8810
+ }, 0);
8811
+ added += newCount;
8812
+ const ws2 = wsRef.current;
8813
+ if (!ws2) {
8814
+ resolve("STOP");
8815
+ return;
8816
+ }
8817
+ resolve("PAGE");
8818
+ }
8819
+ } catch {
8820
+ }
8821
+ };
8822
+ const ws = wsRef.current;
8823
+ if (!ws) {
8824
+ resolve("STOP");
8825
+ return;
8826
+ }
8827
+ const stopCheck = () => {
8828
+ if (!hasMore || !contToken) {
8829
+ resolve("STOP");
8830
+ }
8831
+ };
8832
+ ws.addEventListener("message", handler, { once: true });
8833
+ setTimeout(stopCheck, 0);
8834
+ });
8835
+ while (pages < maxPages && hasMore && contToken && wsRef.current?.readyState === WebSocket.OPEN) {
8836
+ if (added >= maxItems) break;
8837
+ if (!fetchingRef.current) sendScroll();
8838
+ const r = await awaitOnce();
8839
+ if (r === "STOP") break;
8840
+ pages += 1;
8841
+ }
8842
+ return added;
8843
+ },
8844
+ [contToken, hasMore, sendScroll, state.byKey]
8845
+ );
8846
+ useEffect(() => {
8847
+ if (!autoDrain) return;
8848
+ if (!enabledRef.current) return;
8849
+ if (status === "open" && haveAnchorRef.current) {
8850
+ drainAll().catch(() => {
8851
+ });
8852
+ }
8853
+ }, [autoDrain, drainAll, status]);
8854
+ return {
8855
+ state,
8856
+ total,
8857
+ hasMore,
8858
+ continueToken,
8859
+ status,
8860
+ lastError,
8861
+ setPaused: setIsPaused,
8862
+ setIgnoreRemove: setIsRemoveIgnored,
8863
+ sendScroll,
8864
+ drainAll,
8865
+ reconnect,
8866
+ setUrl,
8867
+ setQuery
8868
+ };
8869
+ };
8870
+
8871
+ const buildApiPrefix = (group, version) => {
8872
+ const g = (group ?? "").trim();
8873
+ const v = (version ?? "").trim();
8874
+ const isCore = !g || g === "core" || g === "v1";
8875
+ return isCore ? `/api/${v}` : `/apis/${g}/${v}`;
8876
+ };
8877
+ const buildListUri = ({
8878
+ cluster,
8879
+ group,
8880
+ version,
8881
+ plural,
8882
+ namespace,
8883
+ fieldSelector,
8884
+ labelSelector,
8885
+ limit
8886
+ }) => {
8887
+ const prefix = buildApiPrefix(group, version);
8888
+ const ns = namespace ? `/namespaces/${namespace}` : "";
8889
+ const base = `/api/clusters/${cluster}/k8s${prefix}${ns}/${plural}/`;
8890
+ const params = new URLSearchParams();
8891
+ if (fieldSelector) params.append("fieldSelector", fieldSelector);
8892
+ if (labelSelector) params.append("labelSelector", labelSelector);
8893
+ if (limit) params.append("limit", String(limit));
8894
+ return params.toString() ? `${base}?${params.toString()}` : base;
8895
+ };
8896
+ const useK8sSmartResource = ({
8897
+ cluster,
8898
+ group,
8899
+ version,
8900
+ plural,
8901
+ namespace,
8902
+ fieldSelector,
8903
+ labelSelector,
8904
+ isEnabled = true,
8905
+ listRefetchInterval = 5e3,
8906
+ limit,
8907
+ mapListWatchState
8908
+ }) => {
8909
+ const {
8910
+ canList,
8911
+ canWatch,
8912
+ isLoading: verbsLoading,
8913
+ isError: verbsIsError,
8914
+ error: verbsErrorObj
8915
+ } = useK8sVerbs({
8916
+ cluster,
8917
+ group,
8918
+ version,
8919
+ plural,
8920
+ isEnabled
8921
+ });
8922
+ const listUri = buildListUri({
8923
+ cluster,
8924
+ group,
8925
+ version,
8926
+ plural,
8927
+ namespace,
8928
+ fieldSelector,
8929
+ labelSelector,
8930
+ limit
8931
+ });
8932
+ const restEnabled = Boolean(isEnabled && canList && !canWatch && !verbsLoading && !verbsIsError);
8933
+ const {
8934
+ data: restData,
8935
+ isLoading: restLoading,
8936
+ isError: restIsError,
8937
+ error: restError
8938
+ } = useDirectUnknownResource({
8939
+ uri: listUri,
8940
+ queryKey: [
8941
+ "k8s-list",
8942
+ cluster,
8943
+ group || "",
8944
+ version,
8945
+ namespace || "",
8946
+ plural,
8947
+ fieldSelector || "",
8948
+ labelSelector || ""
8949
+ ],
8950
+ refetchInterval: listRefetchInterval,
8951
+ isEnabled: restEnabled
8952
+ });
8953
+ const watchEnabled = Boolean(isEnabled && canList && canWatch && !verbsLoading && !verbsIsError);
8954
+ const { state, status, lastError } = useListWatch({
8955
+ wsUrl: `/api/clusters/${cluster}/openapi-bff-ws/listThenWatch/listWatchWs`,
8956
+ paused: false,
8957
+ ignoreRemove: false,
8958
+ autoDrain: true,
8959
+ preserveStateOnUrlChange: true,
8960
+ isEnabled: watchEnabled,
8961
+ pageSize: limit,
8962
+ query: {
8963
+ apiGroup: group,
8964
+ apiVersion: version,
8965
+ plural,
8966
+ namespace,
8967
+ fieldSelector,
8968
+ labelSelector
8969
+ }
8970
+ });
8971
+ const defaultMap = (s) => ({ items: s.order.map((k) => s.byKey[k]) });
8972
+ const watchData = useMemo(
8973
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8974
+ () => watchEnabled ? (mapListWatchState ?? defaultMap)(state) : void 0,
8975
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8976
+ [watchEnabled, state, mapListWatchState]
8977
+ );
8978
+ const used = !isEnabled ? "disabled" : verbsLoading ? "verbs-loading" : verbsIsError ? "verbs-error" : watchEnabled ? "watch" : restEnabled ? "list" : "disabled";
8979
+ const isLoading = isEnabled && verbsLoading || used === "watch" && status === "connecting" || used === "list" && restLoading;
8980
+ let error;
8981
+ if (verbsIsError) error = verbsErrorObj;
8982
+ else if (used === "watch" && status === "closed" && lastError) error = lastError;
8983
+ else if (used === "list" && restIsError) error = restError;
8984
+ const isError = Boolean(error);
8985
+ const data = used === "watch" ? watchData : used === "list" ? restData : void 0;
8986
+ return { data, isLoading, isError, error, _meta: { used } };
8987
+ };
8988
+
8346
8989
  const prepareTemplate = ({
8347
8990
  template,
8348
8991
  replaceValues
@@ -8381,7 +9024,7 @@ const prepareDataForManageableBreadcrumbs = ({
8381
9024
  replaceValues,
8382
9025
  idToCompare
8383
9026
  }) => {
8384
- const foundData = data.find((el) => el.id === idToCompare);
9027
+ const foundData = data.find((el) => el?.id === idToCompare);
8385
9028
  if (!foundData) {
8386
9029
  return void 0;
8387
9030
  }
@@ -8480,8 +9123,10 @@ const ManageableBreadcrumbs = ({ data }) => {
8480
9123
  return /* @__PURE__ */ jsxRuntimeExports.jsx(Styled$v.HeightDiv, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleBreadcrumb, { items: data.breadcrumbItems }) });
8481
9124
  };
8482
9125
  const ManageableBreadcrumbsWithDataProvider = ({
8483
- uri,
8484
- refetchInterval,
9126
+ cluster,
9127
+ apiGroup,
9128
+ apiVersion,
9129
+ plural,
8485
9130
  isEnabled,
8486
9131
  replaceValues,
8487
9132
  pathname,
@@ -8491,10 +9136,11 @@ const ManageableBreadcrumbsWithDataProvider = ({
8491
9136
  data: rawData,
8492
9137
  isError: rawDataError,
8493
9138
  isLoading: rawDataLoading
8494
- } = useDirectUnknownResource({
8495
- uri,
8496
- refetchInterval,
8497
- queryKey: ["breadcrumb", uri],
9139
+ } = useK8sSmartResource({
9140
+ cluster: cluster || "",
9141
+ group: apiGroup,
9142
+ version: apiVersion,
9143
+ plural,
8498
9144
  isEnabled
8499
9145
  });
8500
9146
  if (rawDataError) {
@@ -8682,8 +9328,10 @@ const ManageableSidebar = ({ data, noMarginTop }) => {
8682
9328
  );
8683
9329
  };
8684
9330
  const ManageableSidebarWithDataProvider = ({
8685
- uri,
8686
- refetchInterval,
9331
+ cluster,
9332
+ apiGroup,
9333
+ apiVersion,
9334
+ plural,
8687
9335
  isEnabled,
8688
9336
  replaceValues,
8689
9337
  pathname,
@@ -8696,10 +9344,11 @@ const ManageableSidebarWithDataProvider = ({
8696
9344
  data: rawData,
8697
9345
  isError: rawDataError,
8698
9346
  isLoading: rawDataLoading
8699
- } = useDirectUnknownResource({
8700
- uri,
8701
- refetchInterval,
8702
- queryKey: ["sidebar", uri],
9347
+ } = useK8sSmartResource({
9348
+ cluster,
9349
+ group: apiGroup,
9350
+ version: apiVersion,
9351
+ plural,
8703
9352
  isEnabled
8704
9353
  });
8705
9354
  if (rawDataError) {
@@ -49641,22 +50290,6 @@ const getCreatePathToNav = ({
49641
50290
  }
49642
50291
  return `/${baseprefix}/${clusterName}/${namespace}/forms/builtin/${apiVersion}/${typeName}?backlink=${window.location.pathname}`;
49643
50292
  };
49644
- const getListPath = ({
49645
- clusterName,
49646
- namespace,
49647
- type,
49648
- typeName,
49649
- apiGroup,
49650
- apiVersion
49651
- }) => {
49652
- if (type === "crd") {
49653
- return `/api/clusters/${clusterName}/k8s/apis/${apiGroup}/${apiVersion}${namespace ? `/namespaces/${namespace}` : ""}/${typeName}`;
49654
- }
49655
- if (type === "nonCrd") {
49656
- return `/api/clusters/${clusterName}/k8s/apis/${apiGroup}/${apiVersion}${namespace ? `/namespaces/${namespace}` : ""}/${typeName}`;
49657
- }
49658
- return `/api/clusters/${clusterName}/k8s/api/v1${namespace ? `/namespaces/${namespace}` : ""}/${typeName}`;
49659
- };
49660
50293
 
49661
50294
  const CustomCard$4 = styled(Card$1)`
49662
50295
  position: relative;
@@ -49789,19 +50422,13 @@ const MarketplaceCard = ({
49789
50422
  apiVersion,
49790
50423
  baseprefix
49791
50424
  });
49792
- const listUrl = addedMode && type !== "direct" ? getListPath({
49793
- clusterName,
50425
+ const { data: k8sList, error: k8sListError } = useK8sSmartResource({
50426
+ cluster: clusterName || "",
49794
50427
  namespace,
49795
- type,
49796
- typeName,
49797
- apiGroup,
49798
- apiVersion
49799
- }) : void 0;
49800
- const { data: k8sList, error: k8sListError } = useDirectUnknownResource({
49801
- uri: listUrl || "",
49802
- queryKey: [listUrl || ""],
49803
- refetchInterval: false,
49804
- isEnabled: addedMode && listUrl !== void 0
50428
+ group: apiGroup,
50429
+ version: apiVersion || "",
50430
+ plural: type,
50431
+ isEnabled: Boolean(apiVersion && addedMode && type !== "direct")
49805
50432
  });
49806
50433
  if (addedMode && (k8sListError || type === "direct") && !showZeroResources) {
49807
50434
  return null;
@@ -49935,11 +50562,12 @@ const MarketPlace = ({
49935
50562
  data: marketplacePanels,
49936
50563
  isLoading,
49937
50564
  error
49938
- } = useDirectUnknownResource({
49939
- uri: `/api/clusters/${clusterName}/k8s/apis/${baseApiGroup}/${baseApiVersion}/${mpResourceName}/`,
49940
- refetchInterval: 5e3,
49941
- queryKey: ["marketplacePanels", clusterName || "no-cluster"],
49942
- isEnabled: clusterName !== void 0
50565
+ } = useK8sSmartResource({
50566
+ cluster: clusterName || "",
50567
+ group: baseApiGroup,
50568
+ version: baseApiVersion,
50569
+ plural: mpResourceName,
50570
+ isEnabled: Boolean(clusterName !== void 0)
49943
50571
  });
49944
50572
  const createPermission = usePermissions({
49945
50573
  group: baseApiGroup,
@@ -50243,22 +50871,26 @@ const ProjectInfoCard = ({
50243
50871
  data: marketplacePanels,
50244
50872
  isLoading: marketplaceIsLoading
50245
50873
  // error: marketplaceError,
50246
- } = useDirectUnknownResource({
50247
- uri: `/api/clusters/${clusterName}/k8s/apis/${baseApiGroup}/${baseApiVersion}/${mpResourceName}/`,
50248
- refetchInterval: 5e3,
50249
- queryKey: ["marketplacePanels", clusterName || "no-cluster"],
50250
- isEnabled: clusterName !== void 0
50874
+ } = useK8sSmartResource({
50875
+ cluster: clusterName || "",
50876
+ group: baseApiGroup,
50877
+ version: baseApiVersion,
50878
+ plural: mpResourceName,
50879
+ isEnabled: Boolean(clusterName !== void 0)
50251
50880
  });
50252
50881
  const {
50253
- data: project,
50882
+ data: projectArr,
50254
50883
  isLoading,
50255
50884
  error
50256
- } = useDirectUnknownResource({
50257
- uri: `/api/clusters/${clusterName}/k8s/apis/${baseProjectApiGroup}/${baseProjectVersion}/${projectResourceName}/${namespace}`,
50258
- refetchInterval: 5e3,
50259
- queryKey: ["projects", clusterName || "no-cluster"],
50260
- isEnabled: clusterName !== void 0
50885
+ } = useK8sSmartResource({
50886
+ cluster: clusterName || "",
50887
+ group: baseProjectApiGroup,
50888
+ version: baseProjectVersion,
50889
+ plural: projectResourceName,
50890
+ fieldSelector: `metadata.name=${namespace}`,
50891
+ isEnabled: Boolean(clusterName !== void 0)
50261
50892
  });
50893
+ const project = projectArr && projectArr.length > 0 ? projectArr[0] : void 0;
50262
50894
  const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
50263
50895
  const updatePermission = usePermissions({
50264
50896
  group: baseProjectApiGroup,
@@ -52900,5 +53532,18 @@ const useCrdData = ({
52900
53532
  });
52901
53533
  };
52902
53534
 
52903
- export { BackToDefaultIcon, BlackholeForm, BlackholeFormDataProvider, ContentCard$1 as ContentCard, CursorDefaultDiv, CursorPointerTag, CursorPointerTagMinContent, CustomSelect$4 as CustomSelect, DeleteIcon, DeleteModal, DeleteModalMany, DownIcon, DynamicComponents, DynamicRenderer, DynamicRendererWithProviders, EarthIcon, EditIcon, EnrichedTable, EnrichedTableProvider, Events, FlexGrow, LockedIcon, LookingGlassIcon, ManageableBreadcrumbs, ManageableBreadcrumbsWithDataProvider, ManageableSidebar, ManageableSidebarWithDataProvider, MarketPlace, MarketplaceCard, MinusIcon, NodeTerminal, PaddingContainer, PauseCircleIcon, PlusIcon, PodLogs, PodLogsMonaco, PodTerminal, ProjectInfoCard, ResumeCircleIcon, Search, Spacer$1 as Spacer, SuccessIcon, TreeWithSearch, UncontrolledSelect, UnlockedIcon, UpIcon, YamlEditorSingleton, checkIfApiInstanceNamespaceScoped, checkIfBuiltInInstanceNamespaceScoped, checkPermission, createContextFactory, createNewEntry, deepMerge, deleteEntry, feedbackIcons, filterIfApiInstanceNamespaceScoped, filterIfBuiltInInstanceNamespaceScoped, filterSelectOptions, floorToDecimal, getAllPathsFromObj, getApiResourceSingle, getApiResourceTypes, getApiResourceTypesByApiGroup, getApiResources, getBackLinkToTable, getBuiltinResourceSingle, getBuiltinResourceTypes, getBuiltinResources, getBuiltinTreeData, getClusterList, getCrdData, getCrdResourceSingle, getCrdResources, getDirectUnknownResource, getEnrichedColumns, getEnrichedColumnsWithControls, getGroupsByCategory, getKinds, getLinkToApiForm, getLinkToBuiltinForm, getLinkToForm, getObjectFormItemsDraft, getPrefixSubarrays, getSortedKinds, getSortedKindsAll, getStringByName, getSwagger, getUppercase, groupsToTreeData, hslFromString, isFlatObject, isMultilineFromYaml, isMultilineString, kindByGvr, namespacedByGvr, normalizeValuesForQuotasToNumber, parseQuotaValue, parseQuotaValueCpu, parseQuotaValueMemoryAndStorage, pluralByKind, prepareDataForManageableBreadcrumbs, prepareDataForManageableSidebar, prepareTemplate, prepareUrlsToFetchForDynamicRenderer, updateEntry, useApiResourceSingle, useApiResourceTypesByGroup, useApiResources, useApisResourceTypes, useBuiltinResourceSingle, useBuiltinResourceTypes, useBuiltinResources, useClusterList, useCrdData, useCrdResourceSingle, useCrdResources, useDirectUnknownResource, usePermissions };
53535
+ const useInfiniteSentinel = (sentinelRef, hasMore, onNeedMore) => {
53536
+ useEffect(() => {
53537
+ const el = sentinelRef.current;
53538
+ if (!el) return void 0;
53539
+ const io = new IntersectionObserver((entries) => {
53540
+ const visible = entries.some((e) => e.isIntersecting);
53541
+ if (visible && hasMore) onNeedMore();
53542
+ });
53543
+ io.observe(el);
53544
+ return () => io.disconnect();
53545
+ }, [sentinelRef, hasMore, onNeedMore]);
53546
+ };
53547
+
53548
+ export { BackToDefaultIcon, BlackholeForm, BlackholeFormDataProvider, ContentCard$1 as ContentCard, CursorDefaultDiv, CursorPointerTag, CursorPointerTagMinContent, CustomSelect$4 as CustomSelect, DeleteIcon, DeleteModal, DeleteModalMany, DownIcon, DynamicComponents, DynamicRenderer, DynamicRendererWithProviders, EarthIcon, EditIcon, EnrichedTable, EnrichedTableProvider, Events, FlexGrow, LockedIcon, LookingGlassIcon, ManageableBreadcrumbs, ManageableBreadcrumbsWithDataProvider, ManageableSidebar, ManageableSidebarWithDataProvider, MarketPlace, MarketplaceCard, MinusIcon, NodeTerminal, PaddingContainer, PauseCircleIcon, PlusIcon, PodLogs, PodLogsMonaco, PodTerminal, ProjectInfoCard, ResumeCircleIcon, Search, Spacer$1 as Spacer, SuccessIcon, TreeWithSearch, UncontrolledSelect, UnlockedIcon, UpIcon, YamlEditorSingleton, checkIfApiInstanceNamespaceScoped, checkIfBuiltInInstanceNamespaceScoped, checkPermission, createContextFactory, createNewEntry, deepMerge, deleteEntry, feedbackIcons, filterIfApiInstanceNamespaceScoped, filterIfBuiltInInstanceNamespaceScoped, filterSelectOptions, floorToDecimal, getAllPathsFromObj, getApiResourceSingle, getApiResourceTypes, getApiResourceTypesByApiGroup, getApiResources, getBackLinkToTable, getBuiltinResourceSingle, getBuiltinResourceTypes, getBuiltinResources, getBuiltinTreeData, getClusterList, getCrdData, getCrdResourceSingle, getCrdResources, getDirectUnknownResource, getEnrichedColumns, getEnrichedColumnsWithControls, getGroupsByCategory, getKinds, getLinkToApiForm, getLinkToBuiltinForm, getLinkToForm, getObjectFormItemsDraft, getPrefixSubarrays, getSortedKinds, getSortedKindsAll, getStringByName, getSwagger, getUppercase, groupsToTreeData, hslFromString, isFlatObject, isMultilineFromYaml, isMultilineString, kindByGvr, namespacedByGvr, normalizeValuesForQuotasToNumber, parseQuotaValue, parseQuotaValueCpu, parseQuotaValueMemoryAndStorage, pluralByKind, prepareDataForManageableBreadcrumbs, prepareDataForManageableSidebar, prepareTemplate, prepareUrlsToFetchForDynamicRenderer, updateEntry, useApiResourceSingle, useApiResourceTypesByGroup, useApiResources, useApisResourceTypes, useBuiltinResourceSingle, useBuiltinResourceTypes, useBuiltinResources, useClusterList, useCrdData, useCrdResourceSingle, useCrdResources, useDirectUnknownResource, useInfiniteSentinel, useK8sSmartResource, useK8sVerbs, useListWatch, usePermissions };
52904
53549
  //# sourceMappingURL=openapi-k8s-toolkit.es.js.map