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.
Files changed (154) hide show
  1. package/dist/client.js +1 -1
  2. package/dist/fe/components/IntegrationsPage/components/ConnectionAuthFields.d.ts +2 -0
  3. package/dist/fe/components/IntegrationsPage/components/ConnectionAuthFields.js +85 -0
  4. package/dist/fe/components/IntegrationsPage/components/ConnectionBasicFields.d.ts +2 -0
  5. package/dist/fe/components/IntegrationsPage/components/ConnectionBasicFields.js +165 -0
  6. package/dist/fe/components/IntegrationsPage/components/ConnectionForm.d.ts +10 -0
  7. package/dist/fe/components/IntegrationsPage/components/ConnectionForm.js +112 -0
  8. package/dist/fe/components/IntegrationsPage/components/ConnectionList.d.ts +9 -0
  9. package/dist/fe/components/IntegrationsPage/components/ConnectionList.js +184 -0
  10. package/dist/fe/components/IntegrationsPage/components/ConnectionOauthFields.d.ts +2 -0
  11. package/dist/fe/components/IntegrationsPage/components/ConnectionOauthFields.js +131 -0
  12. package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.d.ts +21 -0
  13. package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.js +49 -0
  14. package/dist/fe/components/IntegrationsPage/components/ConnectionTabs.d.ts +12 -0
  15. package/dist/fe/components/IntegrationsPage/components/ConnectionTabs.js +95 -0
  16. package/dist/fe/components/IntegrationsPage/components/ConnectionTest.d.ts +9 -0
  17. package/dist/fe/components/IntegrationsPage/components/ConnectionTest.js +116 -0
  18. package/dist/fe/components/IntegrationsPage/components/CredentialFields.d.ts +9 -0
  19. package/dist/fe/components/IntegrationsPage/components/CredentialFields.js +171 -0
  20. package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.d.ts +13 -0
  21. package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.js +63 -0
  22. package/dist/fe/components/IntegrationsPage/components/EndpointForm.d.ts +11 -0
  23. package/dist/fe/components/IntegrationsPage/components/EndpointForm.js +166 -0
  24. package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.d.ts +19 -0
  25. package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.js +162 -0
  26. package/dist/fe/components/IntegrationsPage/components/EndpointList.d.ts +12 -0
  27. package/dist/fe/components/IntegrationsPage/components/EndpointList.js +205 -0
  28. package/dist/fe/components/IntegrationsPage/components/EndpointSample.d.ts +6 -0
  29. package/dist/fe/components/IntegrationsPage/components/EndpointSample.js +204 -0
  30. package/dist/fe/components/IntegrationsPage/components/EndpointsTab.d.ts +5 -0
  31. package/dist/fe/components/IntegrationsPage/components/EndpointsTab.js +101 -0
  32. package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.d.ts +17 -0
  33. package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.js +179 -0
  34. package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.d.ts +11 -0
  35. package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.js +115 -0
  36. package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.d.ts +2 -0
  37. package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.js +175 -0
  38. package/dist/fe/components/IntegrationsPage/components/MappingEditor.d.ts +15 -0
  39. package/dist/fe/components/IntegrationsPage/components/MappingEditor.js +193 -0
  40. package/dist/fe/components/IntegrationsPage/components/MappingList.d.ts +11 -0
  41. package/dist/fe/components/IntegrationsPage/components/MappingList.js +191 -0
  42. package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.d.ts +19 -0
  43. package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.js +150 -0
  44. package/dist/fe/components/IntegrationsPage/components/MappingsTab.d.ts +5 -0
  45. package/dist/fe/components/IntegrationsPage/components/MappingsTab.js +154 -0
  46. package/dist/fe/components/IntegrationsPage/components/PaginationFields.d.ts +9 -0
  47. package/dist/fe/components/IntegrationsPage/components/PaginationFields.js +218 -0
  48. package/dist/fe/components/IntegrationsPage/components/ParamBindingRow.d.ts +24 -0
  49. package/dist/fe/components/IntegrationsPage/components/ParamBindingRow.js +180 -0
  50. package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.d.ts +33 -0
  51. package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.js +100 -0
  52. package/dist/fe/components/IntegrationsPage/components/Primitives.d.ts +65 -0
  53. package/dist/fe/components/IntegrationsPage/components/Primitives.js +94 -0
  54. package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.d.ts +10 -0
  55. package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.js +48 -0
  56. package/dist/fe/components/IntegrationsPage/components/RunHistory.d.ts +10 -0
  57. package/dist/fe/components/IntegrationsPage/components/RunHistory.js +124 -0
  58. package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.d.ts +7 -0
  59. package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.js +129 -0
  60. package/dist/fe/components/IntegrationsPage/components/RunPanel.d.ts +9 -0
  61. package/dist/fe/components/IntegrationsPage/components/RunPanel.js +160 -0
  62. package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.d.ts +5 -0
  63. package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.js +43 -0
  64. package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.d.ts +5 -0
  65. package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.js +92 -0
  66. package/dist/fe/components/IntegrationsPage/components/RunProgressBar.d.ts +8 -0
  67. package/dist/fe/components/IntegrationsPage/components/RunProgressBar.js +77 -0
  68. package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.d.ts +13 -0
  69. package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.js +65 -0
  70. package/dist/fe/components/IntegrationsPage/components/RunsTab.d.ts +13 -0
  71. package/dist/fe/components/IntegrationsPage/components/RunsTab.js +39 -0
  72. package/dist/fe/components/IntegrationsPage/components/Skeletons.d.ts +9 -0
  73. package/dist/fe/components/IntegrationsPage/components/Skeletons.js +139 -0
  74. package/dist/fe/components/IntegrationsPage/components/authOptions.d.ts +29 -0
  75. package/dist/fe/components/IntegrationsPage/components/authOptions.js +71 -0
  76. package/dist/fe/components/IntegrationsPage/components/connectionDraft.d.ts +64 -0
  77. package/dist/fe/components/IntegrationsPage/components/connectionDraft.js +112 -0
  78. package/dist/fe/components/IntegrationsPage/components/credentialSlots.d.ts +25 -0
  79. package/dist/fe/components/IntegrationsPage/components/credentialSlots.js +67 -0
  80. package/dist/fe/components/IntegrationsPage/components/fieldMappingOptions.d.ts +16 -0
  81. package/dist/fe/components/IntegrationsPage/components/fieldMappingOptions.js +41 -0
  82. package/dist/fe/components/IntegrationsPage/components/formSupport.d.ts +19 -0
  83. package/dist/fe/components/IntegrationsPage/components/formSupport.js +25 -0
  84. package/dist/fe/components/IntegrationsPage/components/mappingDraft.d.ts +40 -0
  85. package/dist/fe/components/IntegrationsPage/components/mappingDraft.js +44 -0
  86. package/dist/fe/components/IntegrationsPage/components/pathParams.d.ts +9 -0
  87. package/dist/fe/components/IntegrationsPage/components/pathParams.js +15 -0
  88. package/dist/fe/components/IntegrationsPage/components/runFormat.d.ts +33 -0
  89. package/dist/fe/components/IntegrationsPage/components/runFormat.js +85 -0
  90. package/dist/fe/components/IntegrationsPage/components/useInfiniteList.d.ts +40 -0
  91. package/dist/fe/components/IntegrationsPage/components/useInfiniteList.js +110 -0
  92. package/dist/fe/components/IntegrationsPage/index.d.ts +18 -0
  93. package/dist/fe/components/IntegrationsPage/index.js +11 -0
  94. package/dist/fe/components/IntegrationsPage/store/index.d.ts +2 -0
  95. package/dist/fe/components/IntegrationsPage/store/index.js +124 -0
  96. package/dist/fe/components/IntegrationsPage/store/state.d.ts +80 -0
  97. package/dist/fe/components/IntegrationsPage/store/state.js +32 -0
  98. package/dist/fe/components/IntegrationsPage/theme/index.d.ts +103 -0
  99. package/dist/fe/components/IntegrationsPage/theme/index.js +113 -0
  100. package/dist/fe/components/IntegrationsPage/types/actions.d.ts +91 -0
  101. package/dist/fe/components/IntegrationsPage/types/actions.js +1 -0
  102. package/dist/fe/components/IntegrationsPage/types/index.d.ts +3 -0
  103. package/dist/fe/components/IntegrationsPage/types/index.js +3 -0
  104. package/dist/fe/components/IntegrationsPage/types/records.d.ts +139 -0
  105. package/dist/fe/components/IntegrationsPage/types/records.js +11 -0
  106. package/dist/fe/components/IntegrationsPage/types/results.d.ts +52 -0
  107. package/dist/fe/components/IntegrationsPage/types/results.js +4 -0
  108. package/dist/fe/index.d.ts +2 -0
  109. package/dist/fe/index.js +1 -0
  110. package/dist/index.js +14 -14
  111. package/dist/src/Client/ApiCaller/system-tables.d.ts +505 -0
  112. package/dist/src/Client/Proxy/wsProxy.js +25 -1
  113. package/dist/src/ElysiaPlugin/routes/backup/types.d.ts +3 -1
  114. package/dist/src/ElysiaPlugin/routes/integrations/index.d.ts +49 -0
  115. package/dist/src/ElysiaPlugin/routes/integrations/inspect.d.ts +87 -0
  116. package/dist/src/ElysiaPlugin/routes/integrations/inspectRoutes.d.ts +41 -0
  117. package/dist/src/ElysiaPlugin/routes/integrations/repository.d.ts +88 -0
  118. package/dist/src/ElysiaPlugin/routes/integrations/types.d.ts +22 -0
  119. package/dist/src/ElysiaPlugin/routes/pubsub/frame.d.ts +37 -0
  120. package/dist/src/ElysiaPlugin/routes/pubsub/frame.test.d.ts +1 -0
  121. package/dist/src/Services/Backup/BackupService.d.ts +16 -8
  122. package/dist/src/Services/Backup/types.d.ts +25 -0
  123. package/dist/src/Services/Integrations/bindings.d.ts +116 -0
  124. package/dist/src/Services/Integrations/bindings.test.d.ts +1 -0
  125. package/dist/src/Services/Integrations/collect.d.ts +68 -0
  126. package/dist/src/Services/Integrations/collect.test.d.ts +1 -0
  127. package/dist/src/Services/Integrations/fetcher.d.ts +100 -0
  128. package/dist/src/Services/Integrations/fetcher.test.d.ts +1 -0
  129. package/dist/src/Services/Integrations/lock.d.ts +77 -0
  130. package/dist/src/Services/Integrations/lock.test.d.ts +1 -0
  131. package/dist/src/Services/Integrations/mapping.d.ts +63 -0
  132. package/dist/src/Services/Integrations/mapping.test.d.ts +1 -0
  133. package/dist/src/Services/Integrations/oauth.d.ts +89 -0
  134. package/dist/src/Services/Integrations/oauth.test.d.ts +1 -0
  135. package/dist/src/Services/Integrations/pagination.d.ts +97 -0
  136. package/dist/src/Services/Integrations/pagination.test.d.ts +1 -0
  137. package/dist/src/Services/Integrations/plan.d.ts +74 -0
  138. package/dist/src/Services/Integrations/plan.test.d.ts +1 -0
  139. package/dist/src/Services/Integrations/revert.d.ts +78 -0
  140. package/dist/src/Services/Integrations/revert.test.d.ts +1 -0
  141. package/dist/src/Services/Integrations/runner.d.ts +126 -0
  142. package/dist/src/Services/Integrations/runner.test.d.ts +1 -0
  143. package/dist/src/Services/Integrations/types.d.ts +208 -0
  144. package/dist/src/Services/Integrations/writer.d.ts +105 -0
  145. package/dist/src/Services/Integrations/writer.test.d.ts +1 -0
  146. package/dist/src/Utils/cron.d.ts +65 -0
  147. package/dist/src/Utils/cron.test.d.ts +1 -0
  148. package/dist/src/types.d.ts +67 -1
  149. package/package.json +1 -1
  150. package/schemas/config.nucleus.json +43 -0
  151. package/schemas/table.schema.json +2 -1
  152. package/scripts/generate-schema.test.ts +78 -0
  153. package/scripts/generate-schema.ts +43 -5
  154. 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;