@skyhook-io/k8s-ui 1.6.1 → 1.7.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/package.json +6 -1
- package/src/components/charts/AreaChart.tsx +371 -0
- package/src/components/charts/MetricsSummary.tsx +49 -0
- package/src/components/charts/SeriesLegend.tsx +27 -0
- package/src/components/charts/colors.ts +49 -0
- package/src/components/charts/format.ts +41 -0
- package/src/components/charts/index.ts +14 -0
- package/src/components/charts/saturation.test.ts +60 -0
- package/src/components/charts/saturation.ts +32 -0
- package/src/components/charts/types.ts +36 -0
- package/src/components/compare/CompareResourcePicker.tsx +192 -0
- package/src/components/compare/CompareTray.tsx +130 -0
- package/src/components/compare/ResourceCompareView.tsx +272 -0
- package/src/components/compare/index.ts +14 -0
- package/src/components/compare/normalize.test.ts +193 -0
- package/src/components/compare/normalize.ts +69 -0
- package/src/components/compare/picks.test.ts +97 -0
- package/src/components/compare/picks.ts +35 -0
- package/src/components/compare/sort.test.ts +78 -0
- package/src/components/compare/sort.ts +26 -0
- package/src/components/compare/types.ts +43 -0
- package/src/components/compare/url.test.ts +61 -0
- package/src/components/compare/url.ts +26 -0
- package/src/components/resources/ResourcesView.tsx +431 -96
- package/src/components/resources/index.ts +1 -0
- package/src/components/resources/renderers/CompositeRenderer.tsx +233 -0
- package/src/components/resources/renderers/CompositionRenderer.tsx +218 -0
- package/src/components/resources/renderers/CrossplanePackageRenderer.tsx +145 -0
- package/src/components/resources/renderers/CrossplaneProviderConfigRenderer.tsx +71 -0
- package/src/components/resources/renderers/HPARenderer.tsx +6 -1
- package/src/components/resources/renderers/ManagedResourceRenderer.tsx +131 -0
- package/src/components/resources/renderers/NamespaceRenderer.tsx +223 -0
- package/src/components/resources/renderers/PVCRenderer.tsx +6 -1
- package/src/components/resources/renderers/PodRenderer.tsx +207 -2
- package/src/components/resources/renderers/RoleBindingRenderer.tsx +132 -20
- package/src/components/resources/renderers/RoleRenderer.tsx +148 -18
- package/src/components/resources/renderers/ServiceAccountRenderer.tsx +456 -3
- package/src/components/resources/renderers/WorkloadRenderer.tsx +189 -2
- package/src/components/resources/renderers/XRDRenderer.tsx +153 -0
- package/src/components/resources/renderers/crossplane-cells.tsx +146 -0
- package/src/components/resources/renderers/flux-cells.tsx +12 -0
- package/src/components/resources/renderers/index.ts +8 -0
- package/src/components/resources/resource-utils-crossplane.test.ts +609 -0
- package/src/components/resources/resource-utils-crossplane.ts +325 -0
- package/src/components/resources/resource-utils-flux.ts +14 -0
- package/src/components/resources/resource-utils.ts +1 -0
- package/src/components/resources/resources-column-filter.test.ts +74 -0
- package/src/components/shared/ResourceActionsBar.tsx +77 -0
- package/src/components/shared/ResourceRendererDispatch.tsx +138 -9
- package/src/components/topology/K8sResourceNode.tsx +21 -2
- package/src/components/topology/TopologyGraph.tsx +46 -10
- package/src/components/topology/layout.ts +20 -11
- package/src/components/topology/layout.worker.ts +21 -17
- package/src/components/ui/YamlEditor.tsx +28 -15
- package/src/components/workload/ResourceDetailDrawer.tsx +1 -1
- package/src/index.ts +6 -0
- package/src/perf/index.ts +1 -0
- package/src/perf/store.ts +110 -0
- package/src/types/core.ts +10 -0
- package/src/types/index.ts +1 -0
- package/src/types/rbac.ts +99 -0
- package/src/utils/api-resources.ts +9 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/rbac-badges.ts +61 -0
- package/src/utils/rbac-blast-radius.test.ts +137 -0
- package/src/utils/rbac-blast-radius.ts +98 -0
- package/src/utils/resource-hierarchy.ts +13 -3
- package/src/utils/structure-hash.test.ts +94 -0
- package/src/utils/structure-hash.ts +35 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { fnv1a32, foldHash } from './structure-hash'
|
|
3
|
+
|
|
4
|
+
describe('fnv1a32', () => {
|
|
5
|
+
it('is deterministic', () => {
|
|
6
|
+
expect(fnv1a32('hello')).toBe(fnv1a32('hello'))
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('distinguishes similar strings', () => {
|
|
10
|
+
expect(fnv1a32('pod/default/a')).not.toBe(fnv1a32('pod/default/b'))
|
|
11
|
+
expect(fnv1a32('a')).not.toBe(fnv1a32('A'))
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('handles empty string', () => {
|
|
15
|
+
expect(typeof fnv1a32('')).toBe('number')
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('foldHash', () => {
|
|
20
|
+
const id = (s: string) => s
|
|
21
|
+
|
|
22
|
+
it('is order-independent', () => {
|
|
23
|
+
const a = foldHash(['a', 'b', 'c'], id)
|
|
24
|
+
const b = foldHash(['c', 'a', 'b'], id)
|
|
25
|
+
expect(a).toBe(b)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('changes when an element is added', () => {
|
|
29
|
+
const before = foldHash(['a', 'b'], id)
|
|
30
|
+
const after = foldHash(['a', 'b', 'c'], id)
|
|
31
|
+
expect(before).not.toBe(after)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('changes when an element is removed', () => {
|
|
35
|
+
const before = foldHash(['a', 'b', 'c'], id)
|
|
36
|
+
const after = foldHash(['a', 'b'], id)
|
|
37
|
+
expect(before).not.toBe(after)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('changes when an element is renamed', () => {
|
|
41
|
+
const before = foldHash(['a', 'b', 'c'], id)
|
|
42
|
+
const after = foldHash(['a', 'b', 'd'], id)
|
|
43
|
+
expect(before).not.toBe(after)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('returns the zero fingerprint for empty input', () => {
|
|
47
|
+
expect(foldHash([], id)).toBe('0.0')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('works with object items via keyOf', () => {
|
|
51
|
+
const items = [{ id: 'x' }, { id: 'y' }]
|
|
52
|
+
expect(foldHash(items, i => i.id)).toBe(foldHash(['x', 'y'], id))
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('emits a "<xor>.<sum>" shape', () => {
|
|
56
|
+
expect(foldHash(['a', 'b'], id)).toMatch(/^\d+\.\d+$/)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// The dual accumulator's whole point: a swap that preserves the XOR fold
|
|
60
|
+
// (a^b stays constant) must still change the fingerprint via the sum fold.
|
|
61
|
+
// Single-XOR would have collided here. Construct two sets with equal XOR
|
|
62
|
+
// but different members.
|
|
63
|
+
it('distinguishes sets that share an XOR but differ in membership', () => {
|
|
64
|
+
// {x} vs {y, z} where hash(x) === hash(y) ^ hash(z) would collide under
|
|
65
|
+
// pure XOR. We can't easily force that, so instead verify the additive
|
|
66
|
+
// fold breaks a known XOR-preserving transform: doubling an element.
|
|
67
|
+
// ['a','a'] XOR-folds to 0 (a^a), same as [] — but sum differs.
|
|
68
|
+
expect(foldHash(['a', 'a'], id)).not.toBe(foldHash([], id))
|
|
69
|
+
expect(foldHash(['a', 'a'], id)).not.toBe(foldHash(['b', 'b'], id))
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// The production guard against skipped relayouts is the *composed* key
|
|
74
|
+
// (count + foldHash), exactly as TopologyGraph builds it. These tests pin that
|
|
75
|
+
// composition, not just the bare fold — a genuine same-count add/remove/rename
|
|
76
|
+
// must change the composed key so the layout effect doesn't short-circuit.
|
|
77
|
+
describe('composed structure key (count + foldHash)', () => {
|
|
78
|
+
const id = (s: string) => s
|
|
79
|
+
// Mirrors TopologyGraph's structureKey shape for the node portion.
|
|
80
|
+
const composed = (nodeIds: string[]) => `n${nodeIds.length}:${foldHash(nodeIds, id)}`
|
|
81
|
+
|
|
82
|
+
it('changes on a same-count rename', () => {
|
|
83
|
+
expect(composed(['a', 'b', 'c'])).not.toBe(composed(['a', 'b', 'x']))
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('changes on add and on remove', () => {
|
|
87
|
+
expect(composed(['a', 'b'])).not.toBe(composed(['a', 'b', 'c']))
|
|
88
|
+
expect(composed(['a', 'b', 'c'])).not.toBe(composed(['a', 'b']))
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('is stable across reorder (no wasted relayout)', () => {
|
|
92
|
+
expect(composed(['a', 'b', 'c'])).toBe(composed(['c', 'b', 'a']))
|
|
93
|
+
})
|
|
94
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// FNV-1a 32-bit string hash. Cheap, well-distributed, no allocations.
|
|
2
|
+
export function fnv1a32(s: string): number {
|
|
3
|
+
let h = 0x811c9dc5
|
|
4
|
+
for (let i = 0; i < s.length; i++) {
|
|
5
|
+
h ^= s.charCodeAt(i)
|
|
6
|
+
h = Math.imul(h, 0x01000193)
|
|
7
|
+
}
|
|
8
|
+
return h >>> 0
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// foldHash returns an order-independent fingerprint of the given items as a
|
|
12
|
+
// "<xor>.<sum>" string. Used by TopologyGraph's structureKey change-detection
|
|
13
|
+
// — at thousands of nodes, sort+join over IDs allocated tens of KB of string
|
|
14
|
+
// every render; this is O(n) with two uint32 accumulators.
|
|
15
|
+
//
|
|
16
|
+
// Two independent commutative folds (XOR and 32-bit additive sum) are combined
|
|
17
|
+
// because a single collision in the structure key is a false negative on the
|
|
18
|
+
// exact path that serves big, high-churn graphs: the layout effect bails on an
|
|
19
|
+
// unchanged key, so a real shape change would silently skip setNodes/setEdges.
|
|
20
|
+
// A collision now requires BOTH folds to collide simultaneously across the
|
|
21
|
+
// difference set, which is vanishingly unlikely. Combine with element count
|
|
22
|
+
// (caller composes "count.xor.sum") for full structural identity.
|
|
23
|
+
//
|
|
24
|
+
// Order independence is intentional: pure reorders of the same node/edge set
|
|
25
|
+
// produce an identical graph and shouldn't trigger an ELK relayout.
|
|
26
|
+
export function foldHash<T>(items: ArrayLike<T>, keyOf: (item: T) => string): string {
|
|
27
|
+
let xor = 0
|
|
28
|
+
let sum = 0
|
|
29
|
+
for (let i = 0; i < items.length; i++) {
|
|
30
|
+
const h = fnv1a32(keyOf(items[i]))
|
|
31
|
+
xor ^= h
|
|
32
|
+
sum = (sum + h) >>> 0
|
|
33
|
+
}
|
|
34
|
+
return `${xor >>> 0}.${sum}`
|
|
35
|
+
}
|