@rahdar/plugin-sdk 0.0.0-stage → 2026.10.50

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.
Files changed (154) hide show
  1. package/README.md +37 -2
  2. package/index.js +52 -0
  3. package/package.json +44 -4
  4. package/peers.json +8 -0
  5. package/shared.d.ts +3 -0
  6. package/shared.js +24 -0
  7. package/types/branding.d.ts +14 -0
  8. package/types/kernel/actions/ActionHost.d.ts +16 -0
  9. package/types/kernel/actions/hostContext.d.ts +4 -0
  10. package/types/kernel/actions/types.d.ts +130 -0
  11. package/types/kernel/api/paging.d.ts +39 -0
  12. package/types/kernel/api/polling.d.ts +37 -0
  13. package/types/kernel/api/request.d.ts +84 -0
  14. package/types/kernel/api/schema.d.ts +24077 -0
  15. package/types/kernel/approvals/ApprovalView.d.ts +6 -0
  16. package/types/kernel/approvals/ApprovalsView.d.ts +7 -0
  17. package/types/kernel/approvals/ApproverQuorumNotice.d.ts +8 -0
  18. package/types/kernel/approvals/BreakGlassBanner.d.ts +5 -0
  19. package/types/kernel/approvals/CancelRequest.d.ts +13 -0
  20. package/types/kernel/approvals/DecisionPanel.d.ts +11 -0
  21. package/types/kernel/approvals/ExpiresIn.d.ts +10 -0
  22. package/types/kernel/approvals/MyRequests.d.ts +5 -0
  23. package/types/kernel/approvals/Recap.d.ts +9 -0
  24. package/types/kernel/approvals/api.d.ts +130 -0
  25. package/types/kernel/approvals/columns.d.ts +22 -0
  26. package/types/kernel/approvals/words.d.ts +31 -0
  27. package/types/kernel/auth/Can.d.ts +9 -0
  28. package/types/kernel/auth/LoginView.d.ts +9 -0
  29. package/types/kernel/auth/SecondFactorForm.d.ts +25 -0
  30. package/types/kernel/auth/StepUp.d.ts +17 -0
  31. package/types/kernel/auth/api.d.ts +115 -0
  32. package/types/kernel/auth/freshSignIn.d.ts +2 -0
  33. package/types/kernel/auth/useCan.d.ts +49 -0
  34. package/types/kernel/auth/webauthn.d.ts +19 -0
  35. package/types/kernel/feature.d.ts +90 -0
  36. package/types/kernel/features.d.ts +5 -0
  37. package/types/kernel/inbox/InboxBell.d.ts +6 -0
  38. package/types/kernel/inbox/InboxView.d.ts +6 -0
  39. package/types/kernel/inbox/NoticeRow.d.ts +17 -0
  40. package/types/kernel/inbox/api.d.ts +53 -0
  41. package/types/kernel/keyboard/ShortcutsHelp.d.ts +6 -0
  42. package/types/kernel/keyboard/filterShortcut.d.ts +5 -0
  43. package/types/kernel/keyboard/keys.d.ts +14 -0
  44. package/types/kernel/keyboard/shortcuts.d.ts +5 -0
  45. package/types/kernel/keyboard/useKeySequences.d.ts +16 -0
  46. package/types/kernel/manifest.d.ts +21 -0
  47. package/types/kernel/metrics/ChartPanel.d.ts +28 -0
  48. package/types/kernel/metrics/MetricChart.d.ts +13 -0
  49. package/types/kernel/metrics/axis.d.ts +71 -0
  50. package/types/kernel/metrics/pluginSeries.d.ts +17 -0
  51. package/types/kernel/nav/currentView.d.ts +21 -0
  52. package/types/kernel/nav/groups.d.ts +22 -0
  53. package/types/kernel/plugins/PluginBoundary.d.ts +25 -0
  54. package/types/kernel/plugins/PluginUnavailable.d.ts +6 -0
  55. package/types/kernel/plugins/boot.d.ts +33 -0
  56. package/types/kernel/plugins/guarded.d.ts +3 -0
  57. package/types/kernel/plugins/usePluginsChanged.d.ts +6 -0
  58. package/types/kernel/plugins/validate.d.ts +19 -0
  59. package/types/kernel/registry.d.ts +12 -0
  60. package/types/kernel/routing/roots.d.ts +24 -0
  61. package/types/kernel/shell/AccountView.d.ts +11 -0
  62. package/types/kernel/shell/AdminView.d.ts +7 -0
  63. package/types/kernel/shell/Breadcrumb.d.ts +6 -0
  64. package/types/kernel/shell/ClusterLayout.d.ts +14 -0
  65. package/types/kernel/shell/ClusterViewNav.d.ts +13 -0
  66. package/types/kernel/shell/CommandPalette.d.ts +15 -0
  67. package/types/kernel/shell/FeatureDisabled.d.ts +13 -0
  68. package/types/kernel/shell/FeatureGate.d.ts +12 -0
  69. package/types/kernel/shell/FreshnessBar.d.ts +11 -0
  70. package/types/kernel/shell/GlobalContext.d.ts +7 -0
  71. package/types/kernel/shell/HomeView.d.ts +6 -0
  72. package/types/kernel/shell/NavGroup.d.ts +13 -0
  73. package/types/kernel/shell/NavItem.d.ts +33 -0
  74. package/types/kernel/shell/NavToggle.d.ts +13 -0
  75. package/types/kernel/shell/RootLayout.d.ts +15 -0
  76. package/types/kernel/shell/SectionNav.d.ts +41 -0
  77. package/types/kernel/shell/UserMenu.d.ts +10 -0
  78. package/types/kernel/shell/lastPlace.d.ts +17 -0
  79. package/types/kernel/shell/pageTitle.d.ts +13 -0
  80. package/types/kernel/shell/recents.d.ts +16 -0
  81. package/types/kernel/shell/useColorSchemeToggle.d.ts +10 -0
  82. package/types/kernel/shell/useFreshness.d.ts +25 -0
  83. package/types/kernel/shell/useNavCollapsed.d.ts +14 -0
  84. package/types/kernel/slots.d.ts +206 -0
  85. package/types/kernel/stream/sse.d.ts +22 -0
  86. package/types/kernel/stream/useClusterStream.d.ts +27 -0
  87. package/types/kernel/stream/useUserStream.d.ts +11 -0
  88. package/types/kernel/time/Ago.d.ts +9 -0
  89. package/types/kernel/time/ranges.d.ts +32 -0
  90. package/types/kernel/time/time.d.ts +89 -0
  91. package/types/kernel/time/timezone.d.ts +44 -0
  92. package/types/kernel/useDismissedNotice.d.ts +3 -0
  93. package/types/sdk/index.d.ts +92 -0
  94. package/types/ui/ActionMenuItem.d.ts +27 -0
  95. package/types/ui/AnchoredMenu.d.ts +20 -0
  96. package/types/ui/CapabilityGate.d.ts +23 -0
  97. package/types/ui/CapabilityReason.d.ts +11 -0
  98. package/types/ui/CodeEditor.d.ts +31 -0
  99. package/types/ui/ConfirmDialog.d.ts +51 -0
  100. package/types/ui/DescriptionList.d.ts +26 -0
  101. package/types/ui/DiagramView.d.ts +35 -0
  102. package/types/ui/DialogActions.d.ts +26 -0
  103. package/types/ui/EmptyState.d.ts +42 -0
  104. package/types/ui/ErrorState.d.ts +36 -0
  105. package/types/ui/FieldRow.d.ts +9 -0
  106. package/types/ui/HeldRequestLink.d.ts +4 -0
  107. package/types/ui/HoldToConfirm.d.ts +22 -0
  108. package/types/ui/LoadingState.d.ts +16 -0
  109. package/types/ui/NodeOutcomeSummary.d.ts +59 -0
  110. package/types/ui/Notice.d.ts +18 -0
  111. package/types/ui/Page.d.ts +13 -0
  112. package/types/ui/PageHeader.d.ts +18 -0
  113. package/types/ui/Pager.d.ts +21 -0
  114. package/types/ui/Section.d.ts +22 -0
  115. package/types/ui/Stat.d.ts +20 -0
  116. package/types/ui/StatusBadge.d.ts +11 -0
  117. package/types/ui/Toolbar.d.ts +19 -0
  118. package/types/ui/capabilityGate.d.ts +43 -0
  119. package/types/ui/codeDiagnostics.d.ts +11 -0
  120. package/types/ui/codeMirrorTheme.d.ts +11 -0
  121. package/types/ui/diagram.d.ts +49 -0
  122. package/types/ui/errorReading.d.ts +30 -0
  123. package/types/ui/formErrors.d.ts +10 -0
  124. package/types/ui/graph/ViewControls.d.ts +19 -0
  125. package/types/ui/graph/elk.d.ts +3 -0
  126. package/types/ui/held.d.ts +12 -0
  127. package/types/ui/inAppNavigation.d.ts +5 -0
  128. package/types/ui/nodeOutcome.d.ts +13 -0
  129. package/types/ui/notify.d.ts +77 -0
  130. package/types/ui/safeHref.d.ts +8 -0
  131. package/types/ui/table/ColumnsMenu.d.ts +26 -0
  132. package/types/ui/table/DataTable.d.ts +109 -0
  133. package/types/ui/table/GridFind.d.ts +16 -0
  134. package/types/ui/table/GridTable.d.ts +49 -0
  135. package/types/ui/table/HeaderCell.d.ts +25 -0
  136. package/types/ui/table/MiddleTruncate.d.ts +21 -0
  137. package/types/ui/table/StaticTable.d.ts +14 -0
  138. package/types/ui/table/TableStates.d.ts +35 -0
  139. package/types/ui/table/columns.d.ts +49 -0
  140. package/types/ui/table/density.d.ts +13 -0
  141. package/types/ui/table/gridFind.d.ts +7 -0
  142. package/types/ui/table/index.d.ts +5 -0
  143. package/types/ui/table/layout.d.ts +48 -0
  144. package/types/ui/table/measure.d.ts +16 -0
  145. package/types/ui/table/measurement.d.ts +92 -0
  146. package/types/ui/table/menuAnchor.d.ts +12 -0
  147. package/types/ui/table/rovingGrid.d.ts +37 -0
  148. package/types/ui/table/sort.d.ts +14 -0
  149. package/types/ui/table/tableState.d.ts +51 -0
  150. package/types/ui/table/useGridFind.d.ts +18 -0
  151. package/types/ui/trustedTypes.d.ts +16 -0
  152. package/types/ui/useTruncated.d.ts +8 -0
  153. package/vite.d.ts +14 -0
  154. package/vite.js +62 -0
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The landing page, with no cluster open: what the features contribute for it. The clusters feature
3
+ * sends the operator to the first cluster, so the page is seen only while that is decided or while no
4
+ * cluster is registered, which is what it is titled for.
5
+ */
6
+ export declare function HomeView(): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * A group of navigation rows under a heading, shared by the sidebar and every section list. The
4
+ * heading names the group but is not an HTML heading: the page's headings are the page's own. Collapsed
5
+ * (the sidebar's rail), the heading is drawn as a rule in the same box, so the icons below it keep their
6
+ * place, and its name stays available to a screen reader.
7
+ */
8
+ export declare function NavGroup({ label, collapsed, sticky, children, }: Readonly<{
9
+ label: string;
10
+ collapsed?: boolean;
11
+ sticky?: boolean;
12
+ children: ReactNode;
13
+ }>): import("react").JSX.Element;
@@ -0,0 +1,33 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * One navigation row, the only one: the sidebar's views and every section list (Settings,
4
+ * Administration, Configuration) use it, so they look and behave alike. It is a full-width block whose
5
+ * whole surface is the link, with its label on the same start edge as the group heading above it
6
+ * (`NavGroup`). Collapsed state hides the label visually but never from a screen reader: `aria-label`
7
+ * carries it, and the `Tooltip` supplies the mouse equivalent. The navbar's `Tooltip.Group` sets the
8
+ * delay: late enough that sweeping the rail doesn't flicker a tooltip per row, then instant from row to
9
+ * row. A label that is cut off by its ellipsis gets a `title`.
10
+ *
11
+ * A row with a `disabledReason` is not a link. It stays in the tab order, so the reason is reachable by
12
+ * keyboard: the tooltip opens on focus as well as hover, and a screen reader hears the reason as the
13
+ * row's description.
14
+ *
15
+ * `current` decides whether the row is the open one when the address alone cannot (a section list's
16
+ * open section is a search value, and the first one is open when there is none); without it the router
17
+ * decides from the path. The open row is marked `aria-current="page"`.
18
+ */
19
+ export declare function NavItem({ to, search, current, label, leading, trailing, collapsed, disabledReason, onIntent, }: Readonly<{
20
+ to: string;
21
+ /** The search the link carries; a section list gives the section's. */
22
+ search?: Record<string, unknown>;
23
+ current?: boolean;
24
+ label: string;
25
+ /** An icon, a health mark, or a monogram — whatever leads the row. Section rows have none. */
26
+ leading?: ReactNode;
27
+ trailing?: ReactNode;
28
+ collapsed?: boolean;
29
+ /** Why the row cannot be opened; the row is disabled while this is set. */
30
+ disabledReason?: string;
31
+ /** The pointer or focus reached the row: a chance to load what it opens. */
32
+ onIntent?: () => void;
33
+ }>): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A real button — `aria-expanded`/`aria-controls` so the collapse is announced. While the window is
3
+ * too narrow for the full sidebar (`forced`) it says why it does nothing: it is `aria-disabled`, not
4
+ * `disabled`, so it keeps its focus and its tooltip, and the reason stays reachable by keyboard.
5
+ * `onToggle` is a no-op while forced (`useNavCollapsed`).
6
+ */
7
+ export declare function NavToggle({ collapsed, forced, onToggle, controls, }: Readonly<{
8
+ collapsed: boolean;
9
+ /** The window is narrower than the sidebar's threshold, so the rail is not the viewer's choice. */
10
+ forced?: boolean;
11
+ onToggle: () => void;
12
+ controls: string;
13
+ }>): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The desktop workspace chrome: a fixed header, the collapsible sidebar (the features' way between
3
+ * clusters, then the open cluster's view nav, ADR-0034), and the routed detail column.
4
+ * Desktop-first: no phone or tablet layout (ADR-0164).
5
+ *
6
+ * The sidebar collapses to an icon rail rather than disappearing: `AppShell`'s
7
+ * own `collapsed` prop removes the navbar's width entirely, which is the wrong
8
+ * shape for a rail that stays present with icons, so `navbar.width` switches
9
+ * between two theme tokens (`--as-nav-w`, `--as-nav-rail-w`) instead. The switch
10
+ * is instant: `AppShell` animates neither the navbar's width nor anything but the
11
+ * main column's padding, so a transition only reflowed the whole page for its
12
+ * length, and ⌘B is a keyboard action repeated all day. The rail is also what a
13
+ * window narrower than 64rem gets (`useNavCollapsed`).
14
+ */
15
+ export declare function RootLayout(): import("react").JSX.Element | null;
@@ -0,0 +1,41 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface SectionTab {
3
+ id: string;
4
+ title: string;
5
+ panel: ReactNode;
6
+ /** Shown at the end of the row, such as a count or a marker; its words become part of the link's name. */
7
+ aside?: ReactNode;
8
+ /** Loads what the section needs; called when the pointer or focus reaches its link. */
9
+ preload?: () => void;
10
+ }
11
+ export interface SectionGroup {
12
+ id: string;
13
+ label: string;
14
+ tabs: SectionTab[];
15
+ }
16
+ /**
17
+ * Called by a form inside a section with whether it holds unsaved input. While it does, its row says
18
+ * Unsaved and opening another section asks first, because the section unmounts on a change and the input
19
+ * would be lost. Outside a section list it does nothing.
20
+ */
21
+ export declare function useUnsavedSection(dirty: boolean): void;
22
+ /**
23
+ * A page's sections as a list of links under fixed group headings, the open one beside the list. It is
24
+ * navigation, not an ARIA tab widget: every row is a link to the same page with `?tab=<id>`, the open one
25
+ * is `aria-current="page"`, and the open section is in the address, so it can be shared and survives a
26
+ * reload. Any other value opens the first section. A group with no sections is left out.
27
+ *
28
+ * Choosing a section never moves the page: the link does not reset the scroll, and the list scrolls the
29
+ * page only when the top of the new section is above the window. A keyboard user who activates a link
30
+ * lands in the section, which is named, so they read what they chose rather than staying in the list.
31
+ * The list is its own scroll area when it is taller than the window, its group headings sticky. Leaving a
32
+ * section whose form holds unsaved input ({@link useUnsavedSection}) asks first, by link or by history.
33
+ *
34
+ * @param keep the search values that survive a change of section (a filter that applies to every one);
35
+ * the rest belong to the section that was open and are dropped.
36
+ */
37
+ export declare function SectionNav({ label, groups, keep, }: Readonly<{
38
+ label: string;
39
+ groups: SectionGroup[];
40
+ keep?: string[];
41
+ }>): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { type MeView } from '../auth/api.ts';
2
+ export interface UserMenuProps {
3
+ me: MeView | undefined;
4
+ }
5
+ /**
6
+ * The signed-in user's identity, admin entry point, account page, display choices, and logout
7
+ * (identity-and-sessions spec). The colour scheme (ADR-0159) and the table density (ADR-0162) are
8
+ * chosen here, each as one of its values; the command palette offers both as actions too.
9
+ */
10
+ export declare function UserMenu({ me }: Readonly<UserMenuProps>): import("react").JSX.Element | null;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Where the operator last was inside a cluster, so a page outside any cluster (Administration, the inbox,
3
+ * an account) can lead straight back to it. Browser-local and per viewer, like the palette's recents: a
4
+ * convenience that is fine to lose with site data, never state anything depends on.
5
+ */
6
+ export interface LastPlace {
7
+ clusterId: string;
8
+ clusterName: string;
9
+ /** The view the operator was in, such as "Queues". */
10
+ label: string;
11
+ /** The address, and the search it was opened with. */
12
+ to: string;
13
+ search: Record<string, unknown>;
14
+ }
15
+ export declare function readLastPlace(): LastPlace | null;
16
+ export declare function recordLastPlace(place: LastPlace): void;
17
+ export declare function useLastPlace(): LastPlace | null;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The parts of "where the operator is" that the shell cannot derive from the address itself
3
+ * (ADR-0109): the cluster's name, which only the clusters feature knows, and the open resource,
4
+ * which only its view knows. The shell combines them with the view it matches from the address into
5
+ * the document title and the breadcrumb.
6
+ */
7
+ export interface TitleParts {
8
+ cluster?: string;
9
+ resource?: string;
10
+ }
11
+ export declare function useTitleParts(): TitleParts;
12
+ /** Declares one part of the title while the calling component is mounted. */
13
+ export declare function useTitlePart(part: keyof TitleParts, value: string | null | undefined): void;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The views and resources an operator opened most recently on a cluster, for the palette's Recent
3
+ * group (ADR-0109). Browser-local and per viewer: a convenience that is fine to lose with site data,
4
+ * never state anything depends on.
5
+ */
6
+ export interface Recent {
7
+ label: string;
8
+ /** Where it is: the pathname, and the search it was opened with. */
9
+ to: string;
10
+ search: Record<string, unknown>;
11
+ /** What kind of place it is, shown as the entry's description. */
12
+ kind: string;
13
+ }
14
+ export declare function readRecents(clusterId: string): Recent[];
15
+ /** Records a visit, most recent first, one entry per label. */
16
+ export declare function recordRecent(clusterId: string, recent: Recent): void;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The command palette's colour scheme action and its name (the user menu offers the three schemes as a
3
+ * choice). It cycles following the system, light, dark (ADR-0159). The name says where the action goes,
4
+ * not where it is, and while the system decides it also says what the system chose.
5
+ */
6
+ export declare function useColorSchemeToggle(): {
7
+ scheme: import("@mantine/core").MantineColorScheme;
8
+ toggle: () => void;
9
+ label: string;
10
+ };
@@ -0,0 +1,25 @@
1
+ /** What the header can say about the data on the screen right now (ADR-0052). */
2
+ export interface Freshness {
3
+ /** Newest successful fetch among the queries this screen is observing, in epoch ms. */
4
+ lastUpdatedAt: number | null;
5
+ isFetching: boolean;
6
+ /** At least one query the screen depends on is in error. */
7
+ hasError: boolean;
8
+ /** How many queries the screen is observing. Zero means there is nothing to report. */
9
+ observed: number;
10
+ }
11
+ /**
12
+ * Freshness derived from the query cache rather than wired per screen.
13
+ *
14
+ * The queries with **active observers** are exactly the ones the screen on the
15
+ * display depends on, so no screen can forget to opt in and a screen added later
16
+ * is covered the day it is written. A query nobody is watching has no freshness
17
+ * worth reporting.
18
+ *
19
+ * `lastUpdatedAt` is the *newest* success, not the oldest: the oldest is
20
+ * pessimistic in a misleading way — one slow background query would make a live
21
+ * screen read as stale — and the honesty lives in `hasError` instead, which puts
22
+ * the whole indicator into its offline state rather than letting a fresh sibling
23
+ * paper over a failing one.
24
+ */
25
+ export declare function useFreshness(): Freshness;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Sidebar collapse state, persisted per browser (ADR-0034). A boolean local to
3
+ * one component doesn't need a global store (non-negotiable #9) — `localStorage`
4
+ * is the whole state layer. `getInitialValueInEffect: false` reads synchronously
5
+ * so there is no expand→collapse flash on first paint.
6
+ *
7
+ * `forced` says the rail is the window's doing, not the viewer's: the toggle then changes
8
+ * nothing, and the viewer's own choice is kept for when the window is wide again.
9
+ */
10
+ export declare function useNavCollapsed(): {
11
+ collapsed: boolean;
12
+ forced: boolean;
13
+ toggle: () => void;
14
+ };
@@ -0,0 +1,206 @@
1
+ import type { ComponentType } from 'react';
2
+ import type { ActionProps, ActionSection, AddressTarget, ClientTarget, ConnectionTarget, ConsumerTarget, DivertTarget, LinkProps, MessageTarget, ProducerTarget, QueueTarget, SessionTarget } from './actions/types.ts';
3
+ import type { HeldOperationDetail } from './approvals/api.ts';
4
+ import type { MetricRange } from './time/ranges.ts';
5
+ /** Queues picked by name, or every queue matching the queues screen's filter (`q`, blank for all). */
6
+ export type QueueSelection = {
7
+ kind: 'names';
8
+ names: string[];
9
+ } | {
10
+ kind: 'filter';
11
+ q: string;
12
+ total: number;
13
+ };
14
+ /** Messages picked by id, every message matching the messages screen's selector, or the whole queue. */
15
+ export type MessageSelection = {
16
+ kind: 'ids';
17
+ ids: number[];
18
+ } | {
19
+ kind: 'filter';
20
+ filter: string;
21
+ } | {
22
+ kind: 'all';
23
+ };
24
+ /**
25
+ * The kernel-owned slots and what each hands its contributions (ADR-0070). A slot lets a screen
26
+ * show another feature's panel without importing that feature, and without a placeholder when the
27
+ * feature is disabled.
28
+ */
29
+ export interface SlotProps {
30
+ /** In the application header, after the product name: status across clusters that needs attention. */
31
+ 'shell.header': object;
32
+ /** In the sidebar, above the open cluster's views: how the operator moves between clusters. */
33
+ 'shell.navbar': {
34
+ collapsed: boolean;
35
+ };
36
+ /** The landing page, when no cluster is open. */
37
+ 'home.empty': object;
38
+ /** Above every view of a cluster: what identifies it, and what needs saying about its state. */
39
+ 'cluster.header': {
40
+ clusterId: string;
41
+ };
42
+ /** Below a passing registration check: what the enabled features added to it, by feature id. */
43
+ 'cluster.registration.afterProbe': {
44
+ contributions: Record<string, unknown>;
45
+ };
46
+ /** In a queue's detail drawer, below its per-node breakdown. `onClose` closes the drawer before navigating. */
47
+ 'queue.detail.panels': {
48
+ clusterId: string;
49
+ queueName: string;
50
+ onClose: () => void;
51
+ };
52
+ /**
53
+ * Beside the queues screen's selection: what can be done to the selected queues. `count` is how
54
+ * many are selected, and may be zero: an action then stays visible and disabled. `clear` empties
55
+ * the selection.
56
+ */
57
+ 'queues.selection': {
58
+ clusterId: string;
59
+ selection: QueueSelection;
60
+ count: number;
61
+ clear: () => void;
62
+ };
63
+ /**
64
+ * Beside the messages screen's selection: what can be done to the selected messages elsewhere.
65
+ * `node` is the Rahdar node being browsed, absent for the live node; `total` is how many messages
66
+ * the selection holds, null when the screen does not know. `clear` empties the selection.
67
+ */
68
+ 'messages.selection': {
69
+ clusterId: string;
70
+ queueName: string;
71
+ node?: string;
72
+ selection: MessageSelection;
73
+ total: number | null;
74
+ clear: () => void;
75
+ };
76
+ /** At the foot of a cluster's metrics view. */
77
+ 'metrics.panels': {
78
+ clusterId: string;
79
+ };
80
+ /** In the flow view's monitoring pane, when a queue is selected: its history over `range`, per node. */
81
+ 'flow.selection.panels': {
82
+ clusterId: string;
83
+ queueName: string;
84
+ range: MetricRange;
85
+ };
86
+ /** Inside a box on the topology graph, after its name. `nodeIds` are the broker endpoints the box stands for. */
87
+ 'topology.node.marks': {
88
+ clusterId: string;
89
+ nodeIds: string[];
90
+ };
91
+ /** A section of a cluster's Settings page, under the contribution's title. */
92
+ 'settings.sections': {
93
+ clusterId: string;
94
+ };
95
+ /** A tab of a cluster's Routing page, after Diverts and Bridges, labelled with the contribution's title; its id is the tab's `?tab=`. */
96
+ 'routing.tabs': {
97
+ clusterId: string;
98
+ };
99
+ /**
100
+ * A tab of the Administration page, labelled with the contribution's title and listed under its
101
+ * `group`; its id is the tab's `?tab=`.
102
+ */
103
+ 'admin.tabs': object;
104
+ /** A section of the signed-in user's Account page, under the contribution's title. */
105
+ 'account.sections': object;
106
+ /**
107
+ * On an approval request's page, above Rahdar's own Approve and Reject: what an installed approval provider
108
+ * shows an approver, such as its policy's checks or its own decision controls. `heldOperation` is the request
109
+ * as the page shows it; `refresh` fetches it again after the contribution changed it.
110
+ */
111
+ 'approval.decision': {
112
+ heldOperation: HeldOperationDetail;
113
+ refresh: () => void;
114
+ };
115
+ /**
116
+ * A dialog opened from the user menu. The menu lists the contribution's title after Account and hosts
117
+ * the dialog outside the menu, so it outlives the menu closing.
118
+ */
119
+ 'shell.userMenu': {
120
+ opened: boolean;
121
+ onClose: () => void;
122
+ };
123
+ 'queue.actions': ActionProps<QueueTarget>;
124
+ 'address.actions': ActionProps<AddressTarget>;
125
+ 'connection.actions': ActionProps<ConnectionTarget>;
126
+ 'session.actions': ActionProps<SessionTarget>;
127
+ 'consumer.actions': ActionProps<ConsumerTarget>;
128
+ 'producer.actions': ActionProps<ProducerTarget>;
129
+ 'message.actions': ActionProps<MessageTarget>;
130
+ 'divert.actions': ActionProps<DivertTarget>;
131
+ 'client.actions': ActionProps<ClientTarget>;
132
+ 'queue.link': LinkProps<QueueTarget>;
133
+ 'address.link': LinkProps<AddressTarget>;
134
+ 'connection.link': LinkProps<ConnectionTarget>;
135
+ 'session.link': LinkProps<SessionTarget>;
136
+ }
137
+ export type SlotName = keyof SlotProps;
138
+ /**
139
+ * The closed, ordered headings a Settings page groups its tabs under, in the order an operator's
140
+ * reach widens: their own preferences, then what is shared across Rahdar, then this cluster, then
141
+ * what plugins added. Adding one is a kernel change, like a navigation group (ADR-0070).
142
+ */
143
+ export declare const SETTINGS_GROUPS: readonly [{
144
+ readonly id: "personal";
145
+ readonly label: "Yours";
146
+ }, {
147
+ readonly id: "rahdar";
148
+ readonly label: "Rahdar";
149
+ }, {
150
+ readonly id: "cluster";
151
+ readonly label: "This cluster";
152
+ }, {
153
+ readonly id: "plugins";
154
+ readonly label: "Plugins";
155
+ }];
156
+ export type SettingsGroupId = (typeof SETTINGS_GROUPS)[number]['id'];
157
+ /**
158
+ * The closed, ordered headings the Administration page groups its tabs under: who may do what, what
159
+ * is installed and where, what rules data and changes follow, then what helps when something is
160
+ * wrong. Every `admin.tabs` contribution names one. Adding one is a kernel change, like a navigation
161
+ * group (ADR-0070).
162
+ */
163
+ export declare const ADMIN_GROUPS: readonly [{
164
+ readonly id: "access";
165
+ readonly label: "Access";
166
+ }, {
167
+ readonly id: "installation";
168
+ readonly label: "Installation";
169
+ }, {
170
+ readonly id: "governance";
171
+ readonly label: "Governance";
172
+ }, {
173
+ readonly id: "support";
174
+ readonly label: "Support";
175
+ }];
176
+ export type AdminGroupId = (typeof ADMIN_GROUPS)[number]['id'];
177
+ export interface SlotContribution<P> {
178
+ /** Unique within the slot. */
179
+ id: string;
180
+ /** Position within the slot; lower comes first. */
181
+ order: number;
182
+ /** The heading or tab label, in the slots that show one. */
183
+ title?: string;
184
+ /**
185
+ * The heading its tab sits under, in the grouped slots: a `SettingsGroupId` for `settings.sections`
186
+ * (listed under Plugins without one), and an `AdminGroupId`, required, for `admin.tabs`.
187
+ */
188
+ group?: string;
189
+ /** `*.actions` only: the menu section the item is listed under. */
190
+ section?: ActionSection;
191
+ Component: ComponentType<P>;
192
+ }
193
+ /** What a contribution to a grouped slot names as its heading. */
194
+ type SlotGroup<K extends SlotName> = K extends 'admin.tabs' ? {
195
+ group: AdminGroupId;
196
+ } : K extends 'settings.sections' ? {
197
+ group?: SettingsGroupId;
198
+ } : object;
199
+ /** One contribution to the named slot, with the heading that slot groups it under. */
200
+ export type SlotEntry<K extends SlotName> = SlotContribution<SlotProps[K]> & SlotGroup<K>;
201
+ export type SlotContributions = {
202
+ [K in SlotName]?: SlotEntry<K>[];
203
+ };
204
+ /** The enabled features' contributions to one slot, in order. */
205
+ export declare function useSlot<K extends SlotName>(name: K): SlotEntry<K>[];
206
+ export {};
@@ -0,0 +1,22 @@
1
+ /** What both of the console's event streams share: the server's control frames and how to reconnect (ADR-0052). */
2
+ /** The server's keep-alive (`SseHub.PING`). Not a topic — every subscriber gets it. */
3
+ export declare const PING = "ping";
4
+ /** The server is about to close this stream (`SseHub.RECONNECT`): reconnect at once, without backoff. */
5
+ export declare const RECONNECT = "reconnect";
6
+ /**
7
+ * The server closed this stream for a newer one of the same user past their limit (`SseHub.EVICTED`): wait
8
+ * until the tab is in use again before reconnecting, or the user's tabs would keep closing each other.
9
+ */
10
+ export declare const EVICTED = "evicted";
11
+ /** Frames were lost (`SseHub.RESYNC`): the bus came back, or a replay was capped. Refetch the views. */
12
+ export declare const RESYNC = "resync";
13
+ /**
14
+ * A connection with no frame for this long is treated as dead. Comfortably above
15
+ * the server's keep-alive interval so one missed beat is not read as death; if
16
+ * `sse.heartbeat-interval` is raised past a third of this, raise this with it.
17
+ */
18
+ export declare const SILENCE_MS = 45000;
19
+ /** Failures past this read as "the server is gone", not "the connection blipped". */
20
+ export declare const OFFLINE_AFTER = 3;
21
+ /** Capped exponential backoff with full jitter, so a restart is not stampeded. */
22
+ export declare function backoff(failures: number): number;
@@ -0,0 +1,27 @@
1
+ /** What the UI reports about the live connection (ADR-0052). */
2
+ export type StreamStatus = 'connecting' | 'live' | 'reconnecting' | 'offline';
3
+ /** The live-stream state, or `null` on a route that mounts no stream. */
4
+ export declare function useStreamStatus(): StreamStatus | null;
5
+ /**
6
+ * One `EventSource` per mounted cluster view (ADR-0003, ADR-0018, ADR-0027).
7
+ *
8
+ * - Each topic's frames go to the handler its feature contributes (ADR-0070). A
9
+ * signal topic's handler invalidates the matching TanStack Query keys and the
10
+ * normal `queryFn` refetches; a topic of a disabled feature has no handler.
11
+ * - `onFrame` also hands the view every frame of the topics it mounted, for a topic
12
+ * that carries data with no server-side resource behind it, such as the live
13
+ * broker-event feed. A new `EventSource` never sends `Last-Event-ID` itself, so the
14
+ * hook remembers the last frame id and presents it as `lastEventId` on every connect,
15
+ * on whichever replica it lands; the server replays what was missed.
16
+ * - `reconnect` (the server is draining) reopens at once with the backoff reset and is
17
+ * not a failure. `resync`, or the first successful connect after a failure, refetches
18
+ * every query of the cluster: change signals sent while the client was away are not
19
+ * replayed.
20
+ *
21
+ * It reconnects indefinitely with capped exponential backoff and full jitter, and
22
+ * treats silence as failure (ADR-0052): an intermediary that drops the connection
23
+ * without a clean close fires no `error`, so the only way to notice is to miss a
24
+ * keep-alive. The returned status is what the freshness indicator reports; the
25
+ * per-hook `refetchInterval` keeps every view updating while it is not `live`.
26
+ */
27
+ export declare function useClusterStream(clusterId: string, topics: string[], onFrame?: (topic: string, data: string) => void): StreamStatus;
@@ -0,0 +1,11 @@
1
+ import type { StreamStatus } from './useClusterStream.ts';
2
+ /**
3
+ * The signed-in user's own event stream (`GET /api/v1/me/stream`): their inbox and their held operations,
4
+ * on every page rather than only a cluster's. Each signal invalidates the queries it covers, and a
5
+ * `resync`, or a reconnect after a failure, refetches all of them. It reconnects indefinitely with capped,
6
+ * jittered backoff and treats silence as failure, as the cluster stream does. A stream the server closed for
7
+ * a newer tab (`evicted`) waits until this tab is in use again.
8
+ *
9
+ * Returns the connection's state, so a view can poll while it is not `live`.
10
+ */
11
+ export declare function useUserStream(): StreamStatus;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A moment in words (or, with `future`, how long from now) inside a `<time>`, with the exact time as text
3
+ * a screen reader reads and a hover shows. For running text, where {@link When} would take a block of its own.
4
+ */
5
+ export declare function Ago({ at, now, future }: Readonly<{
6
+ at: string;
7
+ now: number;
8
+ future?: boolean;
9
+ }>): import("react").JSX.Element;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Preset relative metric windows, shared by every view that charts history (metrics, and the
3
+ * Flow pane that shows its panels). Kept out of `router.tsx` so importing it (a
4
+ * component like `RangePicker` needs the runtime array, not just the type)
5
+ * never has to evaluate the whole route tree — that module calls
6
+ * `createRootRoute`/`createRoute` at import time, which only a real router
7
+ * context (or a full mock of it) can satisfy.
8
+ */
9
+ export declare const METRIC_RANGES: readonly ["15m", "1h", "6h", "24h", "7d"];
10
+ export type MetricRange = (typeof METRIC_RANGES)[number];
11
+ /**
12
+ * Each range's width and the bucket it asks the server for, in one place.
13
+ *
14
+ * The two travel together on purpose: the window advances in whole buckets
15
+ * (ADR-0055), the axis chooses its tick format from the range, and the poll
16
+ * cadence is derived from both. Splitting them across three modules is how they
17
+ * drift.
18
+ *
19
+ * `step` is a hint. The server clamps it to the fastest sampling tier and widens
20
+ * it to keep a response under its point cap, and reports what it actually used;
21
+ * nothing here assumes the hint was honoured.
22
+ */
23
+ export interface RangeSpec {
24
+ /** Window width. */
25
+ windowMs: number;
26
+ /** Requested bucket width, as the ISO-8601 duration the endpoint takes. */
27
+ step: string;
28
+ /** The same bucket width in ms — what the window quantizes to. */
29
+ stepMs: number;
30
+ }
31
+ export declare const RANGE_SPEC: Record<MetricRange, RangeSpec>;
32
+ export declare function rangeSpec(range: MetricRange): RangeSpec;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Ask the server what time it is and fold the answer in.
3
+ *
4
+ * Failures are silent on purpose: the probe is not a screen and has no operator
5
+ * waiting on it. A 401 or a dropped network leaves the previous estimate standing,
6
+ * which is strictly better than reverting to the browser's clock, and the real
7
+ * queries on the screen are what report that the server is unreachable.
8
+ *
9
+ * Concurrent callers join the request in flight rather than starting another.
10
+ */
11
+ export declare function syncServerTime(): Promise<void>;
12
+ /**
13
+ * The SSE keep-alive as a drift detector — never as a measurement.
14
+ *
15
+ * `SseHub.heartbeat` puts the server's clock in every `ping`, so the client is
16
+ * told the time every twenty seconds for free. It is one-way with unmeasurable
17
+ * latency, so it cannot teach the estimator; what it can do is notice that the
18
+ * estimate has gone wrong and ask for a real probe.
19
+ */
20
+ export declare function offerPing(data: unknown): void;
21
+ /** Rahdar's clock, as best the browser can tell. The only wall-clock read in the app. */
22
+ export declare function serverNow(): number;
23
+ /**
24
+ * Put a browser-stamped instant on Rahdar's timeline.
25
+ *
26
+ * The only such instants are TanStack Query's `dataUpdatedAt`, which it stamps
27
+ * with its own `Date.now()` when a response lands. Comparing one against
28
+ * {@link serverNow} without this would reintroduce exactly the error this module
29
+ * removes — and silently, because the number looks right.
30
+ */
31
+ export declare function toServerMs(browserMs: number): number;
32
+ /** How far the browser's clock is from Rahdar's, in ms. Positive means the browser is ahead. */
33
+ export declare function browserOffsetMs(): number;
34
+ /**
35
+ * Keep the estimate honest for as long as the tab lives.
36
+ *
37
+ * Three triggers, because clocks go wrong in three ways: they drift (the
38
+ * interval), they are corrected or the machine wakes from sleep (the step check
39
+ * and `visibilitychange`), and the tab is backgrounded long enough that neither
40
+ * timer ran (`visibilitychange` again — browsers throttle background timers).
41
+ *
42
+ * The step check is the client-side `MonotonicClockWatch`: `performance.now()`
43
+ * advances monotonically, so comparing its delta against the wall clock's
44
+ * separates a jump from ordinary drift. A step means the offset is now wrong by
45
+ * the size of the jump, and unlearning that slowly through the EWMA would leave
46
+ * every label wrong in the meantime.
47
+ *
48
+ * Called once from the application root. Returns a teardown for tests.
49
+ */
50
+ export declare function startServerTimeSync(): () => void;
51
+ /**
52
+ * A clock that ticks to re-render a relative label, on Rahdar's time.
53
+ *
54
+ * Also re-renders when the offset itself changes, which matters more than it
55
+ * looks: `MetricsView` ticks at the metric bucket width, up to an hour, and would
56
+ * otherwise hold a window computed before the first probe resolved for that long.
57
+ */
58
+ export declare function useServerNow(intervalMs?: number): number;
59
+ /**
60
+ * `4s`, `3m`, `2h`, `1d` — short enough to sit in a header or a table cell.
61
+ *
62
+ * Floors rather than rounds. An age is a lower bound on how long ago something
63
+ * happened, and `59s` reading as `1m` overstates it; the operator reading a
64
+ * staleness label before a destructive action is the reason to prefer the
65
+ * conservative direction.
66
+ */
67
+ export declare function elapsedLabel(ms: number): string;
68
+ /**
69
+ * A server instant, written down in the operator's chosen timezone.
70
+ *
71
+ * Fixed-width `YYYY-MM-DD HH:mm:ss` so a column of these sorts and scans by eye,
72
+ * and **always suffixed with the zone it is in** — `Z` for UTC, `+03:30`
73
+ * otherwise. The suffix is not decoration: the whole reason an operator picks a
74
+ * zone is to line a screen up against something else, and a timestamp that does
75
+ * not say which offset it is in is the one way this feature could mislead them.
76
+ *
77
+ * The offset is computed for *that instant*, not for now, so an event from last
78
+ * winter carries the offset that was in force when it happened.
79
+ *
80
+ * Accepts both wire shapes: the ISO-8601 strings every DTO carries, and the
81
+ * epoch-millis a broker's own message timestamps arrive as. Those are `0` when the
82
+ * broker set none, which is not a time and is rendered as such.
83
+ *
84
+ * Reads the zone from module state rather than a hook so it can be called from a
85
+ * table's column accessor. The consequence is that a view rendering one of these
86
+ * must subscribe with `useDisplayZone()` or it will not repaint when the zone
87
+ * changes.
88
+ */
89
+ export declare function absoluteLabel(value: string | number | null | undefined): string;