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,63 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from '../../../utils/cn';
4
+ import { integrationsPageTheme } from '../theme';
5
+ import { slotHint, slotLabel } from './credentialSlots';
6
+ import { describedBy } from './formSupport';
7
+ import { Badge, Field } from './Primitives';
8
+ /**
9
+ * One secret: a box to type a new value into, the masked preview of whatever is
10
+ * already stored, and the only way to remove it.
11
+ *
12
+ * Clearing is its own button rather than "save an empty box", because an empty
13
+ * box is what an operator leaves behind when they came to change something else.
14
+ * Making that mean "delete the credential" would break a working connection by
15
+ * accident, roughly once.
16
+ */ const theme = integrationsPageTheme;
17
+ export function CredentialSlotRow({ slot, source, value, maskedHint, disabled, onChange, onClear }) {
18
+ const id = `credential-${slot}`;
19
+ const label = slotLabel(slot, source);
20
+ const stored = maskedHint !== null && maskedHint.length > 0;
21
+ return /*#__PURE__*/ _jsxs("div", {
22
+ className: "flex flex-col gap-2",
23
+ children: [
24
+ /*#__PURE__*/ _jsx(Field, {
25
+ hint: slotHint(slot, source),
26
+ htmlFor: id,
27
+ label: label,
28
+ children: /*#__PURE__*/ _jsx("input", {
29
+ "aria-describedby": describedBy(id, true, false),
30
+ autoComplete: "new-password",
31
+ className: theme.field.input,
32
+ disabled: disabled,
33
+ id: id,
34
+ onChange: (event)=>onChange(event.target.value),
35
+ placeholder: stored ? 'Leave empty to keep the stored value' : 'Nothing stored yet',
36
+ type: "password",
37
+ value: value
38
+ })
39
+ }),
40
+ /*#__PURE__*/ _jsxs("div", {
41
+ className: "flex flex-wrap items-center gap-2",
42
+ children: [
43
+ /*#__PURE__*/ _jsx(Badge, {
44
+ tone: stored ? 'success' : 'neutral',
45
+ children: stored ? 'Stored' : 'Not set'
46
+ }),
47
+ stored ? /*#__PURE__*/ _jsx("span", {
48
+ className: cn(theme.field.hint, 'truncate font-mono'),
49
+ children: maskedHint
50
+ }) : null,
51
+ /*#__PURE__*/ _jsx("button", {
52
+ "aria-label": `Clear the stored ${label.toLowerCase()}`,
53
+ className: cn(theme.button.ghost, 'ml-auto'),
54
+ disabled: disabled || !stored,
55
+ onClick: onClear,
56
+ type: "button",
57
+ children: "Clear"
58
+ })
59
+ ]
60
+ })
61
+ ]
62
+ });
63
+ }
@@ -0,0 +1,11 @@
1
+ import type { IntegrationEndpoint, IntegrationsActions } from '../types';
2
+ export type EndpointFormProps = {
3
+ sourceId: string;
4
+ /** Null builds a new endpoint. */
5
+ endpoint: IntegrationEndpoint | null;
6
+ actions: IntegrationsActions;
7
+ onSaved: (endpoint: IntegrationEndpoint) => void;
8
+ onDeleted: () => void;
9
+ onCancel: () => void;
10
+ };
11
+ export declare function EndpointForm({ sourceId, endpoint, actions, onSaved, onDeleted, onCancel, }: EndpointFormProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,166 @@
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 { EndpointFormFields } from './EndpointFormFields';
7
+ import { PaginationFields } from './PaginationFields';
8
+ import { ParamBindingRows } from './ParamBindingRows';
9
+ import { pathParamNames } from './pathParams';
10
+ /**
11
+ * One call against a connection: what to ask for, and where the records sit in
12
+ * the answer.
13
+ *
14
+ * The draft is seeded once. The parent remounts this form when the selection
15
+ * changes, so a half-typed path is never pulled out from under the operator by
16
+ * a list page arriving in the background.
17
+ */ const theme = integrationsPageTheme;
18
+ export function EndpointForm({ sourceId, endpoint, actions, onSaved, onDeleted, onCancel }) {
19
+ const store = useIntegrationsStore();
20
+ const [saving, setSaving] = useState(false);
21
+ const [deleting, setDeleting] = useState(false);
22
+ const [draft, setDraft] = useState({
23
+ tag: endpoint?.tag ?? '',
24
+ name: endpoint?.name ?? '',
25
+ method: endpoint?.method ?? 'GET',
26
+ path: endpoint?.path ?? '',
27
+ description: endpoint?.description ?? '',
28
+ responseRootPath: endpoint?.responseRootPath ?? '',
29
+ enabled: endpoint?.enabled ?? true,
30
+ pagination: endpoint?.pagination ?? {
31
+ kind: 'none'
32
+ },
33
+ paramBindings: endpoint?.paramBindings ?? []
34
+ });
35
+ // Which tables a binding may read a column from. Loaded once; the list is
36
+ // small and an operator picking a parameter source should not wait for it.
37
+ const [targets, setTargets] = useState([]);
38
+ const loadTargets = useEffectEvent(()=>{
39
+ actions.listTargets().then(setTargets).catch(()=>{
40
+ // A binding can still be made against another endpoint or a fixed value,
41
+ // so this is a reduced menu rather than a broken screen.
42
+ setTargets([]);
43
+ });
44
+ });
45
+ useEffect(()=>{
46
+ loadTargets();
47
+ }, []);
48
+ const set = (patch)=>{
49
+ setDraft((previous)=>({
50
+ ...previous,
51
+ ...patch
52
+ }));
53
+ };
54
+ const fail = (fallback)=>(error)=>{
55
+ setSaving(false);
56
+ setDeleting(false);
57
+ store.setError(error instanceof Error ? error.message : fallback);
58
+ };
59
+ const tag = draft.tag.trim();
60
+ const path = draft.path.trim();
61
+ const busy = saving || deleting;
62
+ const canSave = tag.length > 0 && path.length > 0;
63
+ const handleSubmit = (event)=>{
64
+ event.preventDefault();
65
+ if (!canSave || busy) return;
66
+ save();
67
+ };
68
+ const save = ()=>{
69
+ setSaving(true);
70
+ store.setError(null);
71
+ actions.saveEndpoint({
72
+ id: endpoint?.id,
73
+ sourceId,
74
+ tag,
75
+ name: draft.name.trim() || null,
76
+ method: draft.method,
77
+ path,
78
+ description: draft.description.trim() || null,
79
+ responseRootPath: draft.responseRootPath.trim() || null,
80
+ pagination: draft.pagination,
81
+ paramBindings: draft.paramBindings,
82
+ enabled: draft.enabled
83
+ }).then((saved)=>{
84
+ setSaving(false);
85
+ if (saved) onSaved(saved);
86
+ else store.setError('The server did not return the saved endpoint.');
87
+ }).catch(fail('Saving the endpoint failed.'));
88
+ };
89
+ const remove = ()=>{
90
+ if (!endpoint) return;
91
+ setDeleting(true);
92
+ store.setError(null);
93
+ actions.deleteEndpoint(endpoint.id).then((ok)=>{
94
+ setDeleting(false);
95
+ if (ok) onDeleted();
96
+ else store.setError('The endpoint was not deleted.');
97
+ }).catch(fail('Deleting the endpoint failed.'));
98
+ };
99
+ return /*#__PURE__*/ _jsxs("form", {
100
+ className: theme.card.wrapper,
101
+ noValidate: true,
102
+ onSubmit: handleSubmit,
103
+ children: [
104
+ /*#__PURE__*/ _jsx("header", {
105
+ className: theme.card.header,
106
+ children: /*#__PURE__*/ _jsx("h3", {
107
+ className: theme.card.title,
108
+ children: endpoint ? 'Edit endpoint' : 'New endpoint'
109
+ })
110
+ }),
111
+ /*#__PURE__*/ _jsxs("div", {
112
+ className: theme.card.body,
113
+ children: [
114
+ /*#__PURE__*/ _jsx(EndpointFormFields, {
115
+ disabled: busy,
116
+ draft: draft,
117
+ onChange: set
118
+ }),
119
+ /*#__PURE__*/ _jsx(PaginationFields, {
120
+ disabled: busy,
121
+ idPrefix: "endpoint-pagination",
122
+ onChange: (pagination)=>set({
123
+ pagination
124
+ }),
125
+ value: draft.pagination
126
+ }),
127
+ /*#__PURE__*/ _jsx(ParamBindingRows, {
128
+ disabled: busy,
129
+ entities: targets,
130
+ onChange: (paramBindings)=>set({
131
+ paramBindings
132
+ }),
133
+ required: pathParamNames(path),
134
+ siblings: store.endpoints.filter((candidate)=>candidate.id !== endpoint?.id),
135
+ value: draft.paramBindings
136
+ })
137
+ ]
138
+ }),
139
+ /*#__PURE__*/ _jsxs("footer", {
140
+ className: theme.card.footer,
141
+ children: [
142
+ endpoint ? /*#__PURE__*/ _jsx("button", {
143
+ className: theme.button.danger,
144
+ disabled: busy,
145
+ onClick: remove,
146
+ type: "button",
147
+ children: deleting ? 'Deleting…' : 'Delete'
148
+ }) : null,
149
+ /*#__PURE__*/ _jsx("button", {
150
+ className: theme.button.secondary,
151
+ disabled: busy,
152
+ onClick: onCancel,
153
+ type: "button",
154
+ children: "Cancel"
155
+ }),
156
+ /*#__PURE__*/ _jsx("button", {
157
+ className: theme.button.primary,
158
+ disabled: busy || !canSave,
159
+ type: "submit",
160
+ children: saving ? 'Saving…' : 'Save endpoint'
161
+ })
162
+ ]
163
+ })
164
+ ]
165
+ });
166
+ }
@@ -0,0 +1,19 @@
1
+ import type { IntegrationEndpoint, PaginationProfileValue, ParamBindingValue } from '../types';
2
+ export type EndpointDraft = {
3
+ tag: string;
4
+ name: string;
5
+ method: IntegrationEndpoint['method'];
6
+ path: string;
7
+ description: string;
8
+ responseRootPath: string;
9
+ enabled: boolean;
10
+ pagination: PaginationProfileValue;
11
+ /** Where each path/query parameter of this endpoint gets its value. */
12
+ paramBindings: ParamBindingValue[];
13
+ };
14
+ export type EndpointFormFieldsProps = {
15
+ draft: EndpointDraft;
16
+ disabled?: boolean;
17
+ onChange: (patch: Partial<EndpointDraft>) => void;
18
+ };
19
+ export declare function EndpointFormFields({ draft, disabled, onChange }: EndpointFormFieldsProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,162 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { cn } from '../../../utils/cn';
4
+ import { integrationsPageTheme } from '../theme';
5
+ import { Field } from './Primitives';
6
+ /**
7
+ * The plain fields of an endpoint.
8
+ *
9
+ * Split out of the form because the form's job is saving and deleting, and this
10
+ * one's is asking. The draft type lives here, next to the controls that fill it.
11
+ */ const theme = integrationsPageTheme;
12
+ const METHODS = [
13
+ 'GET',
14
+ 'POST',
15
+ 'PUT',
16
+ 'PATCH',
17
+ 'DELETE'
18
+ ];
19
+ export function EndpointFormFields({ draft, disabled, onChange }) {
20
+ return /*#__PURE__*/ _jsxs(_Fragment, {
21
+ children: [
22
+ /*#__PURE__*/ _jsxs("div", {
23
+ className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
24
+ children: [
25
+ /*#__PURE__*/ _jsx(Field, {
26
+ hint: "Groups the endpoints of one API.",
27
+ htmlFor: "endpoint-tag",
28
+ label: "Tag",
29
+ children: /*#__PURE__*/ _jsx("input", {
30
+ "aria-describedby": "endpoint-tag-hint",
31
+ className: theme.field.input,
32
+ disabled: disabled,
33
+ id: "endpoint-tag",
34
+ onChange: (event)=>onChange({
35
+ tag: event.target.value
36
+ }),
37
+ placeholder: "directory",
38
+ type: "text",
39
+ value: draft.tag
40
+ })
41
+ }),
42
+ /*#__PURE__*/ _jsx(Field, {
43
+ htmlFor: "endpoint-name",
44
+ label: "Name",
45
+ children: /*#__PURE__*/ _jsx("input", {
46
+ className: theme.field.input,
47
+ disabled: disabled,
48
+ id: "endpoint-name",
49
+ onChange: (event)=>onChange({
50
+ name: event.target.value
51
+ }),
52
+ placeholder: "List people",
53
+ type: "text",
54
+ value: draft.name
55
+ })
56
+ })
57
+ ]
58
+ }),
59
+ /*#__PURE__*/ _jsxs("div", {
60
+ className: "flex flex-col gap-3 sm:flex-row",
61
+ children: [
62
+ /*#__PURE__*/ _jsx("div", {
63
+ className: "sm:w-32",
64
+ children: /*#__PURE__*/ _jsx(Field, {
65
+ htmlFor: "endpoint-method",
66
+ label: "Method",
67
+ children: /*#__PURE__*/ _jsx("select", {
68
+ className: theme.field.input,
69
+ disabled: disabled,
70
+ id: "endpoint-method",
71
+ onChange: (event)=>{
72
+ for (const method of METHODS){
73
+ if (method === event.target.value) onChange({
74
+ method
75
+ });
76
+ }
77
+ },
78
+ value: draft.method,
79
+ children: METHODS.map((method)=>/*#__PURE__*/ _jsx("option", {
80
+ value: method,
81
+ children: method
82
+ }, method))
83
+ })
84
+ })
85
+ }),
86
+ /*#__PURE__*/ _jsx("div", {
87
+ className: "min-w-0 flex-1",
88
+ children: /*#__PURE__*/ _jsx(Field, {
89
+ hint: "{brace} and :colon path parameters are supported — parameter bindings supply their values.",
90
+ htmlFor: "endpoint-path",
91
+ label: "Path",
92
+ children: /*#__PURE__*/ _jsx("input", {
93
+ "aria-describedby": "endpoint-path-hint",
94
+ className: theme.field.input,
95
+ disabled: disabled,
96
+ id: "endpoint-path",
97
+ onChange: (event)=>onChange({
98
+ path: event.target.value
99
+ }),
100
+ placeholder: "/v1/people/{id}",
101
+ type: "text",
102
+ value: draft.path
103
+ })
104
+ })
105
+ })
106
+ ]
107
+ }),
108
+ /*#__PURE__*/ _jsx(Field, {
109
+ hint: "Dotted path to the array of records. Leave it empty when the array is the response root.",
110
+ htmlFor: "endpoint-root",
111
+ label: "Response root path",
112
+ children: /*#__PURE__*/ _jsx("input", {
113
+ "aria-describedby": "endpoint-root-hint",
114
+ className: theme.field.input,
115
+ disabled: disabled,
116
+ id: "endpoint-root",
117
+ onChange: (event)=>onChange({
118
+ responseRootPath: event.target.value
119
+ }),
120
+ placeholder: "data.items",
121
+ type: "text",
122
+ value: draft.responseRootPath
123
+ })
124
+ }),
125
+ /*#__PURE__*/ _jsx(Field, {
126
+ htmlFor: "endpoint-description",
127
+ label: "Description",
128
+ children: /*#__PURE__*/ _jsx("textarea", {
129
+ className: cn(theme.field.input, 'h-auto min-h-20 py-2'),
130
+ disabled: disabled,
131
+ id: "endpoint-description",
132
+ onChange: (event)=>onChange({
133
+ description: event.target.value
134
+ }),
135
+ placeholder: "What this call returns, in one line",
136
+ rows: 3,
137
+ value: draft.description
138
+ })
139
+ }),
140
+ /*#__PURE__*/ _jsxs("label", {
141
+ className: "flex items-center gap-2",
142
+ htmlFor: "endpoint-enabled",
143
+ children: [
144
+ /*#__PURE__*/ _jsx("input", {
145
+ checked: draft.enabled,
146
+ className: "size-4 shrink-0 accent-sky-600 dark:accent-sky-500",
147
+ disabled: disabled,
148
+ id: "endpoint-enabled",
149
+ onChange: (event)=>onChange({
150
+ enabled: event.target.checked
151
+ }),
152
+ type: "checkbox"
153
+ }),
154
+ /*#__PURE__*/ _jsx("span", {
155
+ className: theme.field.label,
156
+ children: "Enabled — a disabled endpoint is never called"
157
+ })
158
+ ]
159
+ })
160
+ ]
161
+ });
162
+ }
@@ -0,0 +1,12 @@
1
+ import type { IntegrationEndpoint, IntegrationsActions } from '../types';
2
+ export type EndpointListProps = {
3
+ sourceId: string;
4
+ actions: IntegrationsActions;
5
+ /** Asked for a blank form. The selection is cleared before this runs. */
6
+ onNewEndpoint?: () => void;
7
+ /** Handed the whole record so the form beside it needs no second fetch. */
8
+ onSelect?: (endpoint: IntegrationEndpoint) => void;
9
+ /** Bump after a save or a delete to pull the list again. */
10
+ reloadToken?: number;
11
+ };
12
+ export declare function EndpointList({ sourceId, actions, onNewEndpoint, onSelect, reloadToken, }: EndpointListProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,205 @@
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 { useIntegrationsStore } from '../store';
6
+ import { integrationsPageTheme } from '../theme';
7
+ import { Badge, Field, Notice } from './Primitives';
8
+ import { ListSkeleton } from './Skeletons';
9
+ import { useInfiniteList } from './useInfiniteList';
10
+ /**
11
+ * The calls one connection knows how to make.
12
+ *
13
+ * The server owns the list: the search term goes back out on the next request
14
+ * and the next page arrives when the sentinel scrolls into view. Nothing here
15
+ * narrows rows already fetched, so a connection with six endpoints and one with
16
+ * six hundred behave identically.
17
+ */ const theme = integrationsPageTheme;
18
+ const PAGE_SIZE = 20;
19
+ const SEARCH_DEBOUNCE_MS = 300;
20
+ const METHOD_TONE = {
21
+ GET: 'info',
22
+ POST: 'success',
23
+ PUT: 'warning',
24
+ PATCH: 'warning',
25
+ DELETE: 'danger'
26
+ };
27
+ export function EndpointList({ sourceId, actions, onNewEndpoint, onSelect, reloadToken = 0 }) {
28
+ const store = useIntegrationsStore();
29
+ const search = store.endpointsSearch;
30
+ const selectedId = store.selectedEndpointId;
31
+ const [term, setTerm] = useState(search);
32
+ // Typed characters become a request only once the typing stops; a fetch per
33
+ // keystroke would put page-1 answers in a race with each other.
34
+ const commitSearch = useEffectEvent(()=>{
35
+ store.setEndpointsSearch(term);
36
+ });
37
+ useEffect(()=>{
38
+ const timer = setTimeout(commitSearch, SEARCH_DEBOUNCE_MS);
39
+ return ()=>clearTimeout(timer);
40
+ }, [
41
+ term
42
+ ]);
43
+ const list = useInfiniteList({
44
+ load: (page)=>actions.listEndpoints(sourceId, {
45
+ page,
46
+ limit: PAGE_SIZE,
47
+ search: search.length > 0 ? search : undefined,
48
+ sort: [
49
+ {
50
+ field: 'tag',
51
+ direction: 'asc'
52
+ }
53
+ ]
54
+ }).then((result)=>({
55
+ items: result.items,
56
+ total: result.meta?.totalItems ?? result.items.length
57
+ })),
58
+ deps: [
59
+ sourceId,
60
+ search,
61
+ reloadToken
62
+ ],
63
+ pageSize: PAGE_SIZE
64
+ });
65
+ // Mirrored so the mapping screens can name an endpoint without fetching it a
66
+ // second time. The rows themselves still come from, and only from, the server.
67
+ const publishItems = useEffectEvent(()=>{
68
+ store.setEndpoints(list.items, list.total);
69
+ });
70
+ const publishLoading = useEffectEvent(()=>{
71
+ store.setEndpointsLoading(list.loading);
72
+ });
73
+ useEffect(()=>{
74
+ publishItems();
75
+ }, [
76
+ list.items,
77
+ list.total
78
+ ]);
79
+ useEffect(()=>{
80
+ publishLoading();
81
+ }, [
82
+ list.loading
83
+ ]);
84
+ const handleNew = ()=>{
85
+ store.selectEndpoint(null);
86
+ onNewEndpoint?.();
87
+ };
88
+ const handleSelect = (endpoint)=>{
89
+ store.selectEndpoint(endpoint.id);
90
+ onSelect?.(endpoint);
91
+ };
92
+ const firstLoad = list.loading && list.items.length === 0;
93
+ return /*#__PURE__*/ _jsxs("section", {
94
+ "aria-label": "Endpoints",
95
+ className: theme.layout.aside,
96
+ children: [
97
+ /*#__PURE__*/ _jsxs("div", {
98
+ className: theme.header.wrapper,
99
+ children: [
100
+ /*#__PURE__*/ _jsxs("div", {
101
+ children: [
102
+ /*#__PURE__*/ _jsx("h2", {
103
+ className: theme.header.title,
104
+ children: "Endpoints"
105
+ }),
106
+ /*#__PURE__*/ _jsx("p", {
107
+ className: theme.header.subtitle,
108
+ children: list.total === 1 ? '1 endpoint' : `${list.total} endpoints`
109
+ })
110
+ ]
111
+ }),
112
+ /*#__PURE__*/ _jsx("div", {
113
+ className: theme.header.actions,
114
+ children: /*#__PURE__*/ _jsx("button", {
115
+ className: theme.button.primary,
116
+ onClick: handleNew,
117
+ type: "button",
118
+ children: "New endpoint"
119
+ })
120
+ })
121
+ ]
122
+ }),
123
+ /*#__PURE__*/ _jsx(Field, {
124
+ htmlFor: "endpoint-search",
125
+ label: "Search",
126
+ children: /*#__PURE__*/ _jsx("input", {
127
+ className: theme.field.input,
128
+ id: "endpoint-search",
129
+ onChange: (event)=>setTerm(event.target.value),
130
+ placeholder: "Tag, name or path",
131
+ type: "search",
132
+ value: term
133
+ })
134
+ }),
135
+ firstLoad ? /*#__PURE__*/ _jsx(ListSkeleton, {
136
+ rows: 6
137
+ }) : null,
138
+ list.error ? /*#__PURE__*/ _jsxs(Notice, {
139
+ title: "This list could not be loaded",
140
+ tone: "danger",
141
+ children: [
142
+ list.error,
143
+ ' ',
144
+ /*#__PURE__*/ _jsx("button", {
145
+ className: theme.button.ghost,
146
+ onClick: list.reload,
147
+ type: "button",
148
+ children: "Try again"
149
+ })
150
+ ]
151
+ }) : null,
152
+ !firstLoad && list.items.length === 0 && !list.error ? /*#__PURE__*/ _jsx("p", {
153
+ className: theme.list.empty,
154
+ children: search.length > 0 ? `No endpoint matches “${search}”. Clear the search to see them all.` : 'No endpoints yet. An endpoint is one call this connection can make — a method, a path, and where the records sit in the answer. Start with New endpoint.'
155
+ }) : null,
156
+ list.items.length > 0 ? /*#__PURE__*/ _jsx("ul", {
157
+ className: theme.list.wrapper,
158
+ children: list.items.map((endpoint)=>/*#__PURE__*/ _jsx("li", {
159
+ children: /*#__PURE__*/ _jsxs("button", {
160
+ "aria-current": endpoint.id === selectedId ? 'true' : undefined,
161
+ className: cn(theme.list.item, endpoint.id === selectedId && theme.list.itemSelected),
162
+ onClick: ()=>handleSelect(endpoint),
163
+ type: "button",
164
+ children: [
165
+ /*#__PURE__*/ _jsxs("span", {
166
+ className: "flex w-full flex-wrap items-center gap-1.5",
167
+ children: [
168
+ /*#__PURE__*/ _jsx(Badge, {
169
+ tone: METHOD_TONE[endpoint.method],
170
+ children: endpoint.method
171
+ }),
172
+ /*#__PURE__*/ _jsx(Badge, {
173
+ tone: "neutral",
174
+ children: endpoint.tag
175
+ }),
176
+ endpoint.enabled === false ? /*#__PURE__*/ _jsx(Badge, {
177
+ tone: "neutral",
178
+ children: "Off"
179
+ }) : null
180
+ ]
181
+ }),
182
+ /*#__PURE__*/ _jsx("span", {
183
+ className: theme.list.itemTitle,
184
+ children: endpoint.name ?? endpoint.path
185
+ }),
186
+ /*#__PURE__*/ _jsx("span", {
187
+ className: theme.list.itemMeta,
188
+ children: endpoint.path
189
+ })
190
+ ]
191
+ })
192
+ }, endpoint.id))
193
+ }) : null,
194
+ /*#__PURE__*/ _jsx("div", {
195
+ "aria-hidden": "true",
196
+ className: theme.list.sentinel,
197
+ ref: list.sentinelRef
198
+ }),
199
+ list.loading && list.items.length > 0 ? /*#__PURE__*/ _jsx("p", {
200
+ className: theme.field.hint,
201
+ children: "Loading more…"
202
+ }) : null
203
+ ]
204
+ });
205
+ }
@@ -0,0 +1,6 @@
1
+ import type { IntegrationEndpoint, IntegrationsActions } from '../types';
2
+ export type EndpointSampleProps = {
3
+ endpoint: IntegrationEndpoint;
4
+ actions: IntegrationsActions;
5
+ };
6
+ export declare function EndpointSample({ endpoint, actions }: EndpointSampleProps): import("react/jsx-runtime").JSX.Element;