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