@skyhook-io/k8s-ui 1.17.0 → 1.18.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/README.md +41 -0
- package/package.json +6 -6
- package/src/components/charts/AreaChart.interaction.test.tsx +118 -0
- package/src/components/charts/AreaChart.test.tsx +25 -0
- package/src/components/charts/AreaChart.tsx +141 -12
- package/src/components/charts/index.ts +1 -0
- package/src/components/checks/ChecksView.tsx +18 -1
- package/src/components/cnpg/CNPGBackupSummary.tsx +57 -9
- package/src/components/cnpg/CNPGClusterHASection.test.tsx +90 -0
- package/src/components/cnpg/CNPGClusterHASection.tsx +331 -0
- package/src/components/cnpg/CNPGClusterSummary.test.tsx +275 -0
- package/src/components/cnpg/CNPGClusterSummary.tsx +310 -96
- package/src/components/cnpg/CNPGConnectSection.test.tsx +64 -0
- package/src/components/cnpg/CNPGConnectSection.tsx +178 -0
- package/src/components/cnpg/CNPGDeclarativeSummary.tsx +157 -26
- package/src/components/cnpg/CNPGImageCatalogSummary.tsx +7 -4
- package/src/components/cnpg/CNPGLogicalPath.test.tsx +22 -0
- package/src/components/cnpg/CNPGLogicalPath.tsx +150 -0
- package/src/components/cnpg/CNPGObjectStoreSummary.tsx +9 -6
- package/src/components/cnpg/CNPGObjectSummary.test.tsx +193 -2
- package/src/components/cnpg/CNPGPoolerScheduling.test.tsx +36 -0
- package/src/components/cnpg/CNPGPoolerSummary.tsx +241 -14
- package/src/components/cnpg/CNPGSharedSummary.tsx +15 -8
- package/src/components/cnpg/CNPGWALArchivingFact.test.tsx +39 -0
- package/src/components/cnpg/backupRuns.test.ts +17 -0
- package/src/components/cnpg/backupRuns.ts +19 -0
- package/src/components/cnpg/connect.test.ts +124 -0
- package/src/components/cnpg/connect.ts +155 -0
- package/src/components/cnpg/databaseRole.test.ts +38 -0
- package/src/components/cnpg/databaseRole.ts +80 -0
- package/src/components/cnpg/ha.test.ts +446 -0
- package/src/components/cnpg/ha.ts +621 -0
- package/src/components/cnpg/index.ts +18 -0
- package/src/components/cnpg/logicalReplication.test.ts +121 -0
- package/src/components/cnpg/logicalReplication.ts +252 -0
- package/src/components/cnpg/pooler.test.ts +84 -0
- package/src/components/cnpg/pooler.ts +225 -0
- package/src/components/cnpg/primitives.tsx +21 -117
- package/src/components/cnpg/problems.ts +12 -0
- package/src/components/cnpg/relations.test.ts +63 -13
- package/src/components/cnpg/relations.ts +61 -52
- package/src/components/cnpg/schedule.ts +35 -0
- package/src/components/cnpg/workspace-disk.test.ts +97 -0
- package/src/components/cnpg/workspace-fleet-metrics.test.ts +250 -0
- package/src/components/cnpg/workspace-problems.test.ts +175 -0
- package/src/components/cnpg/workspace.test.ts +380 -9
- package/src/components/cnpg/workspace.ts +1137 -136
- package/src/components/dock/DockContext.test.tsx +32 -0
- package/src/components/dock/DockContext.tsx +18 -4
- package/src/components/dock/LocalTerminalTab.test.tsx +40 -0
- package/src/components/dock/LocalTerminalTab.tsx +4 -3
- package/src/components/dock/TerminalTab.tsx +20 -8
- package/src/components/facts/certainty.tsx +36 -0
- package/src/components/facts/facts.test.tsx +24 -0
- package/src/components/facts/facts.tsx +62 -0
- package/src/components/facts/index.ts +6 -0
- package/src/components/facts/managed-by.test.tsx +17 -0
- package/src/components/facts/managed-by.tsx +36 -0
- package/src/components/facts/public-exports.test.tsx +35 -0
- package/src/components/gitops/GitOpsGraphFilterRail.tsx +7 -3
- package/src/components/gitops/GitOpsHealthSourceNotice.test.tsx +38 -0
- package/src/components/gitops/GitOpsHealthSourceNotice.tsx +13 -3
- package/src/components/gitops/insights/ArgoResourceDiff.test.tsx +142 -0
- package/src/components/gitops/insights/ArgoResourceDiff.tsx +22 -5
- package/src/components/gitops/insights/DriftPanel.tsx +62 -0
- package/src/components/gitops/insights/GitOpsInsightViews.tsx +40 -81
- package/src/components/gitops/insights/insights-helpers.test.ts +18 -0
- package/src/components/gitops/insights/insights-helpers.ts +7 -0
- package/src/components/gitops/tree/GitOpsTreeGraph.test.ts +91 -2
- package/src/components/gitops/tree/GitOpsTreeGraph.tsx +76 -76
- package/src/components/gitops/tree/layout.test.ts +111 -0
- package/src/components/gitops/tree/layout.ts +128 -0
- package/src/components/gitops/tree/tree-helpers.test.ts +23 -0
- package/src/components/gitops/tree/tree-helpers.ts +12 -2
- package/src/components/issues/ConditionalCleanup.test.tsx +82 -0
- package/src/components/issues/IssuesView.tsx +44 -6
- package/src/components/issues/ResourceIssuesSection.test.tsx +28 -0
- package/src/components/issues/ResourceIssuesSection.tsx +8 -2
- package/src/components/issues/diagnostic.ts +2 -0
- package/src/components/issues/index.ts +3 -1
- package/src/components/issues/issue-timing.ts +12 -1
- package/src/components/issues/issues.test.ts +75 -1
- package/src/components/issues/severity.ts +30 -2
- package/src/components/issues/types.ts +55 -4
- package/src/components/logs/LogCore.theme.test.tsx +206 -3
- package/src/components/logs/LogCore.tsx +180 -53
- package/src/components/logs/LogToolbarSelects.tsx +3 -1
- package/src/components/logs/LogsViewer.test.tsx +369 -0
- package/src/components/logs/LogsViewer.tsx +91 -13
- package/src/components/logs/StructuredLogLine.test.tsx +76 -0
- package/src/components/logs/StructuredLogLine.tsx +23 -31
- package/src/components/logs/WorkloadLogsViewer.test.tsx +65 -0
- package/src/components/logs/WorkloadLogsViewer.tsx +46 -11
- package/src/components/logs/useLogBuffer.reading.test.tsx +149 -0
- package/src/components/logs/useLogBuffer.test.ts +8 -0
- package/src/components/logs/useLogBuffer.ts +48 -18
- package/src/components/logs/useLogSearch.ts +3 -1
- package/src/components/logs/useLogStream.ts +17 -1
- package/src/components/problems/index.ts +1 -0
- package/src/components/problems/problems.test.tsx +63 -0
- package/src/components/problems/problems.tsx +165 -0
- package/src/components/resources/ActiveFilterBar.test.tsx +78 -0
- package/src/components/resources/ActiveFilterBar.tsx +86 -0
- package/src/components/resources/ColumnPickerList.test.tsx +46 -0
- package/src/components/resources/ColumnPickerList.tsx +174 -0
- package/src/components/resources/ResourcesSidebar.keyboard.test.tsx +42 -0
- package/src/components/resources/ResourcesSidebar.test.tsx +36 -2
- package/src/components/resources/ResourcesSidebar.tsx +92 -41
- package/src/components/resources/ResourcesView.kind-filter-memory.test.ts +52 -0
- package/src/components/resources/ResourcesView.kind-view.test.tsx +296 -0
- package/src/components/resources/ResourcesView.per-container.test.ts +56 -1
- package/src/components/resources/ResourcesView.tsx +392 -181
- package/src/components/resources/column-order.test.ts +104 -0
- package/src/components/resources/column-order.ts +64 -0
- package/src/components/resources/curated-column-ownership.test.ts +3 -3
- package/src/components/resources/renderers/ArgoApplicationRenderer.test.tsx +15 -0
- package/src/components/resources/renderers/ArgoApplicationRenderer.tsx +4 -1
- package/src/components/resources/renderers/AutoscalingReferences.test.tsx +286 -0
- package/src/components/resources/renderers/CAPIClusterRenderer.tsx +3 -2
- package/src/components/resources/renderers/CAPIExactReferenceNavigation.test.tsx +27 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.test.tsx +23 -0
- package/src/components/resources/renderers/CNPGBackupRenderer.tsx +8 -19
- package/src/components/resources/renderers/CNPGClusterRenderer.tsx +5 -7
- package/src/components/resources/renderers/CNPGDeclarativeRenderer.tsx +25 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.test.tsx +32 -0
- package/src/components/resources/renderers/CNPGScheduledBackupRenderer.tsx +7 -15
- package/src/components/resources/renderers/CompositeRenderer.navigation.test.tsx +35 -0
- package/src/components/resources/renderers/CompositeRenderer.tsx +3 -3
- package/src/components/resources/renderers/CompositionRenderer.tsx +3 -3
- package/src/components/resources/renderers/ContourExactReferenceNavigation.test.tsx +34 -0
- package/src/components/resources/renderers/ContourHTTPProxyRenderer.tsx +11 -10
- package/src/components/resources/renderers/CronJobRenderer.test.tsx +31 -0
- package/src/components/resources/renderers/CronJobRenderer.tsx +28 -20
- package/src/components/resources/renderers/EndpointSliceRenderer.test.tsx +39 -1
- package/src/components/resources/renderers/EndpointSliceRenderer.tsx +14 -13
- package/src/components/resources/renderers/FluxHelmReleaseRenderer.tsx +22 -9
- package/src/components/resources/renderers/HPARenderer.tsx +56 -16
- package/src/components/resources/renderers/IngressRenderer.defaultBackend.test.tsx +33 -0
- package/src/components/resources/renderers/IngressRenderer.tsx +30 -3
- package/src/components/resources/renderers/IstioDestinationRuleRenderer.tsx +9 -9
- package/src/components/resources/renderers/IstioExactReferenceNavigation.test.tsx +34 -0
- package/src/components/resources/renderers/IstioVirtualServiceRenderer.tsx +5 -8
- package/src/components/resources/renderers/KedaScaledObjectRenderer.tsx +13 -11
- package/src/components/resources/renderers/KnativeEventingRenderer.tsx +24 -31
- package/src/components/resources/renderers/KnativeExactReferenceNavigation.test.tsx +47 -0
- package/src/components/resources/renderers/KnativeFlowRenderer.tsx +6 -7
- package/src/components/resources/renderers/KnativeNetworkingRenderer.tsx +7 -9
- package/src/components/resources/renderers/KnativeReferenceLink.tsx +14 -0
- package/src/components/resources/renderers/KnativeReferenceLinks.test.tsx +60 -0
- package/src/components/resources/renderers/KnativeSourceRenderer.tsx +9 -11
- package/src/components/resources/renderers/ManagedResourceRenderer.tsx +2 -2
- package/src/components/resources/renderers/MetricsUnavailableNotice.tsx +3 -2
- package/src/components/resources/renderers/PodRenderer.sidecar-terminal.test.tsx +200 -0
- package/src/components/resources/renderers/PodRenderer.test.tsx +32 -0
- package/src/components/resources/renderers/PodRenderer.tsx +81 -40
- package/src/components/resources/renderers/SimpleRouteRenderer.tsx +1 -1
- package/src/components/resources/renderers/VPARenderer.tsx +17 -11
- package/src/components/resources/resource-utils-capi.ts +1 -1
- package/src/components/resources/resource-utils-cnpg.test.ts +35 -10
- package/src/components/resources/resource-utils-cnpg.ts +51 -20
- package/src/components/resources/resource-utils-contour.test.ts +44 -0
- package/src/components/resources/resource-utils-contour.ts +16 -0
- package/src/components/resources/resource-utils-istio.ts +12 -8
- package/src/components/resources/resource-utils-knative.ts +18 -0
- package/src/components/resources/resource-utils.ts +96 -7
- package/src/components/resources/rules-column-width.test.ts +1 -1
- package/src/components/resources/summarize-scheduler-message.test.ts +31 -0
- package/src/components/shared/ActionConfirmDialog.test.tsx +154 -0
- package/src/components/shared/ActionConfirmDialog.tsx +246 -0
- package/src/components/shared/CreateResourceDialog.test.tsx +330 -0
- package/src/components/shared/CreateResourceDialog.tsx +126 -80
- package/src/components/shared/DetailShell.test.tsx +80 -0
- package/src/components/shared/DetailShell.tsx +85 -10
- package/src/components/shared/DrainPlanDialog.test.tsx +7 -0
- package/src/components/shared/DrainPlanDialog.tsx +6 -2
- package/src/components/shared/EditableYamlView.test.tsx +7 -0
- package/src/components/shared/EditableYamlView.tsx +1 -1
- package/src/components/shared/ResourceActionsBar.tsx +14 -7
- package/src/components/shared/ResourceRendererDispatch.tsx +15 -10
- package/src/components/shared/index.ts +2 -0
- package/src/components/timeline/DiffViewer.test.tsx +69 -0
- package/src/components/timeline/DiffViewer.tsx +113 -4
- package/src/components/timeline/TimelineSwimlanes.tsx +1 -1
- package/src/components/timeline/shared.test.tsx +48 -0
- package/src/components/timeline/shared.tsx +14 -19
- package/src/components/timeline/timeline-filters.ts +1 -0
- package/src/components/topology/K8sResourceNode.test.ts +18 -1
- package/src/components/topology/K8sResourceNode.tsx +44 -2
- package/src/components/topology/TopologyFilterSidebar.tsx +1 -0
- package/src/components/topology/TopologyGraph.tsx +3 -71
- package/src/components/topology/TopologySearch.tsx +1 -0
- package/src/components/topology/internet-groups.test.ts +35 -0
- package/src/components/topology/internet-groups.ts +55 -0
- package/src/components/topology/layout.ts +1 -1
- package/src/components/ui/Badge.tsx +1 -0
- package/src/components/ui/FoldSection.tsx +68 -0
- package/src/components/ui/ForceDeleteConfirmDialog.test.tsx +62 -0
- package/src/components/ui/ForceDeleteConfirmDialog.tsx +24 -12
- package/src/components/ui/RefLink.tsx +20 -0
- package/src/components/ui/RowActionMenu.tsx +8 -3
- package/src/components/ui/Toast.tsx +15 -4
- package/src/components/ui/crd-definition-navigation.test.tsx +160 -0
- package/src/components/ui/drawer-components.test.tsx +110 -1
- package/src/components/ui/drawer-components.tsx +62 -9
- package/src/components/ui/index.ts +4 -2
- package/src/components/ui/monacoEditorFeatures.ts +77 -0
- package/src/components/ui/monacoRuntime.ts +14 -19
- package/src/components/ui/status-tone.test.ts +10 -1
- package/src/components/ui/status-tone.tsx +29 -0
- package/src/components/workload/WorkloadView.deleted.test.tsx +43 -0
- package/src/components/workload/WorkloadView.relationships.test.tsx +37 -0
- package/src/components/workload/WorkloadView.spec.test.tsx +149 -0
- package/src/components/workload/WorkloadView.tsx +193 -71
- package/src/index.ts +14 -1
- package/src/monaco-deep.d.ts +19 -12
- package/src/theme/components.css +13 -0
- package/src/types/capacity.ts +11 -2
- package/src/types/core.ts +53 -2
- package/src/types/gitops-flux-status.test.ts +32 -1
- package/src/types/gitops-insights.ts +20 -2
- package/src/types/gitops-tree.ts +12 -0
- package/src/types/gitops.ts +26 -6
- package/src/utils/api-resources.ts +7 -0
- package/src/utils/application-history.ts +1 -0
- package/src/utils/cnpg-backup.test.ts +26 -0
- package/src/utils/cnpg-backup.ts +70 -0
- package/src/utils/format.test.ts +9 -0
- package/src/utils/format.ts +4 -0
- package/src/utils/gitops-owner.ts +3 -2
- package/src/utils/grant.test.ts +19 -0
- package/src/utils/grant.ts +35 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/k8s-errors.ts +7 -7
- package/src/utils/log-format.test.ts +41 -0
- package/src/utils/log-format.ts +71 -15
- package/src/utils/log-level.ts +12 -8
- package/src/utils/navigation.test.ts +57 -0
- package/src/utils/navigation.ts +86 -8
- package/src/utils/object-reference-navigation.test.ts +43 -0
- package/src/utils/resource-hierarchy.test.ts +37 -0
- package/src/utils/resource-hierarchy.ts +15 -7
- package/src/utils/resource-icons.ts +2 -0
- package/src/utils/skeleton-yaml.test.ts +21 -0
- package/src/utils/skeleton-yaml.ts +16 -0
- package/src/utils/url-path.test.ts +35 -0
- package/src/utils/url-path.ts +21 -0
- package/src/utils/validators.test.ts +4 -13
- package/src/utils/validators.ts +5 -19
- package/src/utils/view-transition.test.ts +35 -1
- package/src/utils/view-transition.ts +16 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function stripTrailingSlashes(value: string): string {
|
|
2
|
+
let end = value.length
|
|
3
|
+
while (end > 0 && value[end - 1] === '/') end--
|
|
4
|
+
return value.slice(0, end)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function normalizeURLForComparison(value: string): string | null {
|
|
8
|
+
if (!value) return ''
|
|
9
|
+
if (value.includes('\\')) return null
|
|
10
|
+
try {
|
|
11
|
+
const url = new URL(value)
|
|
12
|
+
const authorityStart = value.indexOf('://')
|
|
13
|
+
if (authorityStart < 0 || !url.hostname || url.username || url.password || value.includes('?') || value.includes('#')) return null
|
|
14
|
+
const pathStart = value.indexOf('/', authorityStart + 3)
|
|
15
|
+
const path = pathStart < 0 ? '' : value.slice(pathStart)
|
|
16
|
+
const port = url.port || (url.protocol === 'https:' ? '443' : url.protocol === 'http:' ? '80' : '')
|
|
17
|
+
return `${url.protocol}//${url.hostname.toLowerCase()}:${port}${stripTrailingSlashes(path)}`
|
|
18
|
+
} catch {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -82,25 +82,16 @@ describe('validateRFC1123Subdomain', () => {
|
|
|
82
82
|
if (!r.valid) expect(r.error).toMatch(/253/)
|
|
83
83
|
})
|
|
84
84
|
|
|
85
|
-
it('
|
|
86
|
-
|
|
87
|
-
// separated label on top of the 253-char total. Without the
|
|
88
|
-
// per-label check, a single 200-char label slipped through
|
|
89
|
-
// client-side and was rejected only server-side.
|
|
90
|
-
const huge = 'a'.repeat(200)
|
|
91
|
-
const r = validateRFC1123Subdomain(huge)
|
|
92
|
-
expect(r.valid).toBe(false)
|
|
93
|
-
if (!r.valid) expect(r.error).toMatch(/63/)
|
|
85
|
+
it('accepts long undotted names under the Kubernetes total limit', () => {
|
|
86
|
+
expect(validateRFC1123Subdomain('a'.repeat(200)).valid).toBe(true)
|
|
94
87
|
})
|
|
95
88
|
|
|
96
89
|
it('accepts a 63-char label at the boundary', () => {
|
|
97
90
|
expect(validateRFC1123Subdomain('a'.repeat(63)).valid).toBe(true)
|
|
98
91
|
})
|
|
99
92
|
|
|
100
|
-
it('
|
|
101
|
-
|
|
102
|
-
expect(r.valid).toBe(false)
|
|
103
|
-
if (!r.valid) expect(r.error).toMatch(/63/)
|
|
93
|
+
it('accepts long dot-separated parts under the Kubernetes total limit', () => {
|
|
94
|
+
expect(validateRFC1123Subdomain(`ok.${'a'.repeat(64)}.also-ok`).valid).toBe(true)
|
|
104
95
|
})
|
|
105
96
|
})
|
|
106
97
|
|
package/src/utils/validators.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type ValidationResult =
|
|
|
17
17
|
| { valid: false; error: string }
|
|
18
18
|
|
|
19
19
|
const RFC1123_LABEL_MAX = 63
|
|
20
|
-
// Kubernetes namespace
|
|
20
|
+
// Kubernetes namespace names are RFC 1123 labels:
|
|
21
21
|
// lowercase alphanumeric or '-', start and end alphanumeric, ≤63 chars.
|
|
22
22
|
// Source: k8s.io/apimachinery/pkg/util/validation.IsDNS1123Label.
|
|
23
23
|
const RFC1123_LABEL_RE = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/
|
|
@@ -34,9 +34,7 @@ const HELM_RELEASE_NAME_MAX = 53
|
|
|
34
34
|
// https://helm.sh/docs/chart_best_practices/conventions/#chart-names
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
38
|
-
* pod / service / configmap / secret names — anything Kubernetes
|
|
39
|
-
* accepts as a single-segment identifier.
|
|
37
|
+
* Matches Kubernetes IsDNS1123Label, including namespace names.
|
|
40
38
|
*/
|
|
41
39
|
export function validateRFC1123Label(input: string): ValidationResult {
|
|
42
40
|
if (input.length === 0) {
|
|
@@ -64,9 +62,9 @@ export function validateRFC1123Label(input: string): ValidationResult {
|
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
65
|
+
* Matches Kubernetes IsDNS1123Subdomain: the whole name is capped at 253
|
|
66
|
+
* characters, without a separate 63-character cap on dot-separated parts.
|
|
67
|
+
* Namespace names use validateRFC1123Label instead.
|
|
70
68
|
*/
|
|
71
69
|
export function validateRFC1123Subdomain(input: string): ValidationResult {
|
|
72
70
|
if (input.length === 0) {
|
|
@@ -90,18 +88,6 @@ export function validateRFC1123Subdomain(input: string): ValidationResult {
|
|
|
90
88
|
error: 'must contain only lowercase letters, numbers, hyphens, and dots, and each segment must start/end with a letter or number',
|
|
91
89
|
}
|
|
92
90
|
}
|
|
93
|
-
// Per-label cap: K8s' IsDNS1123Subdomain enforces ≤ 63 chars
|
|
94
|
-
// PER dot-separated label on top of the 253-char total. The
|
|
95
|
-
// regex above doesn't catch that; without this check a single
|
|
96
|
-
// 200-char label passes here and only fails server-side.
|
|
97
|
-
for (const label of input.split('.')) {
|
|
98
|
-
if (label.length > RFC1123_LABEL_MAX) {
|
|
99
|
-
return {
|
|
100
|
-
valid: false,
|
|
101
|
-
error: `each dot-separated label must be at most ${RFC1123_LABEL_MAX} characters (got ${label.length} for "${label}")`,
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
91
|
return { valid: true }
|
|
106
92
|
}
|
|
107
93
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
-
import { isInvalidStateError, startViewTransitionSafe } from './view-transition'
|
|
2
|
+
import { isInvalidStateError, isWebKitGTK, startViewTransitionSafe } from './view-transition'
|
|
3
3
|
|
|
4
4
|
// SKY-833 bug 49: rapid tab / route switches surfaced
|
|
5
5
|
// "InvalidStateError: Transition was aborted because of invalid state"
|
|
@@ -79,3 +79,37 @@ describe('startViewTransitionSafe', () => {
|
|
|
79
79
|
expect(isInvalidStateError(new TypeError('y'))).toBe(false)
|
|
80
80
|
})
|
|
81
81
|
})
|
|
82
|
+
|
|
83
|
+
const WEBKITGTK_UA =
|
|
84
|
+
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15'
|
|
85
|
+
|
|
86
|
+
describe('isWebKitGTK', () => {
|
|
87
|
+
it('matches WebKitGTK user agents', () => {
|
|
88
|
+
expect(isWebKitGTK(WEBKITGTK_UA)).toBe(true)
|
|
89
|
+
expect(isWebKitGTK('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko)')).toBe(true)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('does not match other engines on Linux, or Safari', () => {
|
|
93
|
+
expect(isWebKitGTK('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36')).toBe(false)
|
|
94
|
+
expect(isWebKitGTK('Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0')).toBe(false)
|
|
95
|
+
expect(isWebKitGTK('Mozilla/5.0 (Linux; Android 15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Mobile Safari/537.36')).toBe(false)
|
|
96
|
+
expect(isWebKitGTK('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15')).toBe(false)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('startViewTransitionSafe under WebKitGTK', () => {
|
|
101
|
+
it('applies the update directly instead of starting a transition', () => {
|
|
102
|
+
const originalDoc = (globalThis as { document?: unknown }).document
|
|
103
|
+
const startSpy = vi.fn()
|
|
104
|
+
;(globalThis as { document: object }).document = { startViewTransition: startSpy }
|
|
105
|
+
const uaSpy = vi.spyOn(navigator, 'userAgent', 'get').mockReturnValue(WEBKITGTK_UA)
|
|
106
|
+
|
|
107
|
+
const update = vi.fn()
|
|
108
|
+
startViewTransitionSafe(update)
|
|
109
|
+
expect(update).toHaveBeenCalledTimes(1)
|
|
110
|
+
expect(startSpy).not.toHaveBeenCalled()
|
|
111
|
+
|
|
112
|
+
uaSpy.mockRestore()
|
|
113
|
+
;(globalThis as { document?: unknown }).document = originalDoc
|
|
114
|
+
})
|
|
115
|
+
})
|
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Other rejection types (programming errors, unsupported configs)
|
|
16
16
|
* are re-thrown so we don't accidentally hide real bugs.
|
|
17
|
+
*
|
|
18
|
+
* Under WebKitGTK (the Linux desktop app's webview) the update is applied
|
|
19
|
+
* without a transition: when WebKitGTK has no renderer buffer transport —
|
|
20
|
+
* WEBKIT_DISABLE_DMABUF_RENDERER or WEBKIT_DISABLE_COMPOSITING_MODE set, a
|
|
21
|
+
* distro patch, or an EGL stack without GBM/surfaceless platforms — starting
|
|
22
|
+
* a view transition segfaults the UI process. The page cannot tell which
|
|
23
|
+
* case it is in, so it skips the cross-fade on every WebKitGTK host.
|
|
17
24
|
*/
|
|
18
25
|
export function startViewTransitionSafe(update: () => void): void {
|
|
19
26
|
// Some lib.dom.d.ts versions don't yet expose `startViewTransition`
|
|
@@ -27,7 +34,7 @@ export function startViewTransitionSafe(update: () => void): void {
|
|
|
27
34
|
}
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
if (typeof doc.startViewTransition !== 'function') {
|
|
37
|
+
if (typeof doc.startViewTransition !== 'function' || isWebKitGTK(navigator.userAgent)) {
|
|
31
38
|
update()
|
|
32
39
|
return
|
|
33
40
|
}
|
|
@@ -60,3 +67,11 @@ export function isInvalidStateError(err: unknown): boolean {
|
|
|
60
67
|
(err as { name: unknown }).name === 'InvalidStateError'
|
|
61
68
|
)
|
|
62
69
|
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* WebKitGTK identifies as AppleWebKit on Linux. Chromium-based browsers on
|
|
73
|
+
* Linux and Android carry AppleWebKit too, so they are excluded by name.
|
|
74
|
+
*/
|
|
75
|
+
export function isWebKitGTK(userAgent: string): boolean {
|
|
76
|
+
return /\bLinux\b/.test(userAgent) && /AppleWebKit/.test(userAgent) && !/Chrome|Chromium|Android/.test(userAgent)
|
|
77
|
+
}
|