nucleus-core-ts 0.10.147 → 0.10.151

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 (204) hide show
  1. package/LICENSE +48 -48
  2. package/dist/.build-ok +1 -1
  3. package/dist/fe/components/AuthorizationPage/labels.js +13 -13
  4. package/dist/fe/components/Captcha/labels.js +17 -17
  5. package/dist/fe/components/ChatPanel/helpers.js +17 -17
  6. package/dist/fe/components/ChatPanel/labels.js +16 -16
  7. package/dist/fe/components/DataTable/labels.js +16 -16
  8. package/dist/fe/components/DevicesPage/components/DeviceCard.js +15 -15
  9. package/dist/fe/components/DevicesPage/components/DevicesPage.js +9 -9
  10. package/dist/fe/components/DevicesPage/labels.js +12 -12
  11. package/dist/fe/components/ForgotPasswordPage/labels.js +8 -8
  12. package/dist/fe/components/FormBuilder/components/FormField.js +5 -5
  13. package/dist/fe/components/FormBuilder/utils/fieldMapping.js +9 -9
  14. package/dist/fe/components/IntegrationsPage/components/ConnectionAuthFields.js +8 -8
  15. package/dist/fe/components/IntegrationsPage/components/ConnectionBasicFields.js +3 -3
  16. package/dist/fe/components/IntegrationsPage/components/ConnectionExport.js +23 -23
  17. package/dist/fe/components/IntegrationsPage/components/ConnectionForm.js +22 -22
  18. package/dist/fe/components/IntegrationsPage/components/ConnectionList.js +7 -7
  19. package/dist/fe/components/IntegrationsPage/components/ConnectionOauthFields.js +6 -6
  20. package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.js +24 -24
  21. package/dist/fe/components/IntegrationsPage/components/ConnectionSwitcher.js +19 -19
  22. package/dist/fe/components/IntegrationsPage/components/ConnectionTabs.js +15 -15
  23. package/dist/fe/components/IntegrationsPage/components/ConnectionTest.js +21 -21
  24. package/dist/fe/components/IntegrationsPage/components/ConnectionTransfer.js +18 -18
  25. package/dist/fe/components/IntegrationsPage/components/CredentialFields.js +19 -19
  26. package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.js +8 -8
  27. package/dist/fe/components/IntegrationsPage/components/EndpointForm.js +7 -7
  28. package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.js +12 -12
  29. package/dist/fe/components/IntegrationsPage/components/EndpointList.js +7 -7
  30. package/dist/fe/components/IntegrationsPage/components/EndpointSample.js +36 -36
  31. package/dist/fe/components/IntegrationsPage/components/EndpointsTab.js +29 -29
  32. package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.js +7 -7
  33. package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.js +6 -6
  34. package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.js +60 -60
  35. package/dist/fe/components/IntegrationsPage/components/MappingAccountFields.js +15 -15
  36. package/dist/fe/components/IntegrationsPage/components/MappingEditor.js +9 -9
  37. package/dist/fe/components/IntegrationsPage/components/MappingList.js +23 -23
  38. package/dist/fe/components/IntegrationsPage/components/MappingPasswordField.js +12 -12
  39. package/dist/fe/components/IntegrationsPage/components/MappingScheduleFields.js +23 -23
  40. package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.js +31 -31
  41. package/dist/fe/components/IntegrationsPage/components/MappingsTab.js +56 -56
  42. package/dist/fe/components/IntegrationsPage/components/PaginationFields.js +11 -11
  43. package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.js +15 -15
  44. package/dist/fe/components/IntegrationsPage/components/Primitives.js +28 -28
  45. package/dist/fe/components/IntegrationsPage/components/ReferenceCheckRows.js +9 -9
  46. package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.js +6 -6
  47. package/dist/fe/components/IntegrationsPage/components/RunHistory.js +6 -6
  48. package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.js +9 -9
  49. package/dist/fe/components/IntegrationsPage/components/RunPanel.js +40 -40
  50. package/dist/fe/components/IntegrationsPage/components/RunPasswordField.js +18 -18
  51. package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.js +6 -6
  52. package/dist/fe/components/IntegrationsPage/components/RunPlanReferences.js +12 -12
  53. package/dist/fe/components/IntegrationsPage/components/RunPlanSuggestions.js +11 -11
  54. package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.js +6 -6
  55. package/dist/fe/components/IntegrationsPage/components/RunProgressBar.js +6 -6
  56. package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.js +6 -6
  57. package/dist/fe/components/IntegrationsPage/components/Skeletons.js +7 -7
  58. package/dist/fe/components/IntegrationsPage/components/authOptions.js +4 -4
  59. package/dist/fe/components/IntegrationsPage/components/connectionDraft.js +27 -27
  60. package/dist/fe/components/IntegrationsPage/components/credentialSlots.js +25 -25
  61. package/dist/fe/components/IntegrationsPage/components/deleteIsConfirmed.js +9 -9
  62. package/dist/fe/components/IntegrationsPage/components/fieldMappingOptions.js +4 -4
  63. package/dist/fe/components/IntegrationsPage/components/formSupport.js +34 -34
  64. package/dist/fe/components/IntegrationsPage/components/jsonArray.js +14 -14
  65. package/dist/fe/components/IntegrationsPage/components/jsonObjectField.js +15 -15
  66. package/dist/fe/components/IntegrationsPage/components/mappingDraft.js +10 -10
  67. package/dist/fe/components/IntegrationsPage/components/mappingStaleness.js +22 -22
  68. package/dist/fe/components/IntegrationsPage/components/nextRuns.js +8 -8
  69. package/dist/fe/components/IntegrationsPage/components/passwordIsNeeded.js +13 -13
  70. package/dist/fe/components/IntegrationsPage/components/pathParams.js +6 -6
  71. package/dist/fe/components/IntegrationsPage/components/runFormat.js +18 -18
  72. package/dist/fe/components/IntegrationsPage/components/settleOwnership.js +18 -18
  73. package/dist/fe/components/IntegrationsPage/components/useInfiniteList.js +8 -8
  74. package/dist/fe/components/IntegrationsPage/store/index.js +20 -20
  75. package/dist/fe/components/IntegrationsPage/store/upsertInto.js +12 -12
  76. package/dist/fe/components/IntegrationsPage/text/context.js +6 -6
  77. package/dist/fe/components/IntegrationsPage/text/index.js +18 -18
  78. package/dist/fe/components/IntegrationsPage/theme/index.js +29 -29
  79. package/dist/fe/components/IntegrationsPage/types/records.js +10 -10
  80. package/dist/fe/components/IntegrationsPage/types/results.js +3 -3
  81. package/dist/fe/components/NotificationCenter/labels.js +17 -17
  82. package/dist/fe/components/ProfilePage/labels.js +10 -10
  83. package/dist/fe/components/ResetPasswordPage/labels.js +4 -4
  84. package/dist/fe/components/SetPasswordPage/labels.js +11 -11
  85. package/dist/fe/components/UsersPage/labels.js +14 -14
  86. package/dist/fe/utils/columnUtils.js +24 -24
  87. package/dist/fe/utils/searchFold.js +36 -36
  88. package/dist/index.js +6 -6
  89. package/dist/server.js +6 -6
  90. package/dist/src/Client/Proxy/authz.js +4 -4
  91. package/dist/src/Client/Proxy/httpProxy.js +136 -136
  92. package/dist/src/Client/Proxy/largeBody.js +29 -29
  93. package/dist/src/Client/Proxy/utils.js +32 -32
  94. package/dist/src/Client/Proxy/wsProxy.js +59 -59
  95. package/dist/src/Managers/Redis/index.d.ts +14 -0
  96. package/dist/src/Nucleus/routes/auth/admin/impersonationStore.d.ts +3 -4
  97. package/dist/src/Nucleus/routes/auth/refresh/issuedPair.d.ts +67 -0
  98. package/dist/src/Nucleus/routes/auth/refresh/recordIssued.d.ts +15 -6
  99. package/dist/src/Nucleus/routes/auth/refresh/refreshChain.d.ts +56 -0
  100. package/dist/src/Services/Auth/SessionStore/utils.d.ts +2 -0
  101. package/dist/src/Shared/cron.js +71 -71
  102. package/dist/src/Shared/redactSecrets.js +156 -156
  103. package/dist/src/Shared/redaction/bare.js +61 -61
  104. package/dist/src/Shared/redaction/floor.js +48 -48
  105. package/dist/src/Shared/redaction/freeText.js +70 -70
  106. package/dist/src/Shared/redaction/json.js +30 -30
  107. package/dist/src/Shared/redaction/lists.js +23 -23
  108. package/dist/src/Shared/redaction/markers.js +28 -28
  109. package/dist/src/Shared/redaction/names.js +49 -49
  110. package/dist/src/Shared/redaction/pairs.js +54 -54
  111. package/dist/src/Shared/redaction/secretString.js +29 -29
  112. package/dist/src/Shared/redaction/structured.js +52 -52
  113. package/dist/src/Shared/redaction/walk.js +25 -25
  114. package/infra/scripts/generate-project.ts +766 -766
  115. package/infra/templates/README.md +57 -57
  116. package/infra/templates/backend/.dockerignore +9 -9
  117. package/infra/templates/backend/.env.example +15 -15
  118. package/infra/templates/backend/README.md +28 -28
  119. package/infra/templates/backend/azure-pipelines.yml +135 -135
  120. package/infra/templates/backend/k8s/Dockerfile +31 -31
  121. package/infra/templates/backend/k8s/configmap.yaml +26 -26
  122. package/infra/templates/backend/k8s/deployment.yaml +85 -85
  123. package/infra/templates/backend/k8s/pvc.yaml +12 -12
  124. package/infra/templates/backend/k8s/service.yaml +13 -13
  125. package/infra/templates/backend/tsconfig.json +22 -22
  126. package/infra/templates/frontend/.dockerignore +10 -10
  127. package/infra/templates/frontend/.env.example +8 -8
  128. package/infra/templates/frontend/.env.local.example +10 -10
  129. package/infra/templates/frontend/README.md +69 -69
  130. package/infra/templates/frontend/app/[tablename]/page.tsx +37 -37
  131. package/infra/templates/frontend/app/_components/AuthWrapper/index.tsx +63 -63
  132. package/infra/templates/frontend/app/authorization/page.tsx +43 -43
  133. package/infra/templates/frontend/app/change-password/page.tsx +23 -23
  134. package/infra/templates/frontend/app/devices/page.tsx +127 -127
  135. package/infra/templates/frontend/app/forgot-password/page.tsx +20 -20
  136. package/infra/templates/frontend/app/globals.css +28 -28
  137. package/infra/templates/frontend/app/layout.tsx +35 -35
  138. package/infra/templates/frontend/app/login/page.tsx +33 -33
  139. package/infra/templates/frontend/app/page.tsx +565 -565
  140. package/infra/templates/frontend/app/profile/page.tsx +92 -92
  141. package/infra/templates/frontend/app/register/page.tsx +27 -27
  142. package/infra/templates/frontend/app/reset-password/page.tsx +60 -60
  143. package/infra/templates/frontend/app/set-password/page.tsx +43 -43
  144. package/infra/templates/frontend/app/ui-docs/page.tsx +52 -52
  145. package/infra/templates/frontend/app/users/page.tsx +52 -52
  146. package/infra/templates/frontend/app/verify-email/page.tsx +41 -41
  147. package/infra/templates/frontend/app/verify-magic-link/page.tsx +46 -46
  148. package/infra/templates/frontend/azure-pipelines.yml +137 -137
  149. package/infra/templates/frontend/code-and-design-principles.md +438 -438
  150. package/infra/templates/frontend/k8s/Dockerfile +50 -50
  151. package/infra/templates/frontend/k8s/configmap.yaml +18 -18
  152. package/infra/templates/frontend/k8s/deployment.yaml +72 -72
  153. package/infra/templates/frontend/k8s/ingress.yaml +36 -36
  154. package/infra/templates/frontend/k8s/service.yaml +14 -14
  155. package/infra/templates/frontend/lib/api/endpoints.ts +37 -37
  156. package/infra/templates/frontend/lib/api/factory.ts +44 -44
  157. package/infra/templates/frontend/lib/api/hook.ts +10 -10
  158. package/infra/templates/frontend/next.config.ts +19 -19
  159. package/infra/templates/frontend/package.json +34 -34
  160. package/infra/templates/frontend/postcss.config.mjs +5 -5
  161. package/infra/templates/frontend/server.ts +114 -114
  162. package/infra/templates/frontend/tsconfig.json +23 -23
  163. package/package.json +1 -1
  164. package/public/components/CTA.tsx +25 -25
  165. package/public/components/Capabilities.tsx +67 -67
  166. package/public/components/Features.tsx +107 -107
  167. package/public/components/Footer.tsx +58 -58
  168. package/public/components/Hero.tsx +51 -51
  169. package/public/components/Managers.tsx +55 -55
  170. package/public/components/Nav.tsx +91 -91
  171. package/public/components/Stack.tsx +36 -36
  172. package/public/components/docs/Docs.tsx +60 -60
  173. package/public/components/docs/DocsContent.tsx +176 -176
  174. package/public/components/docs/DocsRightSidebar.tsx +53 -53
  175. package/public/components/docs/DocsSidebar.tsx +54 -54
  176. package/public/components/docs/content/authentication.ts +836 -836
  177. package/public/components/docs/content/authorization.ts +268 -268
  178. package/public/components/docs/content/backupRestore.ts +173 -173
  179. package/public/components/docs/content/clientModule.ts +287 -287
  180. package/public/components/docs/content/entityCrud.ts +682 -682
  181. package/public/components/docs/content/monitoring.ts +313 -313
  182. package/public/components/docs/content/multiTenant.ts +269 -269
  183. package/public/components/docs/content/oauthApiKeys.ts +325 -325
  184. package/public/components/docs/content/pubsub.ts +131 -131
  185. package/public/components/docs/content/storageCdn.ts +171 -171
  186. package/public/components/docs/content/verification.ts +289 -289
  187. package/public/components/docs/docsData.ts +113 -113
  188. package/public/components/docs/index.ts +6 -6
  189. package/public/components/index.ts +9 -9
  190. package/public/docs.css +629 -629
  191. package/public/index.html +81 -81
  192. package/public/index.tsx +42 -42
  193. package/public/store.ts +76 -76
  194. package/public/styles.css +858 -858
  195. package/schemas/config.nucleus.json +2651 -2651
  196. package/scripts/audit-purge-noise.ts +120 -120
  197. package/scripts/build.ts +582 -582
  198. package/scripts/generate-system-tables-types.ts +18 -18
  199. package/scripts/generate-types-schema.test.ts +73 -73
  200. package/scripts/generate-types-schema.ts +1284 -1284
  201. package/scripts/packagedScripts.test.ts +87 -87
  202. package/scripts/publish.ts +276 -276
  203. package/scripts/skillCitations.test.ts +107 -107
  204. package/scripts/verify-publishable.ts +40 -40
