nucleus-core-ts 0.9.781 → 0.9.783
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/dist/client.js +1 -1
- package/dist/fe/components/IntegrationsPage/components/ConnectionAuthFields.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionAuthFields.js +85 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionBasicFields.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionBasicFields.js +165 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionForm.d.ts +10 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionForm.js +112 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionList.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionList.js +184 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionOauthFields.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionOauthFields.js +131 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.d.ts +21 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.js +49 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTabs.d.ts +12 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTabs.js +95 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTest.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTest.js +116 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialFields.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialFields.js +171 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.d.ts +13 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.js +63 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointForm.d.ts +11 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointForm.js +166 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.d.ts +19 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.js +162 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointList.d.ts +12 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointList.js +205 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointSample.d.ts +6 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointSample.js +204 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointsTab.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointsTab.js +101 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.d.ts +17 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.js +179 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.d.ts +11 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.js +115 -0
- package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.js +175 -0
- package/dist/fe/components/IntegrationsPage/components/MappingEditor.d.ts +15 -0
- package/dist/fe/components/IntegrationsPage/components/MappingEditor.js +193 -0
- package/dist/fe/components/IntegrationsPage/components/MappingList.d.ts +11 -0
- package/dist/fe/components/IntegrationsPage/components/MappingList.js +191 -0
- package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.d.ts +19 -0
- package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.js +150 -0
- package/dist/fe/components/IntegrationsPage/components/MappingsTab.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/MappingsTab.js +154 -0
- package/dist/fe/components/IntegrationsPage/components/PaginationFields.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/PaginationFields.js +218 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRow.d.ts +24 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRow.js +180 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.d.ts +33 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.js +100 -0
- package/dist/fe/components/IntegrationsPage/components/Primitives.d.ts +65 -0
- package/dist/fe/components/IntegrationsPage/components/Primitives.js +94 -0
- package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.d.ts +10 -0
- package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.js +48 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistory.d.ts +10 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistory.js +124 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.d.ts +7 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.js +129 -0
- package/dist/fe/components/IntegrationsPage/components/RunPanel.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/RunPanel.js +160 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.js +43 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.js +92 -0
- package/dist/fe/components/IntegrationsPage/components/RunProgressBar.d.ts +8 -0
- package/dist/fe/components/IntegrationsPage/components/RunProgressBar.js +77 -0
- package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.d.ts +13 -0
- package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.js +65 -0
- package/dist/fe/components/IntegrationsPage/components/RunsTab.d.ts +13 -0
- package/dist/fe/components/IntegrationsPage/components/RunsTab.js +39 -0
- package/dist/fe/components/IntegrationsPage/components/Skeletons.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/Skeletons.js +139 -0
- package/dist/fe/components/IntegrationsPage/components/authOptions.d.ts +29 -0
- package/dist/fe/components/IntegrationsPage/components/authOptions.js +71 -0
- package/dist/fe/components/IntegrationsPage/components/connectionDraft.d.ts +64 -0
- package/dist/fe/components/IntegrationsPage/components/connectionDraft.js +112 -0
- package/dist/fe/components/IntegrationsPage/components/credentialSlots.d.ts +25 -0
- package/dist/fe/components/IntegrationsPage/components/credentialSlots.js +67 -0
- package/dist/fe/components/IntegrationsPage/components/fieldMappingOptions.d.ts +16 -0
- package/dist/fe/components/IntegrationsPage/components/fieldMappingOptions.js +41 -0
- package/dist/fe/components/IntegrationsPage/components/formSupport.d.ts +19 -0
- package/dist/fe/components/IntegrationsPage/components/formSupport.js +25 -0
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.d.ts +40 -0
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.js +44 -0
- package/dist/fe/components/IntegrationsPage/components/pathParams.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/pathParams.js +15 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.d.ts +33 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.js +85 -0
- package/dist/fe/components/IntegrationsPage/components/useInfiniteList.d.ts +40 -0
- package/dist/fe/components/IntegrationsPage/components/useInfiniteList.js +110 -0
- package/dist/fe/components/IntegrationsPage/index.d.ts +18 -0
- package/dist/fe/components/IntegrationsPage/index.js +11 -0
- package/dist/fe/components/IntegrationsPage/store/index.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/store/index.js +124 -0
- package/dist/fe/components/IntegrationsPage/store/state.d.ts +80 -0
- package/dist/fe/components/IntegrationsPage/store/state.js +32 -0
- package/dist/fe/components/IntegrationsPage/theme/index.d.ts +103 -0
- package/dist/fe/components/IntegrationsPage/theme/index.js +113 -0
- package/dist/fe/components/IntegrationsPage/types/actions.d.ts +91 -0
- package/dist/fe/components/IntegrationsPage/types/actions.js +1 -0
- package/dist/fe/components/IntegrationsPage/types/index.d.ts +3 -0
- package/dist/fe/components/IntegrationsPage/types/index.js +3 -0
- package/dist/fe/components/IntegrationsPage/types/records.d.ts +139 -0
- package/dist/fe/components/IntegrationsPage/types/records.js +11 -0
- package/dist/fe/components/IntegrationsPage/types/results.d.ts +52 -0
- package/dist/fe/components/IntegrationsPage/types/results.js +4 -0
- package/dist/fe/index.d.ts +2 -0
- package/dist/fe/index.js +1 -0
- package/dist/index.js +14 -14
- package/dist/src/Client/ApiCaller/system-tables.d.ts +505 -0
- package/dist/src/Client/Proxy/wsProxy.js +25 -1
- package/dist/src/ElysiaPlugin/routes/backup/types.d.ts +3 -1
- package/dist/src/ElysiaPlugin/routes/integrations/index.d.ts +49 -0
- package/dist/src/ElysiaPlugin/routes/integrations/inspect.d.ts +87 -0
- package/dist/src/ElysiaPlugin/routes/integrations/inspectRoutes.d.ts +41 -0
- package/dist/src/ElysiaPlugin/routes/integrations/repository.d.ts +88 -0
- package/dist/src/ElysiaPlugin/routes/integrations/types.d.ts +22 -0
- package/dist/src/ElysiaPlugin/routes/pubsub/frame.d.ts +37 -0
- package/dist/src/ElysiaPlugin/routes/pubsub/frame.test.d.ts +1 -0
- package/dist/src/Services/Backup/BackupService.d.ts +16 -8
- package/dist/src/Services/Backup/types.d.ts +25 -0
- package/dist/src/Services/Integrations/bindings.d.ts +116 -0
- package/dist/src/Services/Integrations/bindings.test.d.ts +1 -0
- package/dist/src/Services/Integrations/collect.d.ts +68 -0
- package/dist/src/Services/Integrations/collect.test.d.ts +1 -0
- package/dist/src/Services/Integrations/fetcher.d.ts +100 -0
- package/dist/src/Services/Integrations/fetcher.test.d.ts +1 -0
- package/dist/src/Services/Integrations/lock.d.ts +77 -0
- package/dist/src/Services/Integrations/lock.test.d.ts +1 -0
- package/dist/src/Services/Integrations/mapping.d.ts +63 -0
- package/dist/src/Services/Integrations/mapping.test.d.ts +1 -0
- package/dist/src/Services/Integrations/oauth.d.ts +89 -0
- package/dist/src/Services/Integrations/oauth.test.d.ts +1 -0
- package/dist/src/Services/Integrations/pagination.d.ts +97 -0
- package/dist/src/Services/Integrations/pagination.test.d.ts +1 -0
- package/dist/src/Services/Integrations/plan.d.ts +74 -0
- package/dist/src/Services/Integrations/plan.test.d.ts +1 -0
- package/dist/src/Services/Integrations/revert.d.ts +78 -0
- package/dist/src/Services/Integrations/revert.test.d.ts +1 -0
- package/dist/src/Services/Integrations/runner.d.ts +126 -0
- package/dist/src/Services/Integrations/runner.test.d.ts +1 -0
- package/dist/src/Services/Integrations/types.d.ts +208 -0
- package/dist/src/Services/Integrations/writer.d.ts +105 -0
- package/dist/src/Services/Integrations/writer.test.d.ts +1 -0
- package/dist/src/Utils/cron.d.ts +65 -0
- package/dist/src/Utils/cron.test.d.ts +1 -0
- package/dist/src/types.d.ts +67 -1
- package/package.json +1 -1
- package/schemas/config.nucleus.json +43 -0
- package/schemas/table.schema.json +2 -1
- package/scripts/generate-schema.test.ts +78 -0
- package/scripts/generate-schema.ts +43 -5
- package/src/system.tables.json +362 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { integrationsPageTheme } from '../theme';
|
|
4
|
+
import { CLIENT_AUTH_OPTIONS, GRANT_TYPE_OPTIONS, toClientAuth, toGrantType } from './authOptions';
|
|
5
|
+
import { usesOauth } from './connectionDraft';
|
|
6
|
+
import { describedBy } from './formSupport';
|
|
7
|
+
import { Field } from './Primitives';
|
|
8
|
+
/**
|
|
9
|
+
* The OAuth 2 half of the auth section.
|
|
10
|
+
*
|
|
11
|
+
* It renders nothing at all for the other auth types, so the caller can place it
|
|
12
|
+
* unconditionally and the question "is this connection OAuth" is answered in one
|
|
13
|
+
* place instead of at every field.
|
|
14
|
+
*/ const theme = integrationsPageTheme;
|
|
15
|
+
export function ConnectionOauthFields({ draft, errors, disabled, onChange }) {
|
|
16
|
+
if (!usesOauth(draft.authType)) return null;
|
|
17
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
18
|
+
children: [
|
|
19
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
20
|
+
error: errors.tokenUrl,
|
|
21
|
+
hint: "The full URL the token is fetched from. Same rule as the base URL: any scheme, http included.",
|
|
22
|
+
htmlFor: "connection-token-url",
|
|
23
|
+
label: "Token URL",
|
|
24
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
25
|
+
"aria-describedby": describedBy('connection-token-url', true, Boolean(errors.tokenUrl)),
|
|
26
|
+
className: theme.field.input,
|
|
27
|
+
disabled: disabled,
|
|
28
|
+
id: "connection-token-url",
|
|
29
|
+
onChange: (event)=>onChange({
|
|
30
|
+
tokenUrl: event.target.value
|
|
31
|
+
}),
|
|
32
|
+
placeholder: "https://api.example.com/oauth/token",
|
|
33
|
+
type: "text",
|
|
34
|
+
value: draft.tokenUrl
|
|
35
|
+
})
|
|
36
|
+
}),
|
|
37
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
38
|
+
hint: "Client credentials calls as the application. Password calls as a named user and needs that user's name and password.",
|
|
39
|
+
htmlFor: "connection-grant-type",
|
|
40
|
+
label: "Grant type",
|
|
41
|
+
children: /*#__PURE__*/ _jsx("select", {
|
|
42
|
+
"aria-describedby": describedBy('connection-grant-type', true, false),
|
|
43
|
+
className: theme.field.input,
|
|
44
|
+
disabled: disabled,
|
|
45
|
+
id: "connection-grant-type",
|
|
46
|
+
onChange: (event)=>{
|
|
47
|
+
const next = toGrantType(event.target.value);
|
|
48
|
+
if (next) onChange({
|
|
49
|
+
oauthGrantType: next
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
value: draft.oauthGrantType,
|
|
53
|
+
children: GRANT_TYPE_OPTIONS.map((option)=>/*#__PURE__*/ _jsx("option", {
|
|
54
|
+
value: option.value,
|
|
55
|
+
children: option.label
|
|
56
|
+
}, option.value))
|
|
57
|
+
})
|
|
58
|
+
}),
|
|
59
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
60
|
+
htmlFor: "connection-client-id",
|
|
61
|
+
label: "Client ID",
|
|
62
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
63
|
+
className: theme.field.input,
|
|
64
|
+
disabled: disabled,
|
|
65
|
+
id: "connection-client-id",
|
|
66
|
+
onChange: (event)=>onChange({
|
|
67
|
+
oauthClientId: event.target.value
|
|
68
|
+
}),
|
|
69
|
+
placeholder: "integration-client",
|
|
70
|
+
type: "text",
|
|
71
|
+
value: draft.oauthClientId
|
|
72
|
+
})
|
|
73
|
+
}),
|
|
74
|
+
draft.oauthGrantType === 'password' ? /*#__PURE__*/ _jsx(Field, {
|
|
75
|
+
htmlFor: "connection-oauth-username",
|
|
76
|
+
label: "User name",
|
|
77
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
78
|
+
autoComplete: "off",
|
|
79
|
+
className: theme.field.input,
|
|
80
|
+
disabled: disabled,
|
|
81
|
+
id: "connection-oauth-username",
|
|
82
|
+
onChange: (event)=>onChange({
|
|
83
|
+
oauthUsername: event.target.value
|
|
84
|
+
}),
|
|
85
|
+
placeholder: "service-account",
|
|
86
|
+
type: "text",
|
|
87
|
+
value: draft.oauthUsername
|
|
88
|
+
})
|
|
89
|
+
}) : null,
|
|
90
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
91
|
+
hint: "Space-separated. Leave it empty to take whatever the token endpoint grants by default.",
|
|
92
|
+
htmlFor: "connection-scope",
|
|
93
|
+
label: "Scope",
|
|
94
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
95
|
+
"aria-describedby": describedBy('connection-scope', true, false),
|
|
96
|
+
className: theme.field.input,
|
|
97
|
+
disabled: disabled,
|
|
98
|
+
id: "connection-scope",
|
|
99
|
+
onChange: (event)=>onChange({
|
|
100
|
+
oauthScope: event.target.value
|
|
101
|
+
}),
|
|
102
|
+
placeholder: "read:records",
|
|
103
|
+
type: "text",
|
|
104
|
+
value: draft.oauthScope
|
|
105
|
+
})
|
|
106
|
+
}),
|
|
107
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
108
|
+
hint: "Where the client ID and secret go on the token request. A server accepts one or the other; if the token call comes back with an invalid-client error, try the other.",
|
|
109
|
+
htmlFor: "connection-client-auth",
|
|
110
|
+
label: "Client authentication",
|
|
111
|
+
children: /*#__PURE__*/ _jsx("select", {
|
|
112
|
+
"aria-describedby": describedBy('connection-client-auth', true, false),
|
|
113
|
+
className: theme.field.input,
|
|
114
|
+
disabled: disabled,
|
|
115
|
+
id: "connection-client-auth",
|
|
116
|
+
onChange: (event)=>{
|
|
117
|
+
const next = toClientAuth(event.target.value);
|
|
118
|
+
if (next) onChange({
|
|
119
|
+
oauthClientAuth: next
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
value: draft.oauthClientAuth,
|
|
123
|
+
children: CLIENT_AUTH_OPTIONS.map((option)=>/*#__PURE__*/ _jsx("option", {
|
|
124
|
+
value: option.value,
|
|
125
|
+
children: option.label
|
|
126
|
+
}, option.value))
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
]
|
|
130
|
+
});
|
|
131
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IntegrationSource, IntegrationsActions } from '../types';
|
|
2
|
+
export type ConnectionSettingsTabProps = {
|
|
3
|
+
actions: IntegrationsActions;
|
|
4
|
+
/** Called after a NEW connection is saved, so the list can pick it up. */
|
|
5
|
+
onSaved?: (source: IntegrationSource) => void;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Everything about the connection itself: its address and authentication, its
|
|
9
|
+
* credentials, and whether it answers.
|
|
10
|
+
*
|
|
11
|
+
* These three were written and then never reachable — the shell mounted the
|
|
12
|
+
* connection list and the three child tabs and nothing else, so the panel could
|
|
13
|
+
* show you connections but gave you no way to create or edit one. That is the
|
|
14
|
+
* first screen an operator needs and it was the one screen missing.
|
|
15
|
+
*
|
|
16
|
+
* Credentials sit between the form and the test on purpose: an operator fills in
|
|
17
|
+
* the address, enters the secret, then presses test. Putting the test above the
|
|
18
|
+
* credential boxes invites pressing it before there is anything to authenticate
|
|
19
|
+
* with, and reading the failure as "the address is wrong".
|
|
20
|
+
*/
|
|
21
|
+
export declare function ConnectionSettingsTab({ actions, onSaved }: ConnectionSettingsTabProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useIntegrationsStore } from '../store';
|
|
4
|
+
import { integrationsPageTheme as theme } from '../theme';
|
|
5
|
+
import { ConnectionForm } from './ConnectionForm';
|
|
6
|
+
import { ConnectionTest } from './ConnectionTest';
|
|
7
|
+
import { CredentialFields } from './CredentialFields';
|
|
8
|
+
/**
|
|
9
|
+
* Everything about the connection itself: its address and authentication, its
|
|
10
|
+
* credentials, and whether it answers.
|
|
11
|
+
*
|
|
12
|
+
* These three were written and then never reachable — the shell mounted the
|
|
13
|
+
* connection list and the three child tabs and nothing else, so the panel could
|
|
14
|
+
* show you connections but gave you no way to create or edit one. That is the
|
|
15
|
+
* first screen an operator needs and it was the one screen missing.
|
|
16
|
+
*
|
|
17
|
+
* Credentials sit between the form and the test on purpose: an operator fills in
|
|
18
|
+
* the address, enters the secret, then presses test. Putting the test above the
|
|
19
|
+
* credential boxes invites pressing it before there is anything to authenticate
|
|
20
|
+
* with, and reading the failure as "the address is wrong".
|
|
21
|
+
*/ export function ConnectionSettingsTab({ actions, onSaved }) {
|
|
22
|
+
const store = useIntegrationsStore();
|
|
23
|
+
const source = store.sources.find((candidate)=>candidate.id === store.selectedSourceId) ?? null;
|
|
24
|
+
// The list holds only the current page, so a connection selected before a
|
|
25
|
+
// later page loaded may not be in it yet. Saying so beats an empty form that
|
|
26
|
+
// looks like the record was lost.
|
|
27
|
+
const missingFromPage = store.selectedSourceId !== null && source === null;
|
|
28
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
29
|
+
className: theme.layout.main,
|
|
30
|
+
children: [
|
|
31
|
+
/*#__PURE__*/ _jsx(ConnectionForm, {
|
|
32
|
+
actions: actions,
|
|
33
|
+
loading: store.sourcesLoading || missingFromPage,
|
|
34
|
+
onSaved: onSaved,
|
|
35
|
+
source: source
|
|
36
|
+
}),
|
|
37
|
+
/*#__PURE__*/ _jsx(CredentialFields, {
|
|
38
|
+
actions: actions,
|
|
39
|
+
loading: store.sourcesLoading || missingFromPage,
|
|
40
|
+
source: source
|
|
41
|
+
}),
|
|
42
|
+
/*#__PURE__*/ _jsx(ConnectionTest, {
|
|
43
|
+
actions: actions,
|
|
44
|
+
loading: store.sourcesLoading || missingFromPage,
|
|
45
|
+
source: source
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ConnectionTab } from '../store/state';
|
|
2
|
+
/** Shared with the panel so `aria-controls` and `aria-labelledby` line up. */
|
|
3
|
+
export declare function connectionTabId(tab: ConnectionTab): string;
|
|
4
|
+
export declare function connectionTabPanelId(tab: ConnectionTab): string;
|
|
5
|
+
export type ConnectionTabsProps = {
|
|
6
|
+
value: ConnectionTab;
|
|
7
|
+
onChange: (tab: ConnectionTab) => void;
|
|
8
|
+
/** Server-reported totals. A zero is left off rather than shown as "0". */
|
|
9
|
+
counts?: Partial<Record<ConnectionTab, number>>;
|
|
10
|
+
label?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function ConnectionTabs({ value, onChange, counts, label, }: ConnectionTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef } from 'react';
|
|
4
|
+
import { cn } from '../../../utils/cn';
|
|
5
|
+
import { integrationsPageTheme } from '../theme';
|
|
6
|
+
/**
|
|
7
|
+
* The strip that switches between a connection's own settings, its endpoints,
|
|
8
|
+
* its mappings and its runs.
|
|
9
|
+
*
|
|
10
|
+
* It is a real ARIA tablist: one tab is in the tab order and the arrow keys move
|
|
11
|
+
* between the rest. A row of separately tabbable buttons would make a keyboard
|
|
12
|
+
* user press Tab three times to leave a control they were never inside.
|
|
13
|
+
*/ const theme = integrationsPageTheme;
|
|
14
|
+
const TABS = [
|
|
15
|
+
{
|
|
16
|
+
value: 'connection',
|
|
17
|
+
label: 'Connection'
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: 'endpoints',
|
|
21
|
+
label: 'Endpoints'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: 'mappings',
|
|
25
|
+
label: 'Mappings'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
value: 'runs',
|
|
29
|
+
label: 'Runs'
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
/** Shared with the panel so `aria-controls` and `aria-labelledby` line up. */ export function connectionTabId(tab) {
|
|
33
|
+
return `integrations-tab-${tab}`;
|
|
34
|
+
}
|
|
35
|
+
export function connectionTabPanelId(tab) {
|
|
36
|
+
return `integrations-panel-${tab}`;
|
|
37
|
+
}
|
|
38
|
+
export function ConnectionTabs({ value, onChange, counts, label = 'Connection sections' }) {
|
|
39
|
+
const buttonsRef = useRef({});
|
|
40
|
+
const select = (index)=>{
|
|
41
|
+
const next = TABS[(index % TABS.length + TABS.length) % TABS.length];
|
|
42
|
+
if (!next) return;
|
|
43
|
+
onChange(next.value);
|
|
44
|
+
// Selection follows focus, so the focus has to follow the selection too —
|
|
45
|
+
// otherwise the next arrow press starts from the tab the user just left.
|
|
46
|
+
buttonsRef.current[next.value]?.focus();
|
|
47
|
+
};
|
|
48
|
+
const handleKeyDown = (event, tab)=>{
|
|
49
|
+
const index = TABS.findIndex((entry)=>entry.value === tab);
|
|
50
|
+
if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
select(index + 1);
|
|
53
|
+
} else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
select(index - 1);
|
|
56
|
+
} else if (event.key === 'Home') {
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
select(0);
|
|
59
|
+
} else if (event.key === 'End') {
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
select(TABS.length - 1);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
65
|
+
"aria-label": label,
|
|
66
|
+
className: theme.tabs.wrapper,
|
|
67
|
+
role: "tablist",
|
|
68
|
+
children: TABS.map((tab)=>{
|
|
69
|
+
const selected = tab.value === value;
|
|
70
|
+
const count = counts?.[tab.value];
|
|
71
|
+
return /*#__PURE__*/ _jsxs("button", {
|
|
72
|
+
"aria-controls": connectionTabPanelId(tab.value),
|
|
73
|
+
"aria-selected": selected,
|
|
74
|
+
className: cn(theme.tabs.button, selected ? theme.tabs.active : theme.tabs.inactive),
|
|
75
|
+
id: connectionTabId(tab.value),
|
|
76
|
+
onClick: ()=>onChange(tab.value),
|
|
77
|
+
onKeyDown: (event)=>handleKeyDown(event, tab.value),
|
|
78
|
+
ref: (node)=>{
|
|
79
|
+
buttonsRef.current[tab.value] = node;
|
|
80
|
+
},
|
|
81
|
+
role: "tab",
|
|
82
|
+
// Roving tabindex: the tablist is ONE stop in the tab order.
|
|
83
|
+
tabIndex: selected ? 0 : -1,
|
|
84
|
+
type: "button",
|
|
85
|
+
children: [
|
|
86
|
+
tab.label,
|
|
87
|
+
typeof count === 'number' && count > 0 ? /*#__PURE__*/ _jsx("span", {
|
|
88
|
+
className: cn(theme.progress.label, 'ml-1.5'),
|
|
89
|
+
children: count
|
|
90
|
+
}) : null
|
|
91
|
+
]
|
|
92
|
+
}, tab.value);
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IntegrationSource, IntegrationsActions } from '../types';
|
|
2
|
+
export type ConnectionTestProps = {
|
|
3
|
+
actions: IntegrationsActions;
|
|
4
|
+
/** The saved connection to call, or null before it exists. */
|
|
5
|
+
source: IntegrationSource | null;
|
|
6
|
+
/** True while that record is still on its way. */
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function ConnectionTest({ actions, source, loading }: ConnectionTestProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useEffectEvent, useState } from 'react';
|
|
4
|
+
import { cn } from '../../../utils/cn';
|
|
5
|
+
import { integrationsPageTheme } from '../theme';
|
|
6
|
+
import { errorText } from './formSupport';
|
|
7
|
+
import { Badge, Notice } from './Primitives';
|
|
8
|
+
import { CardSkeleton } from './Skeletons';
|
|
9
|
+
/**
|
|
10
|
+
* One real call to the service, reported exactly as it came back.
|
|
11
|
+
*
|
|
12
|
+
* The failure text is printed whole. The server already names the setting that
|
|
13
|
+
* has to change — a timeout, a certificate it would not accept, a 401 from the
|
|
14
|
+
* token endpoint — and every word of that is worth more than a tidy summary that
|
|
15
|
+
* sends the operator back to guessing.
|
|
16
|
+
*/ const theme = integrationsPageTheme;
|
|
17
|
+
export function ConnectionTest({ actions, source, loading = false }) {
|
|
18
|
+
const [testing, setTesting] = useState(false);
|
|
19
|
+
const [result, setResult] = useState(null);
|
|
20
|
+
// A result describes the connection it was run against. Leaving it up under
|
|
21
|
+
// the next one would read as a verdict on settings never tested.
|
|
22
|
+
const clear = useEffectEvent(()=>{
|
|
23
|
+
setResult(null);
|
|
24
|
+
setTesting(false);
|
|
25
|
+
});
|
|
26
|
+
useEffect(()=>{
|
|
27
|
+
clear();
|
|
28
|
+
}, [
|
|
29
|
+
source?.id
|
|
30
|
+
]);
|
|
31
|
+
const handleTest = ()=>{
|
|
32
|
+
if (!source) return;
|
|
33
|
+
setTesting(true);
|
|
34
|
+
setResult(null);
|
|
35
|
+
actions.testSource(source.id).then((value)=>{
|
|
36
|
+
setTesting(false);
|
|
37
|
+
setResult(value);
|
|
38
|
+
}).catch((reason)=>{
|
|
39
|
+
setTesting(false);
|
|
40
|
+
setResult({
|
|
41
|
+
ok: false,
|
|
42
|
+
error: errorText(reason, 'The test could not be run.')
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
if (loading) return /*#__PURE__*/ _jsx(CardSkeleton, {});
|
|
47
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
48
|
+
"aria-label": "Connection test",
|
|
49
|
+
className: theme.card.wrapper,
|
|
50
|
+
children: [
|
|
51
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
52
|
+
className: theme.card.header,
|
|
53
|
+
children: [
|
|
54
|
+
/*#__PURE__*/ _jsx("h2", {
|
|
55
|
+
className: theme.card.title,
|
|
56
|
+
children: "Connection test"
|
|
57
|
+
}),
|
|
58
|
+
result ? /*#__PURE__*/ _jsx(Badge, {
|
|
59
|
+
tone: result.ok ? 'success' : 'danger',
|
|
60
|
+
children: result.ok ? 'Reachable' : 'Not reachable'
|
|
61
|
+
}) : null
|
|
62
|
+
]
|
|
63
|
+
}),
|
|
64
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
65
|
+
className: theme.card.body,
|
|
66
|
+
children: [
|
|
67
|
+
source ? /*#__PURE__*/ _jsxs("p", {
|
|
68
|
+
className: theme.field.hint,
|
|
69
|
+
children: [
|
|
70
|
+
"Calls ",
|
|
71
|
+
source.baseUrl,
|
|
72
|
+
" with the settings and credentials as they are stored right now."
|
|
73
|
+
]
|
|
74
|
+
}) : /*#__PURE__*/ _jsx("p", {
|
|
75
|
+
className: theme.field.hint,
|
|
76
|
+
children: "Save the connection first — there is nothing to call yet."
|
|
77
|
+
}),
|
|
78
|
+
result?.ok ? /*#__PURE__*/ _jsxs(Notice, {
|
|
79
|
+
title: "Connection succeeded",
|
|
80
|
+
tone: "info",
|
|
81
|
+
children: [
|
|
82
|
+
/*#__PURE__*/ _jsx("p", {
|
|
83
|
+
children: /*#__PURE__*/ _jsx(Badge, {
|
|
84
|
+
tone: "success",
|
|
85
|
+
children: "Reachable"
|
|
86
|
+
})
|
|
87
|
+
}),
|
|
88
|
+
result.detail ? /*#__PURE__*/ _jsx("p", {
|
|
89
|
+
className: cn(theme.notice.body, 'mt-2 whitespace-pre-wrap break-words font-mono'),
|
|
90
|
+
children: result.detail
|
|
91
|
+
}) : null
|
|
92
|
+
]
|
|
93
|
+
}) : null,
|
|
94
|
+
result && !result.ok ? /*#__PURE__*/ _jsx(Notice, {
|
|
95
|
+
title: "Connection failed",
|
|
96
|
+
tone: "danger",
|
|
97
|
+
children: /*#__PURE__*/ _jsx("p", {
|
|
98
|
+
className: cn(theme.notice.body, 'whitespace-pre-wrap break-words font-mono'),
|
|
99
|
+
children: result.error ?? 'The server reported a failure without a message.'
|
|
100
|
+
})
|
|
101
|
+
}) : null
|
|
102
|
+
]
|
|
103
|
+
}),
|
|
104
|
+
/*#__PURE__*/ _jsx("div", {
|
|
105
|
+
className: theme.card.footer,
|
|
106
|
+
children: /*#__PURE__*/ _jsx("button", {
|
|
107
|
+
className: theme.button.secondary,
|
|
108
|
+
disabled: !source || testing,
|
|
109
|
+
onClick: handleTest,
|
|
110
|
+
type: "button",
|
|
111
|
+
children: testing ? 'Testing…' : 'Test connection'
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
]
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IntegrationSource, IntegrationsActions } from '../types';
|
|
2
|
+
export type CredentialFieldsProps = {
|
|
3
|
+
actions: IntegrationsActions;
|
|
4
|
+
/** The saved connection the secrets belong to, or null before it exists. */
|
|
5
|
+
source: IntegrationSource | null;
|
|
6
|
+
/** True while that record is still on its way. */
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function CredentialFields({ actions, source, loading }: CredentialFieldsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useEffectEvent, useState } from 'react';
|
|
4
|
+
import { useIntegrationsStore } from '../store';
|
|
5
|
+
import { integrationsPageTheme } from '../theme';
|
|
6
|
+
import { CredentialSlotRow } from './CredentialSlotRow';
|
|
7
|
+
import { hintFromResult, patchFor, slotsForSource, storedHint } from './credentialSlots';
|
|
8
|
+
import { errorText } from './formSupport';
|
|
9
|
+
import { Notice } from './Primitives';
|
|
10
|
+
import { CardSkeleton } from './Skeletons';
|
|
11
|
+
/**
|
|
12
|
+
* The secrets, written and never read.
|
|
13
|
+
*
|
|
14
|
+
* Nothing on this screen has ever seen a stored value and nothing here asks for
|
|
15
|
+
* one: the record carries a masked hint, a save returns a new masked hint, and
|
|
16
|
+
* that is the whole conversation. An empty box therefore cannot mean "make it
|
|
17
|
+
* empty" — it means "leave it alone" — which is why clearing is a separate
|
|
18
|
+
* button per field.
|
|
19
|
+
*/ const theme = integrationsPageTheme;
|
|
20
|
+
export function CredentialFields({ actions, source, loading = false }) {
|
|
21
|
+
const store = useIntegrationsStore();
|
|
22
|
+
const [values, setValues] = useState({});
|
|
23
|
+
const [hints, setHints] = useState({});
|
|
24
|
+
const [busy, setBusy] = useState(false);
|
|
25
|
+
const [status, setStatus] = useState(null);
|
|
26
|
+
// A different connection means different secrets. Carrying a typed value
|
|
27
|
+
// across would post it to the wrong service.
|
|
28
|
+
const reset = useEffectEvent(()=>{
|
|
29
|
+
setValues({});
|
|
30
|
+
setHints({});
|
|
31
|
+
setBusy(false);
|
|
32
|
+
setStatus(null);
|
|
33
|
+
});
|
|
34
|
+
useEffect(()=>{
|
|
35
|
+
reset();
|
|
36
|
+
}, [
|
|
37
|
+
source?.id
|
|
38
|
+
]);
|
|
39
|
+
if (loading) return /*#__PURE__*/ _jsx(CardSkeleton, {});
|
|
40
|
+
if (!source) {
|
|
41
|
+
return /*#__PURE__*/ _jsx(Notice, {
|
|
42
|
+
title: "Save the connection first",
|
|
43
|
+
tone: "info",
|
|
44
|
+
children: /*#__PURE__*/ _jsx("p", {
|
|
45
|
+
children: "Secrets are stored against a saved connection, so there is somewhere to put them."
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const sourceRecord = source;
|
|
50
|
+
const slots = slotsForSource(sourceRecord);
|
|
51
|
+
const hintOf = (slot)=>slot in hints ? hints[slot] ?? null : storedHint(sourceRecord, slot);
|
|
52
|
+
const applyHints = (result)=>{
|
|
53
|
+
setHints((current)=>{
|
|
54
|
+
const next = {
|
|
55
|
+
...current
|
|
56
|
+
};
|
|
57
|
+
for (const slot of slots){
|
|
58
|
+
const hint = hintFromResult(result, slot);
|
|
59
|
+
if (hint !== undefined) next[slot] = hint;
|
|
60
|
+
}
|
|
61
|
+
return next;
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
const handleSave = ()=>{
|
|
65
|
+
const payload = {};
|
|
66
|
+
let filled = 0;
|
|
67
|
+
for (const slot of slots){
|
|
68
|
+
const value = values[slot] ?? '';
|
|
69
|
+
if (value.length > 0) {
|
|
70
|
+
payload[slot] = value;
|
|
71
|
+
filled += 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (filled === 0) {
|
|
75
|
+
setStatus('Nothing to save — every box is empty, so the stored values are untouched.');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
setBusy(true);
|
|
79
|
+
setStatus(null);
|
|
80
|
+
store.setError(null);
|
|
81
|
+
actions.saveCredentials(sourceRecord.id, payload).then((result)=>{
|
|
82
|
+
setBusy(false);
|
|
83
|
+
// The typed values are dropped here and not kept anywhere: what was
|
|
84
|
+
// stored can only ever be looked at as the mask below.
|
|
85
|
+
setValues({});
|
|
86
|
+
applyHints(result);
|
|
87
|
+
setStatus('Saved. Only the masked hints below come back.');
|
|
88
|
+
}).catch((reason)=>{
|
|
89
|
+
setBusy(false);
|
|
90
|
+
store.setError(errorText(reason, 'The credentials could not be saved.'));
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
const handleClear = (slot)=>{
|
|
94
|
+
setBusy(true);
|
|
95
|
+
setStatus(null);
|
|
96
|
+
store.setError(null);
|
|
97
|
+
actions.saveCredentials(sourceRecord.id, patchFor(slot, '')).then((result)=>{
|
|
98
|
+
setBusy(false);
|
|
99
|
+
setValues((current)=>({
|
|
100
|
+
...current,
|
|
101
|
+
...patchFor(slot, '')
|
|
102
|
+
}));
|
|
103
|
+
setHints((current)=>{
|
|
104
|
+
const next = {
|
|
105
|
+
...current
|
|
106
|
+
};
|
|
107
|
+
next[slot] = hintFromResult(result, slot) ?? null;
|
|
108
|
+
return next;
|
|
109
|
+
});
|
|
110
|
+
setStatus('Cleared. Nothing is stored for that field any more.');
|
|
111
|
+
}).catch((reason)=>{
|
|
112
|
+
setBusy(false);
|
|
113
|
+
store.setError(errorText(reason, 'The credential could not be cleared.'));
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
117
|
+
"aria-label": "Credentials",
|
|
118
|
+
className: theme.card.wrapper,
|
|
119
|
+
children: [
|
|
120
|
+
/*#__PURE__*/ _jsx("div", {
|
|
121
|
+
className: theme.card.header,
|
|
122
|
+
children: /*#__PURE__*/ _jsx("h2", {
|
|
123
|
+
className: theme.card.title,
|
|
124
|
+
children: "Credentials"
|
|
125
|
+
})
|
|
126
|
+
}),
|
|
127
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
128
|
+
className: theme.card.body,
|
|
129
|
+
children: [
|
|
130
|
+
/*#__PURE__*/ _jsx(Notice, {
|
|
131
|
+
title: "Written once, never read back",
|
|
132
|
+
tone: "info",
|
|
133
|
+
children: /*#__PURE__*/ _jsx("p", {
|
|
134
|
+
children: "A value typed here is stored and from then on only ever sent to the service. It is not returned to this screen, or to any other, no matter who asks — all that comes back is a masked hint. Leave a box empty to keep whatever is already stored."
|
|
135
|
+
})
|
|
136
|
+
}),
|
|
137
|
+
slots.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
138
|
+
className: theme.list.empty,
|
|
139
|
+
children: "This connection is set to no authentication, so there is no secret to store. Pick an auth type above to see the fields it needs."
|
|
140
|
+
}) : null,
|
|
141
|
+
slots.map((slot)=>/*#__PURE__*/ _jsx(CredentialSlotRow, {
|
|
142
|
+
disabled: busy,
|
|
143
|
+
maskedHint: hintOf(slot),
|
|
144
|
+
onChange: (value)=>setValues((current)=>({
|
|
145
|
+
...current,
|
|
146
|
+
...patchFor(slot, value)
|
|
147
|
+
})),
|
|
148
|
+
onClear: ()=>handleClear(slot),
|
|
149
|
+
slot: slot,
|
|
150
|
+
source: sourceRecord,
|
|
151
|
+
value: values[slot] ?? ''
|
|
152
|
+
}, slot)),
|
|
153
|
+
status ? /*#__PURE__*/ _jsx("p", {
|
|
154
|
+
className: theme.field.hint,
|
|
155
|
+
children: status
|
|
156
|
+
}) : null
|
|
157
|
+
]
|
|
158
|
+
}),
|
|
159
|
+
slots.length > 0 ? /*#__PURE__*/ _jsx("div", {
|
|
160
|
+
className: theme.card.footer,
|
|
161
|
+
children: /*#__PURE__*/ _jsx("button", {
|
|
162
|
+
className: theme.button.primary,
|
|
163
|
+
disabled: busy,
|
|
164
|
+
onClick: handleSave,
|
|
165
|
+
type: "button",
|
|
166
|
+
children: busy ? 'Saving…' : 'Save credentials'
|
|
167
|
+
})
|
|
168
|
+
}) : null
|
|
169
|
+
]
|
|
170
|
+
});
|
|
171
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IntegrationSource } from '../types';
|
|
2
|
+
import { type CredentialSlot } from './credentialSlots';
|
|
3
|
+
export type CredentialSlotRowProps = {
|
|
4
|
+
slot: CredentialSlot;
|
|
5
|
+
source: IntegrationSource;
|
|
6
|
+
value: string;
|
|
7
|
+
/** The masked preview of the stored value, or null when nothing is stored. */
|
|
8
|
+
maskedHint: string | null;
|
|
9
|
+
disabled: boolean;
|
|
10
|
+
onChange: (value: string) => void;
|
|
11
|
+
onClear: () => void;
|
|
12
|
+
};
|
|
13
|
+
export declare function CredentialSlotRow({ slot, source, value, maskedHint, disabled, onChange, onClear, }: CredentialSlotRowProps): import("react/jsx-runtime").JSX.Element;
|