@rahdar/plugin-sdk 0.0.0 → 2026.10.51
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 +38 -0
- package/index.js +52 -0
- package/package.json +46 -1
- package/peers.json +8 -0
- package/shared.d.ts +3 -0
- package/shared.js +24 -0
- package/types/branding.d.ts +14 -0
- package/types/kernel/actions/ActionHost.d.ts +16 -0
- package/types/kernel/actions/hostContext.d.ts +4 -0
- package/types/kernel/actions/types.d.ts +130 -0
- package/types/kernel/api/paging.d.ts +39 -0
- package/types/kernel/api/polling.d.ts +37 -0
- package/types/kernel/api/request.d.ts +84 -0
- package/types/kernel/api/schema.d.ts +24077 -0
- package/types/kernel/approvals/ApprovalView.d.ts +6 -0
- package/types/kernel/approvals/ApprovalsView.d.ts +7 -0
- package/types/kernel/approvals/ApproverQuorumNotice.d.ts +8 -0
- package/types/kernel/approvals/BreakGlassBanner.d.ts +5 -0
- package/types/kernel/approvals/CancelRequest.d.ts +13 -0
- package/types/kernel/approvals/DecisionPanel.d.ts +11 -0
- package/types/kernel/approvals/ExpiresIn.d.ts +10 -0
- package/types/kernel/approvals/MyRequests.d.ts +5 -0
- package/types/kernel/approvals/Recap.d.ts +9 -0
- package/types/kernel/approvals/api.d.ts +130 -0
- package/types/kernel/approvals/columns.d.ts +22 -0
- package/types/kernel/approvals/words.d.ts +31 -0
- package/types/kernel/auth/Can.d.ts +9 -0
- package/types/kernel/auth/LoginView.d.ts +9 -0
- package/types/kernel/auth/SecondFactorForm.d.ts +25 -0
- package/types/kernel/auth/StepUp.d.ts +17 -0
- package/types/kernel/auth/api.d.ts +115 -0
- package/types/kernel/auth/freshSignIn.d.ts +2 -0
- package/types/kernel/auth/useCan.d.ts +49 -0
- package/types/kernel/auth/webauthn.d.ts +19 -0
- package/types/kernel/feature.d.ts +90 -0
- package/types/kernel/features.d.ts +5 -0
- package/types/kernel/inbox/InboxBell.d.ts +6 -0
- package/types/kernel/inbox/InboxView.d.ts +6 -0
- package/types/kernel/inbox/NoticeRow.d.ts +17 -0
- package/types/kernel/inbox/api.d.ts +53 -0
- package/types/kernel/keyboard/ShortcutsHelp.d.ts +6 -0
- package/types/kernel/keyboard/filterShortcut.d.ts +5 -0
- package/types/kernel/keyboard/keys.d.ts +14 -0
- package/types/kernel/keyboard/shortcuts.d.ts +5 -0
- package/types/kernel/keyboard/useKeySequences.d.ts +16 -0
- package/types/kernel/manifest.d.ts +21 -0
- package/types/kernel/metrics/ChartPanel.d.ts +28 -0
- package/types/kernel/metrics/MetricChart.d.ts +13 -0
- package/types/kernel/metrics/axis.d.ts +71 -0
- package/types/kernel/metrics/pluginSeries.d.ts +17 -0
- package/types/kernel/nav/currentView.d.ts +21 -0
- package/types/kernel/nav/groups.d.ts +22 -0
- package/types/kernel/plugins/PluginBoundary.d.ts +25 -0
- package/types/kernel/plugins/PluginUnavailable.d.ts +6 -0
- package/types/kernel/plugins/boot.d.ts +33 -0
- package/types/kernel/plugins/guarded.d.ts +3 -0
- package/types/kernel/plugins/usePluginsChanged.d.ts +6 -0
- package/types/kernel/plugins/validate.d.ts +19 -0
- package/types/kernel/registry.d.ts +12 -0
- package/types/kernel/routing/roots.d.ts +24 -0
- package/types/kernel/shell/AccountView.d.ts +11 -0
- package/types/kernel/shell/AdminView.d.ts +7 -0
- package/types/kernel/shell/Breadcrumb.d.ts +6 -0
- package/types/kernel/shell/ClusterLayout.d.ts +14 -0
- package/types/kernel/shell/ClusterViewNav.d.ts +13 -0
- package/types/kernel/shell/CommandPalette.d.ts +15 -0
- package/types/kernel/shell/FeatureDisabled.d.ts +13 -0
- package/types/kernel/shell/FeatureGate.d.ts +12 -0
- package/types/kernel/shell/FreshnessBar.d.ts +11 -0
- package/types/kernel/shell/GlobalContext.d.ts +7 -0
- package/types/kernel/shell/HomeView.d.ts +6 -0
- package/types/kernel/shell/NavGroup.d.ts +13 -0
- package/types/kernel/shell/NavItem.d.ts +33 -0
- package/types/kernel/shell/NavToggle.d.ts +13 -0
- package/types/kernel/shell/RootLayout.d.ts +15 -0
- package/types/kernel/shell/SectionNav.d.ts +41 -0
- package/types/kernel/shell/UserMenu.d.ts +10 -0
- package/types/kernel/shell/lastPlace.d.ts +17 -0
- package/types/kernel/shell/pageTitle.d.ts +13 -0
- package/types/kernel/shell/recents.d.ts +16 -0
- package/types/kernel/shell/useColorSchemeToggle.d.ts +10 -0
- package/types/kernel/shell/useFreshness.d.ts +25 -0
- package/types/kernel/shell/useNavCollapsed.d.ts +14 -0
- package/types/kernel/slots.d.ts +206 -0
- package/types/kernel/stream/sse.d.ts +22 -0
- package/types/kernel/stream/useClusterStream.d.ts +27 -0
- package/types/kernel/stream/useUserStream.d.ts +11 -0
- package/types/kernel/time/Ago.d.ts +9 -0
- package/types/kernel/time/ranges.d.ts +32 -0
- package/types/kernel/time/time.d.ts +89 -0
- package/types/kernel/time/timezone.d.ts +44 -0
- package/types/kernel/useDismissedNotice.d.ts +3 -0
- package/types/sdk/index.d.ts +92 -0
- package/types/ui/ActionMenuItem.d.ts +27 -0
- package/types/ui/AnchoredMenu.d.ts +20 -0
- package/types/ui/CapabilityGate.d.ts +23 -0
- package/types/ui/CapabilityReason.d.ts +11 -0
- package/types/ui/CodeEditor.d.ts +31 -0
- package/types/ui/ConfirmDialog.d.ts +51 -0
- package/types/ui/DescriptionList.d.ts +26 -0
- package/types/ui/DiagramView.d.ts +35 -0
- package/types/ui/DialogActions.d.ts +26 -0
- package/types/ui/EmptyState.d.ts +42 -0
- package/types/ui/ErrorState.d.ts +36 -0
- package/types/ui/FieldRow.d.ts +9 -0
- package/types/ui/HeldRequestLink.d.ts +4 -0
- package/types/ui/HoldToConfirm.d.ts +22 -0
- package/types/ui/LoadingState.d.ts +16 -0
- package/types/ui/NodeOutcomeSummary.d.ts +59 -0
- package/types/ui/Notice.d.ts +18 -0
- package/types/ui/Page.d.ts +13 -0
- package/types/ui/PageHeader.d.ts +18 -0
- package/types/ui/Pager.d.ts +21 -0
- package/types/ui/Section.d.ts +22 -0
- package/types/ui/Stat.d.ts +20 -0
- package/types/ui/StatusBadge.d.ts +11 -0
- package/types/ui/Toolbar.d.ts +19 -0
- package/types/ui/capabilityGate.d.ts +43 -0
- package/types/ui/codeDiagnostics.d.ts +11 -0
- package/types/ui/codeMirrorTheme.d.ts +11 -0
- package/types/ui/diagram.d.ts +49 -0
- package/types/ui/errorReading.d.ts +30 -0
- package/types/ui/formErrors.d.ts +10 -0
- package/types/ui/graph/ViewControls.d.ts +19 -0
- package/types/ui/graph/elk.d.ts +3 -0
- package/types/ui/held.d.ts +12 -0
- package/types/ui/inAppNavigation.d.ts +5 -0
- package/types/ui/nodeOutcome.d.ts +13 -0
- package/types/ui/notify.d.ts +77 -0
- package/types/ui/safeHref.d.ts +8 -0
- package/types/ui/table/ColumnsMenu.d.ts +26 -0
- package/types/ui/table/DataTable.d.ts +109 -0
- package/types/ui/table/GridFind.d.ts +16 -0
- package/types/ui/table/GridTable.d.ts +49 -0
- package/types/ui/table/HeaderCell.d.ts +25 -0
- package/types/ui/table/MiddleTruncate.d.ts +21 -0
- package/types/ui/table/StaticTable.d.ts +14 -0
- package/types/ui/table/TableStates.d.ts +35 -0
- package/types/ui/table/columns.d.ts +49 -0
- package/types/ui/table/density.d.ts +13 -0
- package/types/ui/table/gridFind.d.ts +7 -0
- package/types/ui/table/index.d.ts +5 -0
- package/types/ui/table/layout.d.ts +48 -0
- package/types/ui/table/measure.d.ts +16 -0
- package/types/ui/table/measurement.d.ts +92 -0
- package/types/ui/table/menuAnchor.d.ts +12 -0
- package/types/ui/table/rovingGrid.d.ts +37 -0
- package/types/ui/table/sort.d.ts +14 -0
- package/types/ui/table/tableState.d.ts +51 -0
- package/types/ui/table/useGridFind.d.ts +18 -0
- package/types/ui/trustedTypes.d.ts +16 -0
- package/types/ui/useTruncated.d.ts +8 -0
- package/vite.d.ts +14 -0
- package/vite.js +62 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One approval request: what it will do, who asked and why, what happened to it, and — for an approver while it
|
|
3
|
+
* waits — the decision. Generic: every gated operation, Rahdar's or a plugin's, reads the same way, and an
|
|
4
|
+
* installed approval provider adds its own controls through the `approval.decision` slot.
|
|
5
|
+
*/
|
|
6
|
+
export declare function ApprovalView(): import("react").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** The open tab, as the URL names it. */
|
|
2
|
+
export type ApprovalsTab = 'waiting' | 'mine';
|
|
3
|
+
/**
|
|
4
|
+
* Approval requests: those waiting for the signed-in user's decision, and the user's own with what became of
|
|
5
|
+
* them. The open tab is in the URL (`?tab=mine`), so the view can be shared and restored.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ApprovalsView(): import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shown while the approval gate holds requests but fewer than two people may approve them. A request needs a
|
|
3
|
+
* person other than its requester, so with one approver nothing can be approved, not even the removal of the
|
|
4
|
+
* policy that holds it. Rahdar refuses an access change that would take the approvers below two; this says so
|
|
5
|
+
* when it is already the case (a directory sync, a deleted account), and what to do. Nothing while approvals are
|
|
6
|
+
* off, not yet holding anything, or well staffed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ApproverQuorumNotice(): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Above every page while the deployment's break-glass setting is on: approval checks are bypassed, so every
|
|
3
|
+
* operator must know. It cannot be dismissed; it goes when the setting does.
|
|
4
|
+
*/
|
|
5
|
+
export declare function BreakGlassBanner(): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type HeldOperationDetail } from './api.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The requester's "Cancel request", confirmed in a dialog that names the request and says what cancelling does.
|
|
4
|
+
* With the request's `detail` the dialog repeats what it would have done; a list passes only its summary.
|
|
5
|
+
* `onCancelled` refreshes what the caller shows beside the request, such as a pending value.
|
|
6
|
+
*/
|
|
7
|
+
export declare function CancelRequest({ id, summary, detail, size, onCancelled, }: Readonly<{
|
|
8
|
+
id: string;
|
|
9
|
+
summary: string;
|
|
10
|
+
detail?: HeldOperationDetail;
|
|
11
|
+
size?: 'xs' | 'sm';
|
|
12
|
+
onCancelled?: () => void;
|
|
13
|
+
}>): import("react").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type HeldOperationDetail } from './api.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Rahdar's own decision controls, for anyone the server says may decide: a reason, then Approve or Reject, each
|
|
4
|
+
* confirmed in a dialog that says once more what is being approved. The vote is bound to the parameters and
|
|
5
|
+
* version shown, so a request that changed meanwhile is refused and shown again rather than decided blind. A
|
|
6
|
+
* stale sign-in is confirmed in place, and the vote is sent again once it is.
|
|
7
|
+
*/
|
|
8
|
+
export declare function DecisionPanel({ detail, refresh }: Readonly<{
|
|
9
|
+
detail: HeldOperationDetail;
|
|
10
|
+
refresh: () => void;
|
|
11
|
+
}>): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A live countdown to when a request lapses, with the exact time beside it. The figure ticks every second, but
|
|
3
|
+
* it is not a live region, so a screen reader reads it only when it reaches it; what is announced is one polite
|
|
4
|
+
* line at the marks that matter (an hour, 15, 5 and 1 minutes left, and expiry), never a tick. `onExpired` runs
|
|
5
|
+
* once when the time is up, so the page can ask what became of the request.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ExpiresIn({ at, onExpired }: Readonly<{
|
|
8
|
+
at: string;
|
|
9
|
+
onExpired?: () => void;
|
|
10
|
+
}>): import("react").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { HeldOperationDetail } from './api.ts';
|
|
2
|
+
/**
|
|
3
|
+
* What is being decided, said once more where the decision is confirmed or a request cancelled: the summary,
|
|
4
|
+
* the estimated effect, the first changes and the reason, so the operator confirms what they just read.
|
|
5
|
+
*/
|
|
6
|
+
export declare function Recap({ detail, reason }: Readonly<{
|
|
7
|
+
detail: HeldOperationDetail;
|
|
8
|
+
reason?: string | null;
|
|
9
|
+
}>): import("react").JSX.Element;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { ApiError } from '../api/request.ts';
|
|
2
|
+
import type { components } from '../api/schema.d.ts';
|
|
3
|
+
type Schemas = components['schemas'];
|
|
4
|
+
export type HeldOperationSummary = Schemas['HeldOperationSummaryView'];
|
|
5
|
+
export type HeldOperationDetail = Schemas['HeldOperationDetailView'];
|
|
6
|
+
export type HeldOperationPage = Schemas['HeldOperationPageView'];
|
|
7
|
+
export type HeldDisplayRow = Schemas['HeldDisplayRowView'];
|
|
8
|
+
export type HeldEvent = Schemas['HeldEventView'];
|
|
9
|
+
export type HeldDecision = Schemas['HeldDecisionRequest'];
|
|
10
|
+
export type HeldState = HeldOperationSummary['state'];
|
|
11
|
+
export type GateStatus = Schemas['GateStatusView'];
|
|
12
|
+
/** Whose requests a list shows: the caller's own, or those waiting for the caller's decision. */
|
|
13
|
+
export type HeldScope = 'MINE' | 'DECIDABLE';
|
|
14
|
+
/** Every key starts from {@link heldOperationsKey}, so the user stream's `held` signal refreshes all of them. */
|
|
15
|
+
export declare const heldKeys: {
|
|
16
|
+
all: readonly ["held-operations"];
|
|
17
|
+
list: (scope: HeldScope, limit: number) => readonly ["held-operations", "list", HeldScope, number];
|
|
18
|
+
detail: (id: string) => readonly ["held-operations", "detail", string];
|
|
19
|
+
gate: readonly ["gate", "status"];
|
|
20
|
+
};
|
|
21
|
+
/** The value the server writes in place of a secret parameter. */
|
|
22
|
+
export declare const REDACTED = "[redacted]";
|
|
23
|
+
/** Whether a request has ended: nothing more happens to it, and nobody can act on it. */
|
|
24
|
+
export declare const isClosed: (state: HeldState) => state is "REJECTED" | "CANCELLED" | "EXPIRED" | "SUCCEEDED" | "FAILED" | "REFUSED" | "OUTCOME_UNKNOWN";
|
|
25
|
+
/** The caller's own requests, or the ones waiting for them, newest first, a page at a time by keyset. */
|
|
26
|
+
export declare function useHeldList(scope: HeldScope, limit?: number): import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/query-core").InfiniteData<{
|
|
27
|
+
items: components["schemas"]["HeldOperationSummaryView"][];
|
|
28
|
+
next?: string | null;
|
|
29
|
+
}, unknown>, Error>;
|
|
30
|
+
/** One request as its page shows it. A request the caller may not see answers 404, like one that does not exist. */
|
|
31
|
+
export declare function useHeldOperation(id: string): import("@tanstack/react-query").UseQueryResult<{
|
|
32
|
+
operation: components["schemas"]["HeldOperationSummaryView"];
|
|
33
|
+
typeVersion: number;
|
|
34
|
+
mode: "ON_APPROVAL" | "BY_REQUESTER";
|
|
35
|
+
traits: string[];
|
|
36
|
+
environmentId?: string | null;
|
|
37
|
+
display: components["schemas"]["HeldDisplayRowView"][];
|
|
38
|
+
params: string;
|
|
39
|
+
paramsHash: string;
|
|
40
|
+
version: number;
|
|
41
|
+
tokenName?: string | null;
|
|
42
|
+
effect: components["schemas"]["HeldEffectView"];
|
|
43
|
+
policy: components["schemas"]["HeldPolicyView"];
|
|
44
|
+
approverHint?: string | null;
|
|
45
|
+
approverId?: string | null;
|
|
46
|
+
decisionReason?: string | null;
|
|
47
|
+
outcomeDetail?: string | null;
|
|
48
|
+
link: string;
|
|
49
|
+
events: components["schemas"]["HeldEventView"][];
|
|
50
|
+
mine: boolean;
|
|
51
|
+
canDecide: boolean;
|
|
52
|
+
decideRefusal?: string | null;
|
|
53
|
+
canCancel: boolean;
|
|
54
|
+
mfaVerified: boolean;
|
|
55
|
+
requesterLacksPermission?: boolean | null;
|
|
56
|
+
}, ApiError>;
|
|
57
|
+
/** Approves or rejects a request, bound to the parameters and version the decider saw. */
|
|
58
|
+
export declare function useDecide(id: string): import("@tanstack/react-query").UseMutationResult<{
|
|
59
|
+
operation: components["schemas"]["HeldOperationSummaryView"];
|
|
60
|
+
typeVersion: number;
|
|
61
|
+
mode: "ON_APPROVAL" | "BY_REQUESTER";
|
|
62
|
+
traits: string[];
|
|
63
|
+
environmentId?: string | null;
|
|
64
|
+
display: components["schemas"]["HeldDisplayRowView"][];
|
|
65
|
+
params: string;
|
|
66
|
+
paramsHash: string;
|
|
67
|
+
version: number;
|
|
68
|
+
tokenName?: string | null;
|
|
69
|
+
effect: components["schemas"]["HeldEffectView"];
|
|
70
|
+
policy: components["schemas"]["HeldPolicyView"];
|
|
71
|
+
approverHint?: string | null;
|
|
72
|
+
approverId?: string | null;
|
|
73
|
+
decisionReason?: string | null;
|
|
74
|
+
outcomeDetail?: string | null;
|
|
75
|
+
link: string;
|
|
76
|
+
events: components["schemas"]["HeldEventView"][];
|
|
77
|
+
mine: boolean;
|
|
78
|
+
canDecide: boolean;
|
|
79
|
+
decideRefusal?: string | null;
|
|
80
|
+
canCancel: boolean;
|
|
81
|
+
mfaVerified: boolean;
|
|
82
|
+
requesterLacksPermission?: boolean | null;
|
|
83
|
+
}, ApiError, {
|
|
84
|
+
vote: "APPROVE" | "REJECT";
|
|
85
|
+
reason?: string | null;
|
|
86
|
+
paramsHash: string;
|
|
87
|
+
version: number;
|
|
88
|
+
}, unknown>;
|
|
89
|
+
/** Withdraws the caller's own request. */
|
|
90
|
+
export declare function useCancelHeld(): import("@tanstack/react-query").UseMutationResult<{
|
|
91
|
+
operation: components["schemas"]["HeldOperationSummaryView"];
|
|
92
|
+
typeVersion: number;
|
|
93
|
+
mode: "ON_APPROVAL" | "BY_REQUESTER";
|
|
94
|
+
traits: string[];
|
|
95
|
+
environmentId?: string | null;
|
|
96
|
+
display: components["schemas"]["HeldDisplayRowView"][];
|
|
97
|
+
params: string;
|
|
98
|
+
paramsHash: string;
|
|
99
|
+
version: number;
|
|
100
|
+
tokenName?: string | null;
|
|
101
|
+
effect: components["schemas"]["HeldEffectView"];
|
|
102
|
+
policy: components["schemas"]["HeldPolicyView"];
|
|
103
|
+
approverHint?: string | null;
|
|
104
|
+
approverId?: string | null;
|
|
105
|
+
decisionReason?: string | null;
|
|
106
|
+
outcomeDetail?: string | null;
|
|
107
|
+
link: string;
|
|
108
|
+
events: components["schemas"]["HeldEventView"][];
|
|
109
|
+
mine: boolean;
|
|
110
|
+
canDecide: boolean;
|
|
111
|
+
decideRefusal?: string | null;
|
|
112
|
+
canCancel: boolean;
|
|
113
|
+
mfaVerified: boolean;
|
|
114
|
+
requesterLacksPermission?: boolean | null;
|
|
115
|
+
}, ApiError, string, unknown>;
|
|
116
|
+
/** Whether an approval provider is installed, and whether break-glass bypasses it. */
|
|
117
|
+
export declare function useGateStatus({ enabled }?: Readonly<{
|
|
118
|
+
enabled?: boolean;
|
|
119
|
+
}>): import("@tanstack/react-query").UseQueryResult<{
|
|
120
|
+
armed: boolean;
|
|
121
|
+
providerId?: string | null;
|
|
122
|
+
attached: boolean;
|
|
123
|
+
breakGlass: boolean;
|
|
124
|
+
enforcing: boolean;
|
|
125
|
+
approvers: number;
|
|
126
|
+
quorate: boolean;
|
|
127
|
+
}, Error>;
|
|
128
|
+
/** The problem slug of a refused call, such as `held-operation-changed`. */
|
|
129
|
+
export declare function problemSlug(error: unknown): string | undefined;
|
|
130
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Column } from '../../ui/table/index.ts';
|
|
2
|
+
import { type HeldDisplayRow, type HeldOperationSummary } from './api.ts';
|
|
3
|
+
/** A value as the change table shows it: a secret says it is hidden, and an absent value says which absence. */
|
|
4
|
+
export declare function valueText(value: string | null | undefined, absent: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Whether the rows only name what the request acts on (a queue, a user) rather than change values: every row
|
|
7
|
+
* lacks a current value. Those read as a list of facts; rows with a current value read as a change table.
|
|
8
|
+
*/
|
|
9
|
+
export declare const namesTargets: (rows: readonly HeldDisplayRow[]) => boolean;
|
|
10
|
+
/** A row that names a target in words, as a confirmation repeats it: its value. */
|
|
11
|
+
export declare function targetText(row: HeldDisplayRow): string;
|
|
12
|
+
/** A change row in words, as a confirmation repeats it: `before → after`. */
|
|
13
|
+
export declare function changeText(row: HeldDisplayRow): string;
|
|
14
|
+
/** What a request changes, row by row: the item, its value now and its value after. */
|
|
15
|
+
export declare function changeColumns(): Column<HeldDisplayRow>[];
|
|
16
|
+
/**
|
|
17
|
+
* A list of requests: what each would do (a link to its page), its state, who asked and when. The caller's own
|
|
18
|
+
* requests (`mine`) leave out who asked: it is always them.
|
|
19
|
+
*/
|
|
20
|
+
export declare function requestColumns({ mine }?: Readonly<{
|
|
21
|
+
mine?: boolean;
|
|
22
|
+
}>): Column<HeldOperationSummary>[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { HeldEvent, HeldOperationSummary, HeldState } from './api.ts';
|
|
2
|
+
type Tone = 'neutral' | 'info' | 'warning' | 'danger';
|
|
3
|
+
/**
|
|
4
|
+
* A request's state in words, with the tone that emphasises it. Colour enters only where something went wrong
|
|
5
|
+
* or is not known; a request that waits or ended as asked reads near-monochrome.
|
|
6
|
+
*/
|
|
7
|
+
export declare const STATE: Readonly<Record<HeldState, {
|
|
8
|
+
word: string;
|
|
9
|
+
tone: Tone;
|
|
10
|
+
}>>;
|
|
11
|
+
/** What each timeline entry records. */
|
|
12
|
+
export declare const EVENT: Readonly<Record<string, string>>;
|
|
13
|
+
export declare const eventWord: (event: HeldEvent) => string;
|
|
14
|
+
/** How the requester was signed in when they asked. */
|
|
15
|
+
export declare const AUTH_KIND: Readonly<Record<HeldOperationSummary['authKind'], string>>;
|
|
16
|
+
/** How the requester was signed in, naming the API token when they used one. */
|
|
17
|
+
export declare function signedInWith(authKind: HeldOperationSummary['authKind'], tokenName?: string | null): string;
|
|
18
|
+
/** A trait as an approver reads it. */
|
|
19
|
+
export declare const TRAIT: Readonly<Record<string, string>>;
|
|
20
|
+
/**
|
|
21
|
+
* Time left before a request lapses, to the second in its last minute: `2d 3h`, `1h 05m`, `4m 09s`, `42s`.
|
|
22
|
+
* Floors, so it never claims more time than there is.
|
|
23
|
+
*/
|
|
24
|
+
export declare function remainingLabel(ms: number): string;
|
|
25
|
+
export declare function expiryMark(ms: number): string | undefined;
|
|
26
|
+
/** "1,204 messages": the effect as a figure with its unit. */
|
|
27
|
+
export declare function effectLabel(effect: {
|
|
28
|
+
count: number;
|
|
29
|
+
unit: string;
|
|
30
|
+
}): string;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface CanProps {
|
|
3
|
+
permission: string;
|
|
4
|
+
clusterId?: string;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
fallback?: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/** Renders `children` only if the current user holds `permission` (optionally scoped to `clusterId`). */
|
|
9
|
+
export declare function Can({ permission, clusterId, children, fallback }: CanProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The login screen, built only from the installation's identity providers
|
|
3
|
+
* (identity-and-sessions spec): a username and password form when a credential
|
|
4
|
+
* provider exists — with a choice when there is more than one — and one sign-in
|
|
5
|
+
* action per redirect provider. While the list loads the form is offered, so a
|
|
6
|
+
* slow request never reads as "sign-in unavailable". An account with a second factor gets a second step
|
|
7
|
+
* once its password is right (ADR-0143); nothing is signed in before it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function LoginView(): import("react").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type AuthResult, type SecondFactorMethod } from './api.ts';
|
|
2
|
+
/** The password step starts again, with `message` saying why (`failed`: the reason is an error, not a timeout). */
|
|
3
|
+
export interface Restart {
|
|
4
|
+
message: string;
|
|
5
|
+
failed: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The second step of a sign-in, and of a step-up for an account that has a factor (ADR-0143): a passkey when the
|
|
9
|
+
* account has one and this browser can use it, otherwise the six-digit code from an authenticator app, and a
|
|
10
|
+
* recovery code as the way out for a lost device. Submitted explicitly (Enter or the button), never on the sixth
|
|
11
|
+
* digit, so a code is never sent before the person means it.
|
|
12
|
+
*
|
|
13
|
+
* <p>The password is already right when this shows, so every failure states what happened and what to do:
|
|
14
|
+
* a wrong code stays here, beside its field; a timed-out sign-in or a locked account returns to the password
|
|
15
|
+
* step through `onRestart`, with the same generic words a wrong password gets.
|
|
16
|
+
*
|
|
17
|
+
* <p>`trustDeviceDays` above zero offers to trust this browser; it is zero for a step-up, which always asks.
|
|
18
|
+
*/
|
|
19
|
+
export declare function SecondFactorForm({ methods, trustDeviceDays, onDone, onRestart, onBack, }: Readonly<{
|
|
20
|
+
methods: SecondFactorMethod[];
|
|
21
|
+
trustDeviceDays: number;
|
|
22
|
+
onDone: (result: AuthResult) => void;
|
|
23
|
+
onRestart: (why: Restart) => void;
|
|
24
|
+
onBack: () => void;
|
|
25
|
+
}>): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Confirm it is you" (ADR-0103): a sensitive change — installing a plugin, changing a second factor — runs only
|
|
3
|
+
* after a sign-in from the last five minutes, so a session that signed in earlier confirms first: with its password
|
|
4
|
+
* and, when the account has one, its second factor; or by signing in again at its identity provider, which brings
|
|
5
|
+
* the operator back to `returnTo`. Renders nothing once the session is fresh.
|
|
6
|
+
*/
|
|
7
|
+
export declare function StepUp({ returnTo }: Readonly<{
|
|
8
|
+
returnTo: string;
|
|
9
|
+
}>): import("react").JSX.Element | null;
|
|
10
|
+
/**
|
|
11
|
+
* What an action shows when the server refused it with `reauthentication-required`: the step-up itself, then, once
|
|
12
|
+
* confirmed, a line saying to try again, since the refused action did not run. Renders nothing for any other error.
|
|
13
|
+
*/
|
|
14
|
+
export declare function StepUpPrompt({ error, returnTo }: Readonly<{
|
|
15
|
+
error: unknown;
|
|
16
|
+
returnTo: string;
|
|
17
|
+
}>): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { type UseQueryResult } from '@tanstack/react-query';
|
|
2
|
+
import { ApiError } from '../api/request.ts';
|
|
3
|
+
import type { components } from '../api/schema.d.ts';
|
|
4
|
+
type Schemas = components['schemas'];
|
|
5
|
+
export type GrantView = Schemas['GrantView'];
|
|
6
|
+
export type IdentityProviderView = Schemas['IdentityProviderView'];
|
|
7
|
+
export type LoginRequest = Schemas['LoginRequest'];
|
|
8
|
+
export type MeView = Schemas['MeView'];
|
|
9
|
+
export type AccessSummary = Schemas['AccessSummary'];
|
|
10
|
+
export type MyResourceAccess = Schemas['MyResourceAccess'];
|
|
11
|
+
export type AuthResult = Schemas['AuthResult'];
|
|
12
|
+
export type SecondFactorRequest = Schemas['SecondFactorRequest'];
|
|
13
|
+
/** How a person can prove a second factor: an authenticator code, a passkey, or a recovery code. */
|
|
14
|
+
export type SecondFactorMethod = NonNullable<AuthResult['methods']>[number];
|
|
15
|
+
export declare const keys: {
|
|
16
|
+
authProviders: readonly ["auth", "providers"];
|
|
17
|
+
me: readonly ["auth", "me"];
|
|
18
|
+
};
|
|
19
|
+
export declare const accessKeys: {
|
|
20
|
+
all: readonly ["access"];
|
|
21
|
+
of: (clusterId?: string) => readonly ["access", string];
|
|
22
|
+
resource: (clusterId: string, kind: string, name: string) => readonly ["access", "resource", string, string, string];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* What the signed-in user holds, globally or on one cluster, as the server's resolver decides it: through role
|
|
26
|
+
* grants at any scope, and for a cluster the resource permissions held somewhere on it through teams and shares.
|
|
27
|
+
* The console only uses it to offer or withhold controls; the server enforces.
|
|
28
|
+
*/
|
|
29
|
+
export declare function accessQuery(clusterId?: string): import("@tanstack/query-core").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<{
|
|
30
|
+
permissions: string[];
|
|
31
|
+
anywhere: string[];
|
|
32
|
+
canSeeCluster?: boolean | null;
|
|
33
|
+
teams: components["schemas"]["TeamMembership"][];
|
|
34
|
+
createPatterns: components["schemas"]["CreatePatterns"];
|
|
35
|
+
}, Error, {
|
|
36
|
+
permissions: string[];
|
|
37
|
+
anywhere: string[];
|
|
38
|
+
canSeeCluster?: boolean | null;
|
|
39
|
+
teams: components["schemas"]["TeamMembership"][];
|
|
40
|
+
createPatterns: components["schemas"]["CreatePatterns"];
|
|
41
|
+
}, readonly ["access", string]>, "queryFn"> & {
|
|
42
|
+
queryFn?: import("@tanstack/query-core").QueryFunction<{
|
|
43
|
+
permissions: string[];
|
|
44
|
+
anywhere: string[];
|
|
45
|
+
canSeeCluster?: boolean | null;
|
|
46
|
+
teams: components["schemas"]["TeamMembership"][];
|
|
47
|
+
createPatterns: components["schemas"]["CreatePatterns"];
|
|
48
|
+
}, readonly ["access", string], never> | undefined;
|
|
49
|
+
} & import("@tanstack/query-core").QueryKeyWithDataTag<readonly ["access", string], {
|
|
50
|
+
permissions: string[];
|
|
51
|
+
anywhere: string[];
|
|
52
|
+
canSeeCluster?: boolean | null;
|
|
53
|
+
teams: components["schemas"]["TeamMembership"][];
|
|
54
|
+
createPatterns: components["schemas"]["CreatePatterns"];
|
|
55
|
+
}, Error>;
|
|
56
|
+
/** What the signed-in user may do with one queue or address: the actions they hold on it, as the server decides. */
|
|
57
|
+
export declare function resourceAccessQuery(clusterId: string, kind: 'QUEUE' | 'ADDRESS', name: string): import("@tanstack/query-core").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<{
|
|
58
|
+
actions: string[];
|
|
59
|
+
}, Error, {
|
|
60
|
+
actions: string[];
|
|
61
|
+
}, readonly ["access", "resource", string, string, string]>, "queryFn"> & {
|
|
62
|
+
queryFn?: import("@tanstack/query-core").QueryFunction<{
|
|
63
|
+
actions: string[];
|
|
64
|
+
}, readonly ["access", "resource", string, string, string], never> | undefined;
|
|
65
|
+
} & import("@tanstack/query-core").QueryKeyWithDataTag<readonly ["access", "resource", string, string, string], {
|
|
66
|
+
actions: string[];
|
|
67
|
+
}, Error>;
|
|
68
|
+
/** `retry: false` — a 401 here means "not logged in," which retrying cannot fix. */
|
|
69
|
+
export declare function useMe(): UseQueryResult<MeView, ApiError>;
|
|
70
|
+
/** Public, unauthenticated — the login screen needs this before any session exists. */
|
|
71
|
+
export declare function useAuthProviders(): UseQueryResult<IdentityProviderView[], ApiError>;
|
|
72
|
+
export declare function useLogin(): import("@tanstack/react-query").UseMutationResult<{
|
|
73
|
+
status: "AUTHENTICATED" | "SECOND_FACTOR_REQUIRED";
|
|
74
|
+
me?: components["schemas"]["MeView"] | null;
|
|
75
|
+
methods?: ("TOTP" | "WEBAUTHN" | "RECOVERY_CODE" | "TRUSTED_DEVICE")[] | null;
|
|
76
|
+
trustDeviceDays: number;
|
|
77
|
+
}, ApiError, {
|
|
78
|
+
provider?: string | null;
|
|
79
|
+
username: string;
|
|
80
|
+
password: string;
|
|
81
|
+
}, unknown>;
|
|
82
|
+
/** The server wants a fresh sign-in before it acts (ADR-0103). */
|
|
83
|
+
export declare function needsReauthentication(error: unknown): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Step-up with a password (ADR-0103). An account with a second factor answers `SECOND_FACTOR_REQUIRED` and
|
|
86
|
+
* is not fresh until {@link useSecondFactor} finishes it; an account without one is fresh now, so `/auth/me`,
|
|
87
|
+
* which carries when the session last signed in, is refreshed.
|
|
88
|
+
*/
|
|
89
|
+
export declare function useReauthenticate(): import("@tanstack/react-query").UseMutationResult<{
|
|
90
|
+
status: "AUTHENTICATED" | "SECOND_FACTOR_REQUIRED";
|
|
91
|
+
me?: components["schemas"]["MeView"] | null;
|
|
92
|
+
methods?: ("TOTP" | "WEBAUTHN" | "RECOVERY_CODE" | "TRUSTED_DEVICE")[] | null;
|
|
93
|
+
trustDeviceDays: number;
|
|
94
|
+
}, ApiError, string, unknown>;
|
|
95
|
+
/**
|
|
96
|
+
* The second factor of a sign-in, when no one is signed in yet, or of a step-up, when someone is: the server
|
|
97
|
+
* tells them apart by the session. Either way `/auth/me` is refreshed, since what it carries has changed.
|
|
98
|
+
*/
|
|
99
|
+
export declare function useSecondFactor(): import("@tanstack/react-query").UseMutationResult<{
|
|
100
|
+
status: "AUTHENTICATED" | "SECOND_FACTOR_REQUIRED";
|
|
101
|
+
me?: components["schemas"]["MeView"] | null;
|
|
102
|
+
methods?: ("TOTP" | "WEBAUTHN" | "RECOVERY_CODE" | "TRUSTED_DEVICE")[] | null;
|
|
103
|
+
trustDeviceDays: number;
|
|
104
|
+
}, ApiError, {
|
|
105
|
+
totpCode?: string | null;
|
|
106
|
+
recoveryCode?: string | null;
|
|
107
|
+
webauthn?: {
|
|
108
|
+
[key: string]: unknown;
|
|
109
|
+
} | null;
|
|
110
|
+
trustDevice?: boolean | null;
|
|
111
|
+
}, unknown>;
|
|
112
|
+
/** The options for a passkey answer to the second factor now owed, as `parseRequestOptionsFromJSON` takes them. */
|
|
113
|
+
export declare function fetchPasskeyRequestOptions(): Promise<PublicKeyCredentialRequestOptionsJSON>;
|
|
114
|
+
export declare function useLogout(): import("@tanstack/react-query").UseMutationResult<void, ApiError, void, unknown>;
|
|
115
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A queue or address of a cluster, for a check on one resource. Pass the `allowedActions` a row already
|
|
3
|
+
* carries from the server to answer from it with no request.
|
|
4
|
+
*/
|
|
5
|
+
export interface ResourceWhere {
|
|
6
|
+
clusterId: string;
|
|
7
|
+
kind: 'queue' | 'address';
|
|
8
|
+
name: string;
|
|
9
|
+
allowedActions?: AllowedActions;
|
|
10
|
+
}
|
|
11
|
+
/** The actions the server says the caller holds on one row, as a list carries them. */
|
|
12
|
+
export type AllowedActions = readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* Client-side reflection of the server's permission model (authorization spec), used only to offer, disable or
|
|
15
|
+
* explain controls a request would be rejected for anyway. The server's own checks are the enforcement; this reads
|
|
16
|
+
* the summary it computes with the same resolver (`/me/access`), so environment grants, teams and shares are
|
|
17
|
+
* counted as the server counts them.
|
|
18
|
+
*
|
|
19
|
+
* <p>`can(permission)` is about the whole installation. `can(permission, clusterId)` is about one cluster, and
|
|
20
|
+
* `can(permission, { clusterId, kind, name })` about one queue or address, with a team's or share's rights counted.
|
|
21
|
+
* Each is fetched the first time it is asked. Until it arrives the control is offered, as it is while grants load:
|
|
22
|
+
* blocking on an answer not yet known would lock out someone the server lets in. For a resource permission the
|
|
23
|
+
* cluster answer counts grants only; what a team or share gives on some queue or address is in the summary's
|
|
24
|
+
* `anywhere`, and on one named queue or address in its own answer, or in the row's `allowedActions`.
|
|
25
|
+
*
|
|
26
|
+
* <p>`canAnywhere` is the page-level right: held on the cluster by a grant, or on some queue or address through a team
|
|
27
|
+
* or a share. A control is hidden only when it is false. `canOn` is the right on one row the server sent, from the
|
|
28
|
+
* row's `allowedActions`: false there is a control shown disabled, with its reason. `createPatterns` are the name
|
|
29
|
+
* patterns a team or share lets the caller create queues and addresses under.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useCan(): {
|
|
32
|
+
can: (permission: string, where?: string | ResourceWhere) => boolean;
|
|
33
|
+
canAnywhere: (permission: string, clusterId: string) => boolean;
|
|
34
|
+
reach: (permission: string, clusterId: string) => "grant" | "teams" | "none" | "unknown";
|
|
35
|
+
/** Whether the row the server sent allows the action to the caller. */
|
|
36
|
+
canOn: (permission: string, resource: {
|
|
37
|
+
allowedActions: AllowedActions;
|
|
38
|
+
}) => boolean;
|
|
39
|
+
createPatterns: (clusterId: string, kind: "queue" | "address") => string[] | undefined;
|
|
40
|
+
/** The teams the caller belongs to, with their role in each. */
|
|
41
|
+
teams: {
|
|
42
|
+
teamId: string;
|
|
43
|
+
teamName: string;
|
|
44
|
+
roleId: string;
|
|
45
|
+
roleName: string;
|
|
46
|
+
teamAdmin: boolean;
|
|
47
|
+
}[];
|
|
48
|
+
loading: boolean;
|
|
49
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passkeys through the browser's own JSON helpers (`PublicKeyCredential.parse*OptionsFromJSON` and `toJSON()`),
|
|
3
|
+
* with no WebAuthn library: the server speaks the JSON forms, and a browser that has the API has the helpers.
|
|
4
|
+
*/
|
|
5
|
+
/** Whether this browser can take the server's passkey options as they are sent. */
|
|
6
|
+
export declare function passkeysSupported(): boolean;
|
|
7
|
+
/** Said where a passkey cannot be used, so the reason and the alternative are one sentence. */
|
|
8
|
+
export declare const PASSKEYS_UNSUPPORTED = "This browser cannot use passkeys. Update it, or use another browser.";
|
|
9
|
+
/** Why passkeys cannot be used here, or null. Until the server has said (`undefined`), unknown is not unavailable. */
|
|
10
|
+
export declare function passkeyUnavailableReason(webauthn: {
|
|
11
|
+
available: boolean;
|
|
12
|
+
reason?: string | null;
|
|
13
|
+
} | undefined): string | null;
|
|
14
|
+
/** Asks for a passkey to answer the options with. `null` when the prompt was dismissed. */
|
|
15
|
+
export declare function getPasskey(options: PublicKeyCredentialRequestOptionsJSON): Promise<object | null>;
|
|
16
|
+
/** Asks the browser to create a passkey. `null` when the prompt was dismissed. */
|
|
17
|
+
export declare function createPasskey(options: PublicKeyCredentialCreationOptionsJSON): Promise<object | null>;
|
|
18
|
+
/** What went wrong in the browser, and what to do, for an error that is not a dismissed prompt. */
|
|
19
|
+
export declare function passkeyFailure(error: unknown): string;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
import type { SpotlightActionGroupData } from '@mantine/spotlight';
|
|
3
|
+
import type { Icon } from '@tabler/icons-react';
|
|
4
|
+
import type { AnyRoute } from '@tanstack/react-router';
|
|
5
|
+
import type { NavGroupId } from './nav/groups.ts';
|
|
6
|
+
import type { SlotContributions } from './slots.ts';
|
|
7
|
+
/** The extension contract version, shared with the backend's `Contract.VERSION` (ADR-0070). */
|
|
8
|
+
export declare const CONTRACT = 15;
|
|
9
|
+
/** The module ids the backend manifest reports. A frontend feature uses the same id as its backend module. */
|
|
10
|
+
export declare const FEATURE_IDS: readonly ["security", "audit", "auditfeed", "settings", "lifecycle", "stream", "inbox", "approvals", "broker", "clusters", "governance", "scrape", "mcp", "queues", "resources", "messages", "routing", "metrics", "alerting", "events", "rr", "sql", "brokerconfig", "flow", "triage", "bulk", "transfer", "setupreview", "diagnostics", "apitokens", "plugins", "identity-local", "identity-oidc"];
|
|
11
|
+
export type FeatureId = (typeof FEATURE_IDS)[number];
|
|
12
|
+
/**
|
|
13
|
+
* An installed plugin's id, as its `plugin.json` declares it: lowercase kebab-case, vendor first,
|
|
14
|
+
* at least two segments (ADR-0099). A plugin's routes, slot ids and topics are all namespaced by it.
|
|
15
|
+
*/
|
|
16
|
+
export type PluginId = `${string}-${string}`;
|
|
17
|
+
/** A built-in module or an installed plugin — whatever the manifest lists. */
|
|
18
|
+
export type ModuleId = FeatureId | PluginId;
|
|
19
|
+
/**
|
|
20
|
+
* The routes a feature adds, each created with a kernel root from `routing/roots.ts` as its parent. The
|
|
21
|
+
* composition root adds every installed feature's routes whether or not the feature is enabled, so a deep
|
|
22
|
+
* link to a disabled feature reaches the page that explains it rather than a not-found.
|
|
23
|
+
*/
|
|
24
|
+
export interface RouteContributions {
|
|
25
|
+
/** Pages outside any cluster, children of `rootRoute`. */
|
|
26
|
+
root?: AnyRoute[];
|
|
27
|
+
/** Views of one cluster, children of `clusterRoute`. */
|
|
28
|
+
cluster?: AnyRoute[];
|
|
29
|
+
}
|
|
30
|
+
/** One view in a cluster's navigation and command palette. */
|
|
31
|
+
export interface NavContribution {
|
|
32
|
+
group: NavGroupId;
|
|
33
|
+
/** Position within the group; lower comes first. */
|
|
34
|
+
order: number;
|
|
35
|
+
label: string;
|
|
36
|
+
icon: Icon;
|
|
37
|
+
/** The view's path below `/clusters/$clusterId/`. */
|
|
38
|
+
path: string;
|
|
39
|
+
/**
|
|
40
|
+
* The permission the view reads with. Without it the entry stays visible and disabled,
|
|
41
|
+
* and says why; the server enforces it regardless.
|
|
42
|
+
*/
|
|
43
|
+
permission?: string;
|
|
44
|
+
/** Shown after the label, such as a count of what needs attention. */
|
|
45
|
+
Badge?: ComponentType<{
|
|
46
|
+
clusterId: string;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* The letter that, after `g`, goes to this view (ADR-0109). Unique among the built-in views; a
|
|
50
|
+
* plugin's is ignored, so a plugin can never take a letter an operator already relies on.
|
|
51
|
+
*/
|
|
52
|
+
hotkey?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A feature's command-palette groups. It is rendered inside the palette, so it may use hooks, and calls
|
|
56
|
+
* `report` whenever its groups change; `clusterId` is the cluster in view, if there is one.
|
|
57
|
+
*
|
|
58
|
+
* `query` is what the operator has typed (debounced) and `opened` whether the palette is open: a source
|
|
59
|
+
* that searches fetches only while it is open, never on every keystroke of a broker (ADR-0109).
|
|
60
|
+
*/
|
|
61
|
+
export type PaletteSource = ComponentType<{
|
|
62
|
+
clusterId?: string;
|
|
63
|
+
query: string;
|
|
64
|
+
opened: boolean;
|
|
65
|
+
report: (groups: SpotlightActionGroupData[]) => void;
|
|
66
|
+
}>;
|
|
67
|
+
/** Handles one frame of a stream topic the feature owns (ADR-0070). */
|
|
68
|
+
export type TopicHandler = (frame: {
|
|
69
|
+
clusterId: string;
|
|
70
|
+
/** The frame's payload, as the server sent it. */
|
|
71
|
+
data: string;
|
|
72
|
+
/** Invalidates a query key, or only marks it stale while refreshing is paused. */
|
|
73
|
+
invalidate: (queryKey: readonly unknown[]) => void;
|
|
74
|
+
}) => void;
|
|
75
|
+
/** What a frontend feature contributes to the shell (ADR-0070). */
|
|
76
|
+
export interface RahdarFeature {
|
|
77
|
+
contract: typeof CONTRACT;
|
|
78
|
+
/** A built-in's module id, or a plugin's id (a plugin is loaded from its own bundle at startup). */
|
|
79
|
+
id: ModuleId;
|
|
80
|
+
routes?: RouteContributions;
|
|
81
|
+
nav?: NavContribution[];
|
|
82
|
+
palette?: PaletteSource;
|
|
83
|
+
slots?: SlotContributions;
|
|
84
|
+
/**
|
|
85
|
+
* A handler per stream topic the feature's backend module declares. A cluster's layout subscribes to
|
|
86
|
+
* the topics of every enabled feature.
|
|
87
|
+
*/
|
|
88
|
+
streamTopics?: Record<string, TopicHandler>;
|
|
89
|
+
}
|
|
90
|
+
export declare function defineFeature<T extends RahdarFeature>(feature: T): T;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RahdarFeature } from './feature.ts';
|
|
2
|
+
/** The composition root's features. The kernel never imports them; `FeatureProvider` hands them in (ADR-0069). */
|
|
3
|
+
export declare const FeaturesContext: import("react").Context<RahdarFeature[] | null>;
|
|
4
|
+
/** The installed features this installation has enabled, in composition order. */
|
|
5
|
+
export declare function useFeatures(): RahdarFeature[];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The header's bell: the unread count, kept live by the user's stream on every page, and a popover with the
|
|
3
|
+
* latest notices, "Mark all read" and the way to the inbox. Opening a notice marks it read and follows its
|
|
4
|
+
* link inside the app. A new notice is announced politely.
|
|
5
|
+
*/
|
|
6
|
+
export declare function InboxBell(): import("react").JSX.Element;
|