@@ -6,12 +6,12 @@ import { clientAuthOptions, grantTypeOptions, toClientAuth, toGrantType } from '
6
6
  import { usesOauth } from './connectionDraft';
7
7
  import { describedBy } from './formSupport';
8
8
  import { Field } from './Primitives';
9
- /**
10
- * The OAuth 2 half of the auth section.
11
- *
12
- * It renders nothing at all for the other auth types, so the caller can place it
13
- * unconditionally and the question "is this connection OAuth" is answered in one
14
- * place instead of at every field.
9
+ /**
10
+ * The OAuth 2 half of the auth section.
11
+ *
12
+ * It renders nothing at all for the other auth types, so the caller can place it
13
+ * unconditionally and the question "is this connection OAuth" is answered in one
14
+ * place instead of at every field.
15
15
  */ const theme = integrationsPageTheme;
16
16
  export function ConnectionOauthFields({ draft, errors, disabled, onChange }) {
17
17
  const labels = useLabels();
@@ -7,33 +7,33 @@ import { ConnectionExport } from './ConnectionExport';
7
7
  import { ConnectionForm } from './ConnectionForm';
8
8
  import { ConnectionTest } from './ConnectionTest';
9
9
  import { CredentialFields } from './CredentialFields';
10
- /**
11
- * Everything about the connection itself: its address and authentication, its
12
- * credentials, and whether it answers.
13
- *
14
- * These three were written and then never reachable — the shell mounted the
15
- * connection list and the three child tabs and nothing else, so the panel could
16
- * show you connections but gave you no way to create or edit one. That is the
17
- * first screen an operator needs and it was the one screen missing.
18
- *
19
- * Credentials sit between the form and the test on purpose: an operator fills in
20
- * the address, enters the secret, then presses test. Putting the test above the
21
- * credential boxes invites pressing it before there is anything to authenticate
22
- * with, and reading the failure as "the address is wrong".
10
+ /**
11
+ * Everything about the connection itself: its address and authentication, its
12
+ * credentials, and whether it answers.
13
+ *
14
+ * These three were written and then never reachable — the shell mounted the
15
+ * connection list and the three child tabs and nothing else, so the panel could
16
+ * show you connections but gave you no way to create or edit one. That is the
17
+ * first screen an operator needs and it was the one screen missing.
18
+ *
19
+ * Credentials sit between the form and the test on purpose: an operator fills in
20
+ * the address, enters the secret, then presses test. Putting the test above the
21
+ * credential boxes invites pressing it before there is anything to authenticate
22
+ * with, and reading the failure as "the address is wrong".
23
23
  */ export function ConnectionSettingsTab({ actions, onSaved, onDeleted }) {
24
24
  const store = useIntegrationsStore();
25
25
  const source = store.sources.find((candidate)=>candidate.id === store.selectedSourceId) ?? null;
26
- /**
27
- * Waiting for THIS record, and nothing else.
28
- *
29
- * It used to be `store.sourcesLoading || missingFromPage`, and sourcesLoading
30
- * is true whenever the rail is fetching ANY page — including a page fetched
31
- * because somebody typed a letter into the search box. With no connection
32
- * chosen this tab is the blank "new connection" form, and every keystroke in
33
- * the rail beside it replaced that form with skeletons.
34
- *
35
- * A form that is not editing a record is never loading: there is nothing to
36
- * wait for. A form that IS editing one waits only until that record is here.
26
+ /**
27
+ * Waiting for THIS record, and nothing else.
28
+ *
29
+ * It used to be `store.sourcesLoading || missingFromPage`, and sourcesLoading
30
+ * is true whenever the rail is fetching ANY page — including a page fetched
31
+ * because somebody typed a letter into the search box. With no connection
32
+ * chosen this tab is the blank "new connection" form, and every keystroke in
33
+ * the rail beside it replaced that form with skeletons.
34
+ *
35
+ * A form that is not editing a record is never loading: there is nothing to
36
+ * wait for. A form that IS editing one waits only until that record is here.
37
37
  */ const waitingForRecord = store.selectedSourceId !== null && source === null;
38
38
  return /*#__PURE__*/ _jsxs("div", {
39
39
  className: theme.layout.main,
@@ -4,28 +4,28 @@ import { cn } from '../../../utils/cn';
4
4
  import { useIntegrationsStore } from '../store';
5
5
  import { useLabels } from '../text/context';
6
6
  import { integrationsPageTheme as theme } from '../theme';
7
- /**
8
- * The chosen connection, as one line instead of a column.
9
- *
10
- * The panel used to keep the connection list as a permanent rail, so the work
11
- * happened in what was left — and the work is itself a list beside an editor.
12
- * Three columns inside an admin page that already has a nav is how a form ends
13
- * up past the right edge of the screen with the page scrolling sideways.
14
- *
15
- * A connection is CHOSEN once and then worked in. Once it is chosen the rail
16
- * has nothing left to say that this line does not, so the rail goes and the
17
- * whole width goes to the step.
7
+ /**
8
+ * The chosen connection, as one line instead of a column.
9
+ *
10
+ * The panel used to keep the connection list as a permanent rail, so the work
11
+ * happened in what was left — and the work is itself a list beside an editor.
12
+ * Three columns inside an admin page that already has a nav is how a form ends
13
+ * up past the right edge of the screen with the page scrolling sideways.
14
+ *
15
+ * A connection is CHOSEN once and then worked in. Once it is chosen the rail
16
+ * has nothing left to say that this line does not, so the rail goes and the
17
+ * whole width goes to the step.
18
18
  */ export function ConnectionSwitcher() {
19
19
  const store = useIntegrationsStore();
20
20
  const labels = useLabels();
21
- /**
22
- * The way back does not depend on finding anything.
23
- *
24
- * `store.sources` is the rail's CURRENT PAGE, and the rail is unmounted the
25
- * moment a connection is chosen — so a selected connection that is not on
26
- * that page rendered nothing at all here: no name, and no way back to the
27
- * list short of reloading the page. The name is what needs looking up; the
28
- * button is not.
21
+ /**
22
+ * The way back does not depend on finding anything.
23
+ *
24
+ * `store.sources` is the rail's CURRENT PAGE, and the rail is unmounted the
25
+ * moment a connection is chosen — so a selected connection that is not on
26
+ * that page rendered nothing at all here: no name, and no way back to the
27
+ * list short of reloading the page. The name is what needs looking up; the
28
+ * button is not.
29
29
  */ if (!store.selectedSourceId) return null;
30
30
  const source = store.sources.find((candidate)=>candidate.id === store.selectedSourceId);
31
31
  return /*#__PURE__*/ _jsxs("div", {
@@ -4,21 +4,21 @@ import { useRef } from 'react';
4
4
  import { cn } from '../../../utils/cn';
5
5
  import { useLabels } from '../text/context';
6
6
  import { integrationsPageTheme } from '../theme';
7
- /**
8
- * The four phases of an import, in the order they happen.
9
- *
10
- * These were four flat tabs — Connection, Endpoints, Mappings, Runs — sitting
11
- * side by side with nothing to say that they are a SEQUENCE, that a mapping is
12
- * meaningless before a call has been tried, or which of them the operator had
13
- * already finished. People opened the middle one first and met a dead end.
14
- *
15
- * So each is a step: a number, its name, one sentence saying what it is for, and
16
- * a line of evidence once it is done. What is on screen now answers "where am I,
17
- * what is this for, and what have I already got".
18
- *
19
- * It remains a real ARIA tablist — one stop in the tab order, arrow keys between
20
- * the steps. A row of separately tabbable cards would make a keyboard user press
21
- * Tab four times to leave a strip they were never inside.
7
+ /**
8
+ * The four phases of an import, in the order they happen.
9
+ *
10
+ * These were four flat tabs — Connection, Endpoints, Mappings, Runs — sitting
11
+ * side by side with nothing to say that they are a SEQUENCE, that a mapping is
12
+ * meaningless before a call has been tried, or which of them the operator had
13
+ * already finished. People opened the middle one first and met a dead end.
14
+ *
15
+ * So each is a step: a number, its name, one sentence saying what it is for, and
16
+ * a line of evidence once it is done. What is on screen now answers "where am I,
17
+ * what is this for, and what have I already got".
18
+ *
19
+ * It remains a real ARIA tablist — one stop in the tab order, arrow keys between
20
+ * the steps. A row of separately tabbable cards would make a keyboard user press
21
+ * Tab four times to leave a strip they were never inside.
22
22
  */ const theme = integrationsPageTheme;
23
23
  const ORDER = [
24
24
  'connection',
@@ -7,13 +7,13 @@ import { integrationsPageTheme } from '../theme';
7
7
  import { errorText } from './formSupport';
8
8
  import { Badge, Notice } from './Primitives';
9
9
  import { CardSkeleton } from './Skeletons';
10
- /**
11
- * One real call to the service, reported exactly as it came back.
12
- *
13
- * The failure text is printed whole. The server already names the setting that
14
- * has to change — a timeout, a certificate it would not accept, a 401 from the
15
- * token endpoint — and every word of that is worth more than a tidy summary that
16
- * sends the operator back to guessing.
10
+ /**
11
+ * One real call to the service, reported exactly as it came back.
12
+ *
13
+ * The failure text is printed whole. The server already names the setting that
14
+ * has to change — a timeout, a certificate it would not accept, a 401 from the
15
+ * token endpoint — and every word of that is worth more than a tidy summary that
16
+ * sends the operator back to guessing.
17
17
  */ const theme = integrationsPageTheme;
18
18
  export function ConnectionTest({ actions, source, loading = false }) {
19
19
  const t = useLabels().errors;
@@ -26,20 +26,20 @@ export function ConnectionTest({ actions, source, loading = false }) {
26
26
  setResult(null);
27
27
  setTesting(false);
28
28
  });
29
- /**
30
- * A verdict describes the settings it was given, not the connection's name.
31
- *
32
- * Keyed on the id alone, "Reachable" survived editing the base URL and saving:
33
- * the record object is replaced but its id is not, so this never re-ran and a
34
- * green badge sat under an address that had never been called. Everything the
35
- * test actually depends on is in the key.
36
- *
37
- * A stored CREDENTIAL is such a setting, and it was the one left out. Clearing
38
- * a token left the green verdict standing for a connection that would now be
39
- * refused; the mirror image costs more of an afternoon — a red "Not reachable"
40
- * from a 401 stayed up after the right password was saved, reading as "the
41
- * password did not help". The masked hints are what move when a credential
42
- * does, so they are what this watches.
29
+ /**
30
+ * A verdict describes the settings it was given, not the connection's name.
31
+ *
32
+ * Keyed on the id alone, "Reachable" survived editing the base URL and saving:
33
+ * the record object is replaced but its id is not, so this never re-ran and a
34
+ * green badge sat under an address that had never been called. Everything the
35
+ * test actually depends on is in the key.
36
+ *
37
+ * A stored CREDENTIAL is such a setting, and it was the one left out. Clearing
38
+ * a token left the green verdict standing for a connection that would now be
39
+ * refused; the mirror image costs more of an afternoon — a red "Not reachable"
40
+ * from a 401 stayed up after the right password was saved, reading as "the
41
+ * password did not help". The masked hints are what move when a credential
42
+ * does, so they are what this watches.
43
43
  */ useEffect(()=>{
44
44
  clear();
45
45
  }, [
@@ -6,24 +6,24 @@ import { useIntegrationsStore } from '../store';
6
6
  import { useLabels } from '../text/context';
7
7
  import { integrationsPageTheme } from '../theme';
8
8
  import { Notice } from './Primitives';
9
- /**
10
- * Bringing a whole integration in from another installation.
11
- *
12
- * A connection is the connection, the calls it makes, the parameters those calls
13
- * take from each other, and the mappings that turn the answers into rows.
14
- * Rebuilding that by hand in a second environment is hours of work and a fresh
15
- * chance to get one field wrong in a way nobody notices until an import writes
16
- * it. So it travels as one file.
17
- *
18
- * The file carries NO credentials — deliberately, and it says so on both sides,
19
- * because a connection that arrives looking configured and cannot authenticate
20
- * is a confusing half hour otherwise.
21
- *
22
- * Export used to sit beside this button and could never be pressed: it needs a
23
- * connection to be CHOSEN, and choosing one replaces this rail with that
24
- * connection's own screens. It now lives there, on the thing being exported —
25
- * see ConnectionExport. Importing is the one that belongs here, because
26
- * importing is what you do when you have nothing chosen yet.
9
+ /**
10
+ * Bringing a whole integration in from another installation.
11
+ *
12
+ * A connection is the connection, the calls it makes, the parameters those calls
13
+ * take from each other, and the mappings that turn the answers into rows.
14
+ * Rebuilding that by hand in a second environment is hours of work and a fresh
15
+ * chance to get one field wrong in a way nobody notices until an import writes
16
+ * it. So it travels as one file.
17
+ *
18
+ * The file carries NO credentials — deliberately, and it says so on both sides,
19
+ * because a connection that arrives looking configured and cannot authenticate
20
+ * is a confusing half hour otherwise.
21
+ *
22
+ * Export used to sit beside this button and could never be pressed: it needs a
23
+ * connection to be CHOSEN, and choosing one replaces this rail with that
24
+ * connection's own screens. It now lives there, on the thing being exported —
25
+ * see ConnectionExport. Importing is the one that belongs here, because
26
+ * importing is what you do when you have nothing chosen yet.
27
27
  */ const theme = integrationsPageTheme;
28
28
  const messageOf = (error, fallback)=>error instanceof Error ? error.message : fallback;
29
29
  export function ConnectionTransfer({ actions, onImported }) {
@@ -9,14 +9,14 @@ import { hintColumnsFor, hintFromResult, patchFor, slotsForSource, storedHint }
9
9
  import { errorText } from './formSupport';
10
10
  import { Notice } from './Primitives';
11
11
  import { CardSkeleton } from './Skeletons';
12
- /**
13
- * The secrets, written and never read.
14
- *
15
- * Nothing on this screen has ever seen a stored value and nothing here asks for
16
- * one: the record carries a masked hint, a save returns a new masked hint, and
17
- * that is the whole conversation. An empty box therefore cannot mean "make it
18
- * empty" — it means "leave it alone" — which is why clearing is a separate
19
- * button per field.
12
+ /**
13
+ * The secrets, written and never read.
14
+ *
15
+ * Nothing on this screen has ever seen a stored value and nothing here asks for
16
+ * one: the record carries a masked hint, a save returns a new masked hint, and
17
+ * that is the whole conversation. An empty box therefore cannot mean "make it
18
+ * empty" — it means "leave it alone" — which is why clearing is a separate
19
+ * button per field.
20
20
  */ const theme = integrationsPageTheme;
21
21
  export function CredentialFields({ actions, source, loading = false }) {
22
22
  const t = useLabels().errors;
@@ -52,17 +52,17 @@ export function CredentialFields({ actions, source, loading = false }) {
52
52
  const sourceRecord = source;
53
53
  const slots = slotsForSource(sourceRecord);
54
54
  const hintOf = (slot)=>slot in hints ? hints[slot] ?? null : storedHint(sourceRecord, slot);
55
- /**
56
- * The new masks, into local state AND into the record everything else reads.
57
- *
58
- * Local state alone is not enough, and the way it failed is worth keeping:
59
- * `hintOf` falls back to `storedHint(sourceRecord, …)` for any slot local
60
- * state has nothing for, and local state is cleared whenever the chosen
61
- * connection changes. So a token could be saved, show its green "Stored"
62
- * badge, and revert to "Not set" the moment the operator visited another tab
63
- * and came back — because the store's copy of the connection still carried
64
- * the pre-save hints. The value was in the database throughout; only the
65
- * record in memory was old.
55
+ /**
56
+ * The new masks, into local state AND into the record everything else reads.
57
+ *
58
+ * Local state alone is not enough, and the way it failed is worth keeping:
59
+ * `hintOf` falls back to `storedHint(sourceRecord, …)` for any slot local
60
+ * state has nothing for, and local state is cleared whenever the chosen
61
+ * connection changes. So a token could be saved, show its green "Stored"
62
+ * badge, and revert to "Not set" the moment the operator visited another tab
63
+ * and came back — because the store's copy of the connection still carried
64
+ * the pre-save hints. The value was in the database throughout; only the
65
+ * record in memory was old.
66
66
  */ const remember = (changed)=>{
67
67
  if (Object.keys(changed).length === 0) return;
68
68
  setHints((current)=>({
@@ -6,14 +6,14 @@ import { integrationsPageTheme } from '../theme';
6
6
  import { slotHint, slotLabel } from './credentialSlots';
7
7
  import { describedBy } from './formSupport';
8
8
  import { Badge, Field } from './Primitives';
9
- /**
10
- * One secret: a box to type a new value into, the masked preview of whatever is
11
- * already stored, and the only way to remove it.
12
- *
13
- * Clearing is its own button rather than "save an empty box", because an empty
14
- * box is what an operator leaves behind when they came to change something else.
15
- * Making that mean "delete the credential" would break a working connection by
16
- * accident, roughly once.
9
+ /**
10
+ * One secret: a box to type a new value into, the masked preview of whatever is
11
+ * already stored, and the only way to remove it.
12
+ *
13
+ * Clearing is its own button rather than "save an empty box", because an empty
14
+ * box is what an operator leaves behind when they came to change something else.
15
+ * Making that mean "delete the credential" would break a working connection by
16
+ * accident, roughly once.
17
17
  */ const theme = integrationsPageTheme;
18
18
  export function CredentialSlotRow({ slot, source, value, maskedHint, disabled, onChange, onClear }) {
19
19
  const t = useLabels().errors;
@@ -10,13 +10,13 @@ import { formatJsonObject, parseJsonObject } from './jsonObjectField';
10
10
  import { PaginationFields } from './PaginationFields';
11
11
  import { ParamBindingRows } from './ParamBindingRows';
12
12
  import { pathParamNames } from './pathParams';
13
- /**
14
- * One call against a connection: what to ask for, and where the records sit in
15
- * the answer.
16
- *
17
- * The draft is seeded once. The parent remounts this form when the selection
18
- * changes, so a half-typed path is never pulled out from under the operator by
19
- * a list page arriving in the background.
13
+ /**
14
+ * One call against a connection: what to ask for, and where the records sit in
15
+ * the answer.
16
+ *
17
+ * The draft is seeded once. The parent remounts this form when the selection
18
+ * changes, so a half-typed path is never pulled out from under the operator by
19
+ * a list page arriving in the background.
20
20
  */ const theme = integrationsPageTheme;
21
21
  export function EndpointForm({ sourceId, endpoint, actions, onSaved, onDeleted, onCancel }) {
22
22
  const t = useLabels().errors;
@@ -6,11 +6,11 @@ import { useLabels } from '../text/context';
6
6
  import { integrationsPageTheme } from '../theme';
7
7
  import { describedBy } from './formSupport';
8
8
  import { Field } from './Primitives';
9
- /**
10
- * The plain fields of an endpoint.
11
- *
12
- * Split out of the form because the form's job is saving and deleting, and this
13
- * one's is asking. The draft type lives here, next to the controls that fill it.
9
+ /**
10
+ * The plain fields of an endpoint.
11
+ *
12
+ * Split out of the form because the form's job is saving and deleting, and this
13
+ * one's is asking. The draft type lives here, next to the controls that fill it.
14
14
  */ const theme = integrationsPageTheme;
15
15
  const METHODS = [
16
16
  'GET',
@@ -21,13 +21,13 @@ const METHODS = [
21
21
  ];
22
22
  export function EndpointFormFields({ draft, disabled, errors, onChange }) {
23
23
  const labels = useLabels();
24
- /**
25
- * Unique per instance.
26
- *
27
- * These ids were literals — `endpoint-tag`, `endpoint-name` — which is fine
28
- * for one form on a page and wrong the moment there are two: every `<label
29
- * htmlFor>` binds to whichever rendered first, so clicking a label focuses the
30
- * OTHER record's field. `useId` makes that structurally impossible.
24
+ /**
25
+ * Unique per instance.
26
+ *
27
+ * These ids were literals — `endpoint-tag`, `endpoint-name` — which is fine
28
+ * for one form on a page and wrong the moment there are two: every `<label
29
+ * htmlFor>` binds to whichever rendered first, so clicking a label focuses the
30
+ * OTHER record's field. `useId` makes that structurally impossible.
31
31
  */ const uid = useId();
32
32
  return /*#__PURE__*/ _jsxs(_Fragment, {
33
33
  children: [
@@ -8,13 +8,13 @@ import { integrationsPageTheme } from '../theme';
8
8
  import { Badge, Field, Notice } from './Primitives';
9
9
  import { ListSkeleton } from './Skeletons';
10
10
  import { useInfiniteList } from './useInfiniteList';
11
- /**
12
- * The calls one connection knows how to make.
13
- *
14
- * The server owns the list: the search term goes back out on the next request
15
- * and the next page arrives when the sentinel scrolls into view. Nothing here
16
- * narrows rows already fetched, so a connection with six endpoints and one with
17
- * six hundred behave identically.
11
+ /**
12
+ * The calls one connection knows how to make.
13
+ *
14
+ * The server owns the list: the search term goes back out on the next request
15
+ * and the next page arrives when the sentinel scrolls into view. Nothing here
16
+ * narrows rows already fetched, so a connection with six endpoints and one with
17
+ * six hundred behave identically.
18
18
  */ const theme = integrationsPageTheme;
19
19
  const PAGE_SIZE = 20;
20
20
  const SEARCH_DEBOUNCE_MS = 300;
@@ -8,13 +8,13 @@ import { Badge, CodeBlock, Field, Notice, Stat } from './Primitives';
8
8
  import { pathParamNames } from './pathParams';
9
9
  import { CardSkeleton, StatGridSkeleton } from './Skeletons';
10
10
  import { settlementOf } from './settleOwnership';
11
- /**
12
- * One real request, shown in full.
13
- *
14
- * The interesting case is zero records, because that is when a configuration
15
- * looks finished and imports nothing. The server already answers it — it returns
16
- * the top-level keys of the body it actually received — so those keys are given
17
- * their own block here rather than being folded into a generic "no data".
11
+ /**
12
+ * One real request, shown in full.
13
+ *
14
+ * The interesting case is zero records, because that is when a configuration
15
+ * looks finished and imports nothing. The server already answers it — it returns
16
+ * the top-level keys of the body it actually received — so those keys are given
17
+ * their own block here rather than being folded into a generic "no data".
18
18
  */ const theme = integrationsPageTheme;
19
19
  export function EndpointSample({ endpoint, actions }) {
20
20
  const t = useLabels().errors;
@@ -27,27 +27,27 @@ export function EndpointSample({ endpoint, actions }) {
27
27
  const selectedRef = useRef(store.selectedEndpointId);
28
28
  selectedRef.current = store.selectedEndpointId;
29
29
  const params = pathParamNames(endpoint.path);
30
- /**
31
- * Only a sample taken of THIS call.
32
- *
33
- * The card is keyed per selection, so switching calls removes it and a request
34
- * still in flight settles with nowhere to report to — except the shared slot,
35
- * which it writes to regardless. Before the tag, that answer appeared under
36
- * whichever call was open when it landed, and the mapping built next took its
37
- * field list from a call that had never returned those fields.
30
+ /**
31
+ * Only a sample taken of THIS call.
32
+ *
33
+ * The card is keyed per selection, so switching calls removes it and a request
34
+ * still in flight settles with nowhere to report to — except the shared slot,
35
+ * which it writes to regardless. Before the tag, that answer appeared under
36
+ * whichever call was open when it landed, and the mapping built next took its
37
+ * field list from a call that had never returned those fields.
38
38
  */ const sample = store.sampleEndpointId === endpoint.id ? store.sample : null;
39
39
  let unfilled = 0;
40
40
  for (const name of params){
41
41
  if (!(values[name] ?? '').trim()) unfilled += 1;
42
42
  }
43
- /**
44
- * Discard a late answer, but hand the busy flag back on the way out.
45
- *
46
- * The discard itself is right — the operator has moved on and the field list
47
- * must describe the endpoint they are looking at. It used to be a bare
48
- * `return`, which skipped the clearing too: `sampling` stayed set with nobody
49
- * left to unset it, and the sample card showed a skeleton until the page was
50
- * reloaded. See settleOwnership.ts for who owns the flag.
43
+ /**
44
+ * Discard a late answer, but hand the busy flag back on the way out.
45
+ *
46
+ * The discard itself is right — the operator has moved on and the field list
47
+ * must describe the endpoint they are looking at. It used to be a bare
48
+ * `return`, which skipped the clearing too: `sampling` stayed set with nobody
49
+ * left to unset it, and the sample card showed a skeleton until the page was
50
+ * reloaded. See settleOwnership.ts for who owns the flag.
51
51
  */ const settleElsewhere = (mine, requestedFor)=>{
52
52
  const { discard, releaseFlag } = settlementOf({
53
53
  mine,
@@ -70,19 +70,19 @@ export function EndpointSample({ endpoint, actions }) {
70
70
  actions.sampleEndpoint(requestedFor, params.length > 0 ? values : undefined).then((result)=>{
71
71
  if (settleElsewhere(mine, requestedFor)) return;
72
72
  store.setSample(result, requestedFor);
73
- /**
74
- * The ROW learns it has been tried, not just this card.
75
- *
76
- * The step strip counts "tried" off `endpoints[].sampleResponse`,
77
- * because whether a call has ever been run is a fact about the call and
78
- * not about whichever screen is open. The server stores it there. This
79
- * panel never did, so a connection whose calls had all been sampled
80
- * went on announcing "7 call(s), 0 tried" — seen exactly that way on a
81
- * customer's live connection, where it read as "nobody has ever tried
82
- * these", which was not true.
83
- *
84
- * Only a sample that actually returned records counts, which is the
85
- * same rule the strip applies.
73
+ /**
74
+ * The ROW learns it has been tried, not just this card.
75
+ *
76
+ * The step strip counts "tried" off `endpoints[].sampleResponse`,
77
+ * because whether a call has ever been run is a fact about the call and
78
+ * not about whichever screen is open. The server stores it there. This
79
+ * panel never did, so a connection whose calls had all been sampled
80
+ * went on announcing "7 call(s), 0 tried" — seen exactly that way on a
81
+ * customer's live connection, where it read as "nobody has ever tried
82
+ * these", which was not true.
83
+ *
84
+ * Only a sample that actually returned records counts, which is the
85
+ * same rule the strip applies.
86
86
  */ if (result.ok && result.recordCount > 0) {
87
87
  const row = store.endpoints.find((candidate)=>candidate.id === requestedFor);
88
88
  if (row) {
@@ -7,12 +7,12 @@ import { integrationsPageTheme } from '../theme';
7
7
  import { EndpointForm } from './EndpointForm';
8
8
  import { EndpointList } from './EndpointList';
9
9
  import { EndpointSample } from './EndpointSample';
10
- /**
11
- * The endpoints section: the list on one side, the selected call on the other.
12
- *
13
- * The record the list hands over is the record the form edits, so opening an
14
- * endpoint costs no extra request. The form is keyed by that record's id, which
15
- * is what makes switching selection a fresh form rather than a merged one.
10
+ /**
11
+ * The endpoints section: the list on one side, the selected call on the other.
12
+ *
13
+ * The record the list hands over is the record the form edits, so opening an
14
+ * endpoint costs no extra request. The form is keyed by that record's id, which
15
+ * is what makes switching selection a fresh form rather than a merged one.
16
16
  */ const theme = integrationsPageTheme;
17
17
  export function EndpointsTab({ actions }) {
18
18
  const labels = useLabels();
@@ -20,15 +20,15 @@ export function EndpointsTab({ actions }) {
20
20
  const sourceId = store.selectedSourceId;
21
21
  const [creating, setCreating] = useState(false);
22
22
  const [reloadToken, setReloadToken] = useState(0);
23
- /**
24
- * The selection is the STORE's, and only the store's.
25
- *
26
- * It used to be kept here as well, and the two were reconciled by comparing
27
- * them on every render. Clicking a second call then left the first form on
28
- * screen and added the new one under it — two live editors of two different
29
- * records, both writing to the same hard-coded field ids, growing by one card
30
- * per click. One source of truth, and one keyed container below, is what makes
31
- * that impossible rather than unlikely.
23
+ /**
24
+ * The selection is the STORE's, and only the store's.
25
+ *
26
+ * It used to be kept here as well, and the two were reconciled by comparing
27
+ * them on every render. Clicking a second call then left the first form on
28
+ * screen and added the new one under it — two live editors of two different
29
+ * records, both writing to the same hard-coded field ids, growing by one card
30
+ * per click. One source of truth, and one keyed container below, is what makes
31
+ * that impossible rather than unlikely.
32
32
  */ const selectedId = store.selectedEndpointId;
33
33
  const editing = selectedId ? store.endpoints.find((e)=>e.id === selectedId) ?? null : null;
34
34
  // Another connection is another set of calls. A form left open across the
@@ -57,20 +57,20 @@ export function EndpointsTab({ actions }) {
57
57
  // collapse to "pick one" — with the form still half-filled a moment ago.
58
58
  store.upsertEndpoint(saved);
59
59
  store.selectEndpoint(saved.id);
60
- /**
61
- * The sample described the call as it was BEFORE this save.
62
- *
63
- * Re-selecting was supposed to drop it, and does not: `selectEndpoint`
64
- * returns early when the id has not changed, which on a save it never has —
65
- * it is the guard that stops re-choosing the open call from throwing away a
66
- * sample somebody just paid a real request for. Correct there, and the
67
- * reason nothing was cleared here.
68
- *
69
- * So the sample sat under an edited call: change `responseRootPath` from
70
- * empty to `data`, save, and the card still reports "Status 200, 0 records"
71
- * with the old body keys — and the mapping's field list is built from those
72
- * keys. The answer on screen belonged to a request that is no longer the one
73
- * configured.
60
+ /**
61
+ * The sample described the call as it was BEFORE this save.
62
+ *
63
+ * Re-selecting was supposed to drop it, and does not: `selectEndpoint`
64
+ * returns early when the id has not changed, which on a save it never has —
65
+ * it is the guard that stops re-choosing the open call from throwing away a
66
+ * sample somebody just paid a real request for. Correct there, and the
67
+ * reason nothing was cleared here.
68
+ *
69
+ * So the sample sat under an edited call: change `responseRootPath` from
70
+ * empty to `data`, save, and the card still reports "Status 200, 0 records"
71
+ * with the old body keys — and the mapping's field list is built from those
72
+ * keys. The answer on screen belonged to a request that is no longer the one
73
+ * configured.
74
74
  */ store.setSample(null, null);
75
75
  setReloadToken((token)=>token + 1);
76
76
  };
@@ -4,13 +4,13 @@ import { cn } from '../../../utils/cn';
4
4
  import { useLabels } from '../text/context';
5
5
  import { integrationsPageTheme } from '../theme';
6
6
  import { FIELD_MAPPING_GRID, PARENT_PREFIX, TRANSFORM_LABEL, TRANSFORMS, toTransform } from './fieldMappingOptions';
7
- /**
8
- * One line of a field mapping: where the value comes from, what it becomes, and
9
- * which column it lands in.
10
- *
11
- * The row owns no state. It reports a patch and the editor above decides what
12
- * the list of rows now is, so ordering and validation live in one place instead
13
- * of being spread across however many rows are on screen.
7
+ /**
8
+ * One line of a field mapping: where the value comes from, what it becomes, and
9
+ * which column it lands in.
10
+ *
11
+ * The row owns no state. It reports a patch and the editor above decides what
12
+ * the list of rows now is, so ordering and validation live in one place instead
13
+ * of being spread across however many rows are on screen.
14
14
  */ const theme = integrationsPageTheme;
15
15
  /** Label above each control on a phone; the header row replaces it from lg up. */ function Cell({ id, label, children }) {
16
16
  return /*#__PURE__*/ _jsxs("div", {