@shipfox/client-integrations 5.0.0 → 6.0.1

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 (187) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +64 -0
  3. package/dist/application/complete-integration-callback.d.ts +13 -0
  4. package/dist/application/complete-integration-callback.d.ts.map +1 -0
  5. package/dist/application/complete-integration-callback.js +26 -0
  6. package/dist/application/complete-integration-callback.js.map +1 -0
  7. package/dist/components/connection-picker.d.ts +2 -2
  8. package/dist/components/connection-picker.d.ts.map +1 -1
  9. package/dist/components/connection-picker.js +4 -4
  10. package/dist/components/connection-picker.js.map +1 -1
  11. package/dist/components/installed-integrations-section.d.ts +8 -6
  12. package/dist/components/installed-integrations-section.d.ts.map +1 -1
  13. package/dist/components/installed-integrations-section.js +22 -15
  14. package/dist/components/installed-integrations-section.js.map +1 -1
  15. package/dist/components/integration-gallery-for-workspace.d.ts +2 -2
  16. package/dist/components/integration-gallery-for-workspace.d.ts.map +1 -1
  17. package/dist/components/integration-gallery-for-workspace.js +14 -7
  18. package/dist/components/integration-gallery-for-workspace.js.map +1 -1
  19. package/dist/components/integration-gallery.d.ts +2 -2
  20. package/dist/components/integration-gallery.d.ts.map +1 -1
  21. package/dist/components/integration-gallery.js.map +1 -1
  22. package/dist/components/integration-usage-events.d.ts +1 -3
  23. package/dist/components/integration-usage-events.d.ts.map +1 -1
  24. package/dist/components/integration-usage-events.js +1 -125
  25. package/dist/components/integration-usage-events.js.map +1 -1
  26. package/dist/components/integration-usage-modal.d.ts +2 -2
  27. package/dist/components/integration-usage-modal.d.ts.map +1 -1
  28. package/dist/components/integration-usage-modal.js +2 -2
  29. package/dist/components/integration-usage-modal.js.map +1 -1
  30. package/dist/components/provider-grid.d.ts +7 -4
  31. package/dist/components/provider-grid.d.ts.map +1 -1
  32. package/dist/components/provider-grid.js +15 -10
  33. package/dist/components/provider-grid.js.map +1 -1
  34. package/dist/components/redirect-install-page.d.ts +2 -3
  35. package/dist/components/redirect-install-page.d.ts.map +1 -1
  36. package/dist/components/redirect-install-page.js +1 -1
  37. package/dist/components/redirect-install-page.js.map +1 -1
  38. package/dist/components/repository-picker.d.ts +2 -2
  39. package/dist/components/repository-picker.d.ts.map +1 -1
  40. package/dist/components/repository-picker.js +4 -4
  41. package/dist/components/repository-picker.js.map +1 -1
  42. package/dist/components/webhook/webhook-create-modal.d.ts +2 -2
  43. package/dist/components/webhook/webhook-create-modal.d.ts.map +1 -1
  44. package/dist/components/webhook/webhook-create-modal.js +8 -8
  45. package/dist/components/webhook/webhook-create-modal.js.map +1 -1
  46. package/dist/components/webhook/webhook-modals.stories.d.ts.map +1 -1
  47. package/dist/components/webhook/webhook-modals.stories.js +7 -7
  48. package/dist/components/webhook/webhook-modals.stories.js.map +1 -1
  49. package/dist/components/webhook/webhook-usage-details.d.ts.map +1 -1
  50. package/dist/components/webhook/webhook-usage-details.js +2 -2
  51. package/dist/components/webhook/webhook-usage-details.js.map +1 -1
  52. package/dist/connection-status-badge.d.ts +2 -2
  53. package/dist/connection-status-badge.d.ts.map +1 -1
  54. package/dist/connection-status-badge.js +11 -15
  55. package/dist/connection-status-badge.js.map +1 -1
  56. package/dist/core/models.d.ts +66 -0
  57. package/dist/core/models.d.ts.map +1 -0
  58. package/dist/core/models.js +156 -0
  59. package/dist/core/models.js.map +1 -0
  60. package/dist/feature.d.ts +7 -0
  61. package/dist/feature.d.ts.map +1 -1
  62. package/dist/feature.js +10 -9
  63. package/dist/feature.js.map +1 -1
  64. package/dist/hooks/api/integration-mapper.d.ts +11 -0
  65. package/dist/hooks/api/integration-mapper.d.ts.map +1 -0
  66. package/dist/hooks/api/integration-mapper.js +54 -0
  67. package/dist/hooks/api/integration-mapper.js.map +1 -0
  68. package/dist/hooks/api/integrations.d.ts +45 -240
  69. package/dist/hooks/api/integrations.d.ts.map +1 -1
  70. package/dist/hooks/api/integrations.js +78 -31
  71. package/dist/hooks/api/integrations.js.map +1 -1
  72. package/dist/hooks/api/webhook-connections.d.ts +12 -64
  73. package/dist/hooks/api/webhook-connections.d.ts.map +1 -1
  74. package/dist/hooks/api/webhook-connections.js +17 -9
  75. package/dist/hooks/api/webhook-connections.js.map +1 -1
  76. package/dist/index.d.ts +2 -1
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +2 -1
  79. package/dist/index.js.map +1 -1
  80. package/dist/linear-callback.d.ts +2 -1
  81. package/dist/linear-callback.d.ts.map +1 -1
  82. package/dist/linear-callback.js +14 -15
  83. package/dist/linear-callback.js.map +1 -1
  84. package/dist/pages/gitea-install-page.d.ts.map +1 -1
  85. package/dist/pages/gitea-install-page.js +1 -10
  86. package/dist/pages/gitea-install-page.js.map +1 -1
  87. package/dist/pages/linear-callback-page.d.ts.map +1 -1
  88. package/dist/pages/linear-callback-page.js +19 -32
  89. package/dist/pages/linear-callback-page.js.map +1 -1
  90. package/dist/pages/linear-install-page.d.ts.map +1 -1
  91. package/dist/pages/linear-install-page.js +2 -1
  92. package/dist/pages/linear-install-page.js.map +1 -1
  93. package/dist/pages/sentry-callback-page.d.ts.map +1 -1
  94. package/dist/pages/sentry-callback-page.js +22 -25
  95. package/dist/pages/sentry-callback-page.js.map +1 -1
  96. package/dist/pages/sentry-install-page.d.ts.map +1 -1
  97. package/dist/pages/sentry-install-page.js +2 -1
  98. package/dist/pages/sentry-install-page.js.map +1 -1
  99. package/dist/pages/slack-callback-page.d.ts.map +1 -1
  100. package/dist/pages/slack-callback-page.js +21 -28
  101. package/dist/pages/slack-callback-page.js.map +1 -1
  102. package/dist/pages/slack-install-page.d.ts.map +1 -1
  103. package/dist/pages/slack-install-page.js +2 -1
  104. package/dist/pages/slack-install-page.js.map +1 -1
  105. package/dist/pages/source-control-onboarding-page.d.ts.map +1 -1
  106. package/dist/pages/source-control-onboarding-page.js +5 -1
  107. package/dist/pages/source-control-onboarding-page.js.map +1 -1
  108. package/dist/provider-catalog.d.ts.map +1 -1
  109. package/dist/provider-catalog.js +7 -6
  110. package/dist/provider-catalog.js.map +1 -1
  111. package/dist/routes/github-callback.d.ts +16 -0
  112. package/dist/routes/github-callback.d.ts.map +1 -1
  113. package/dist/routes/github-callback.js +43 -21
  114. package/dist/routes/github-callback.js.map +1 -1
  115. package/dist/routes/linear-callback.d.ts +2 -0
  116. package/dist/routes/linear-callback.d.ts.map +1 -1
  117. package/dist/routes/linear-callback.js +2 -0
  118. package/dist/routes/linear-callback.js.map +1 -1
  119. package/dist/routes/sentry-callback.d.ts +2 -0
  120. package/dist/routes/sentry-callback.d.ts.map +1 -1
  121. package/dist/routes/sentry-callback.js +2 -0
  122. package/dist/routes/sentry-callback.js.map +1 -1
  123. package/dist/routes/slack-callback.d.ts +2 -0
  124. package/dist/routes/slack-callback.d.ts.map +1 -1
  125. package/dist/routes/slack-callback.js +2 -0
  126. package/dist/routes/slack-callback.js.map +1 -1
  127. package/dist/sentry-callback.d.ts +2 -1
  128. package/dist/sentry-callback.d.ts.map +1 -1
  129. package/dist/sentry-callback.js +16 -19
  130. package/dist/sentry-callback.js.map +1 -1
  131. package/dist/slack-callback.d.ts +2 -1
  132. package/dist/slack-callback.d.ts.map +1 -1
  133. package/dist/slack-callback.js +14 -15
  134. package/dist/slack-callback.js.map +1 -1
  135. package/dist/tsconfig.test.tsbuildinfo +1 -1
  136. package/package.json +17 -17
  137. package/src/application/complete-integration-callback.test.ts +41 -0
  138. package/src/application/complete-integration-callback.ts +39 -0
  139. package/src/components/connection-picker.tsx +7 -7
  140. package/src/components/installed-integrations-section.tsx +29 -24
  141. package/src/components/integration-gallery-for-workspace.tsx +23 -16
  142. package/src/components/integration-gallery.tsx +3 -3
  143. package/src/components/integration-usage-events.ts +1 -116
  144. package/src/components/integration-usage-modal.tsx +4 -4
  145. package/src/components/provider-grid.tsx +30 -18
  146. package/src/components/redirect-install-page.test.tsx +2 -6
  147. package/src/components/redirect-install-page.tsx +3 -2
  148. package/src/components/repository-picker.tsx +6 -6
  149. package/src/components/webhook/webhook-create-modal.tsx +12 -11
  150. package/src/components/webhook/webhook-modals.stories.tsx +10 -10
  151. package/src/components/webhook/webhook-usage-details.tsx +2 -4
  152. package/src/connection-status-badge.tsx +8 -14
  153. package/src/core/models.ts +200 -0
  154. package/src/feature.ts +12 -10
  155. package/src/hooks/api/integration-mapper.ts +64 -0
  156. package/src/hooks/api/integrations.test.ts +44 -22
  157. package/src/hooks/api/integrations.ts +229 -84
  158. package/src/hooks/api/webhook-connections.ts +58 -17
  159. package/src/index.ts +2 -1
  160. package/src/linear-callback.ts +21 -16
  161. package/src/pages/gitea-install-page.tsx +1 -10
  162. package/src/pages/linear-callback-page.tsx +18 -30
  163. package/src/pages/linear-install-page.tsx +2 -1
  164. package/src/pages/sentry-callback-page.test.tsx +5 -1
  165. package/src/pages/sentry-callback-page.tsx +28 -25
  166. package/src/pages/sentry-install-page.tsx +2 -1
  167. package/src/pages/slack-callback-page.tsx +20 -26
  168. package/src/pages/slack-install-page.tsx +2 -1
  169. package/src/pages/source-control-onboarding-page.tsx +5 -1
  170. package/src/provider-catalog.ts +7 -6
  171. package/src/routes/github-callback.test.ts +35 -1
  172. package/src/routes/github-callback.tsx +47 -21
  173. package/src/routes/linear-callback.tsx +5 -1
  174. package/src/routes/sentry-callback.tsx +5 -1
  175. package/src/routes/slack-callback.tsx +6 -1
  176. package/src/sentry-callback.test.ts +2 -1
  177. package/src/sentry-callback.ts +28 -20
  178. package/src/slack-callback.ts +21 -16
  179. package/tsconfig.build.tsbuildinfo +1 -1
  180. package/vercel.json +5 -0
  181. package/dist/integration-icon.d.ts +0 -17
  182. package/dist/integration-icon.d.ts.map +0 -1
  183. package/dist/integration-icon.js +0 -22
  184. package/dist/integration-icon.js.map +0 -1
  185. package/src/integration-icon.react.test.tsx +0 -40
  186. package/src/integration-icon.test.ts +0 -19
  187. package/src/integration-icon.tsx +0 -26
@@ -1,13 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useRefreshAuth } from '@shipfox/client-auth';
3
- import { createSingleFlight } from '@shipfox/client-ui';
3
+ import { useRouteSearch } from '@shipfox/client-shell/runtime';
4
+ import { createSingleFlight, sessionStorageOrUndefined } from '@shipfox/client-ui';
4
5
  import { FullPageLoader } from '@shipfox/react-ui/loader';
5
6
  import { toast } from '@shipfox/react-ui/toast';
6
- import { useQueryClient } from '@tanstack/react-query';
7
- import { useNavigate, useSearch } from '@tanstack/react-router';
7
+ import { useNavigate } from '@tanstack/react-router';
8
8
  import { useEffect, useMemo, useState } from 'react';
9
+ import { useCompleteIntegrationCallback } from '#application/complete-integration-callback.js';
9
10
  import { CallbackStatusShell } from '#components/callback-status-shell.js';
10
- import { integrationsQueryKeys, useCompleteSlackCallbackMutation } from '#hooks/api/integrations.js';
11
+ import { useCompleteSlackCallbackMutation } from '#hooks/api/integrations.js';
11
12
  import { classifySlackCallbackError, clearSlackInstallWorkspace, parseSlackCallbackQuery, readSlackInstallWorkspace, serializeSlackCallbackQuery } from '#slack-callback.js';
12
13
  // Retain only recent completions: this bounds long-lived callback pages while
13
14
  // still covering StrictMode and immediate Back/Forward remounts.
@@ -17,46 +18,38 @@ const callbackRequests = createSingleFlight({
17
18
  const completedCallbacks = new Set();
18
19
  const toastedCallbacks = new Set();
19
20
  export function SlackCallbackPage() {
20
- const search = useSearch({
21
- strict: false
22
- });
23
21
  const navigate = useNavigate();
24
22
  const refreshAuth = useRefreshAuth();
25
- const queryClient = useQueryClient();
23
+ const completeIntegrationCallback = useCompleteIntegrationCallback();
26
24
  const { mutateAsync: completeSlackCallback } = useCompleteSlackCallbackMutation();
27
- const params = useMemo(()=>parseSlackCallbackQuery(search), [
28
- search
29
- ]);
30
- const workspaceId = useMemo(()=>readSlackInstallWorkspace(window.sessionStorage), []);
25
+ const params = useRouteSearch(parseSlackCallbackQuery);
26
+ const workspaceId = useMemo(()=>readSlackInstallWorkspace(sessionStorageOrUndefined()), []);
31
27
  const [failure, setFailure] = useState();
32
28
  const [completedWorkspaceId, setCompletedWorkspaceId] = useState();
33
29
  useEffect(()=>{
34
30
  if (!params) return;
35
31
  let disposed = false;
36
32
  const key = serializeSlackCallbackQuery(params);
37
- const request = callbackRequests.run(key, async ()=>await refreshAuth().then(async (session)=>await completeSlackCallback({
38
- query: params,
39
- token: session.accessToken
40
- })));
33
+ const request = callbackRequests.run(key, async ()=>await completeIntegrationCallback({
34
+ input: params,
35
+ refreshAuth,
36
+ complete: async (query, token)=>await completeSlackCallback({
37
+ query,
38
+ token
39
+ })
40
+ }));
41
41
  request.then(async (connection)=>{
42
42
  if (disposed) return;
43
43
  if (completedCallbacks.has(key)) {
44
- setCompletedWorkspaceId(connection.workspace_id);
44
+ setCompletedWorkspaceId(connection.workspaceId);
45
45
  return;
46
46
  }
47
47
  completedCallbacks.add(key);
48
48
  try {
49
- clearSlackInstallWorkspace(window.sessionStorage);
49
+ clearSlackInstallWorkspace(sessionStorageOrUndefined());
50
50
  } catch {
51
51
  // The successful API response remains the source of truth.
52
52
  }
53
- try {
54
- await queryClient.invalidateQueries({
55
- queryKey: integrationsQueryKeys.connectionsByWorkspace(connection.workspace_id)
56
- });
57
- } catch {
58
- // Navigation can continue when cache refresh is unavailable.
59
- }
60
53
  if (disposed) return;
61
54
  if (!toastedCallbacks.has(key)) {
62
55
  toastedCallbacks.add(key);
@@ -66,12 +59,12 @@ export function SlackCallbackPage() {
66
59
  await navigate({
67
60
  to: '/workspaces/$wid/settings/integrations',
68
61
  params: {
69
- wid: connection.workspace_id
62
+ wid: connection.workspaceId
70
63
  },
71
64
  replace: true
72
65
  });
73
66
  } catch {
74
- if (!disposed) setCompletedWorkspaceId(connection.workspace_id);
67
+ if (!disposed) setCompletedWorkspaceId(connection.workspaceId);
75
68
  }
76
69
  }, (error)=>{
77
70
  if (!disposed) setFailure(classifySlackCallbackError(error));
@@ -80,10 +73,10 @@ export function SlackCallbackPage() {
80
73
  disposed = true;
81
74
  };
82
75
  }, [
76
+ completeIntegrationCallback,
83
77
  completeSlackCallback,
84
78
  navigate,
85
79
  params,
86
- queryClient,
87
80
  refreshAuth
88
81
  ]);
89
82
  if (!params) return /*#__PURE__*/ _jsx(CallbackStatusShell, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/slack-callback-page.tsx"],"sourcesContent":["import type {SlackCallbackResponseDto} from '@shipfox/api-integration-slack-dto';\nimport {useRefreshAuth} from '@shipfox/client-auth';\nimport {createSingleFlight} from '@shipfox/client-ui';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useQueryClient} from '@tanstack/react-query';\nimport {useNavigate, useSearch} from '@tanstack/react-router';\nimport {useEffect, useMemo, useState} from 'react';\nimport {CallbackStatusShell} from '#components/callback-status-shell.js';\nimport {integrationsQueryKeys, useCompleteSlackCallbackMutation} from '#hooks/api/integrations.js';\nimport {\n classifySlackCallbackError,\n clearSlackInstallWorkspace,\n parseSlackCallbackQuery,\n readSlackInstallWorkspace,\n type SlackCallbackFailure,\n serializeSlackCallbackQuery,\n} from '#slack-callback.js';\n\n// Retain only recent completions: this bounds long-lived callback pages while\n// still covering StrictMode and immediate Back/Forward remounts.\nconst callbackRequests = createSingleFlight<string, SlackCallbackResponseDto>({\n maxTerminalResults: 32,\n});\nconst completedCallbacks = new Set<string>();\nconst toastedCallbacks = new Set<string>();\n\nexport function SlackCallbackPage() {\n const search = useSearch({strict: false});\n const navigate = useNavigate();\n const refreshAuth = useRefreshAuth();\n const queryClient = useQueryClient();\n const {mutateAsync: completeSlackCallback} = useCompleteSlackCallbackMutation();\n const params = useMemo(() => parseSlackCallbackQuery(search), [search]);\n const workspaceId = useMemo(() => readSlackInstallWorkspace(window.sessionStorage), []);\n const [failure, setFailure] = useState<SlackCallbackFailure>();\n const [completedWorkspaceId, setCompletedWorkspaceId] = useState<string>();\n\n useEffect(() => {\n if (!params) return;\n let disposed = false;\n const key = serializeSlackCallbackQuery(params);\n const request = callbackRequests.run(\n key,\n async () =>\n await refreshAuth().then(\n async (session) =>\n await completeSlackCallback({query: params, token: session.accessToken}),\n ),\n );\n request.then(\n async (connection) => {\n if (disposed) return;\n if (completedCallbacks.has(key)) {\n setCompletedWorkspaceId(connection.workspace_id);\n return;\n }\n completedCallbacks.add(key);\n try {\n clearSlackInstallWorkspace(window.sessionStorage);\n } catch {\n // The successful API response remains the source of truth.\n }\n try {\n await queryClient.invalidateQueries({\n queryKey: integrationsQueryKeys.connectionsByWorkspace(connection.workspace_id),\n });\n } catch {\n // Navigation can continue when cache refresh is unavailable.\n }\n if (disposed) return;\n if (!toastedCallbacks.has(key)) {\n toastedCallbacks.add(key);\n toast.success('Slack installed.');\n }\n try {\n await navigate({\n to: '/workspaces/$wid/settings/integrations',\n params: {wid: connection.workspace_id},\n replace: true,\n });\n } catch {\n if (!disposed) setCompletedWorkspaceId(connection.workspace_id);\n }\n },\n (error: unknown) => {\n if (!disposed) setFailure(classifySlackCallbackError(error));\n },\n );\n return () => {\n disposed = true;\n };\n }, [completeSlackCallback, navigate, params, queryClient, refreshAuth]);\n\n if (!params)\n return (\n <CallbackStatusShell\n title=\"Invalid Slack callback\"\n message=\"This Slack link is missing required parameters. Start the install again from workspace settings.\"\n startOver\n workspaceId={workspaceId}\n installPath=\"/workspaces/$wid/integrations/slack\"\n />\n );\n if (completedWorkspaceId)\n return (\n <CallbackStatusShell\n title=\"Slack connected\"\n message=\"Slack is connected. Continue in integrations settings.\"\n workspaceId={completedWorkspaceId}\n installPath=\"/workspaces/$wid/integrations/slack\"\n />\n );\n if (failure)\n return (\n <CallbackStatusShell\n {...failure}\n workspaceId={workspaceId}\n switchAccount={failure.signIn}\n installPath=\"/workspaces/$wid/integrations/slack\"\n />\n );\n return <FullPageLoader aria-label=\"Connecting Slack\" />;\n}\n"],"names":["useRefreshAuth","createSingleFlight","FullPageLoader","toast","useQueryClient","useNavigate","useSearch","useEffect","useMemo","useState","CallbackStatusShell","integrationsQueryKeys","useCompleteSlackCallbackMutation","classifySlackCallbackError","clearSlackInstallWorkspace","parseSlackCallbackQuery","readSlackInstallWorkspace","serializeSlackCallbackQuery","callbackRequests","maxTerminalResults","completedCallbacks","Set","toastedCallbacks","SlackCallbackPage","search","strict","navigate","refreshAuth","queryClient","mutateAsync","completeSlackCallback","params","workspaceId","window","sessionStorage","failure","setFailure","completedWorkspaceId","setCompletedWorkspaceId","disposed","key","request","run","then","session","query","token","accessToken","connection","has","workspace_id","add","invalidateQueries","queryKey","connectionsByWorkspace","success","to","wid","replace","error","title","message","startOver","installPath","switchAccount","signIn","aria-label"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/pages/slack-callback-page.tsx"],"sourcesContent":["import {useRefreshAuth} from '@shipfox/client-auth';\nimport {useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {createSingleFlight, sessionStorageOrUndefined} from '@shipfox/client-ui';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useEffect, useMemo, useState} from 'react';\nimport {useCompleteIntegrationCallback} from '#application/complete-integration-callback.js';\nimport {CallbackStatusShell} from '#components/callback-status-shell.js';\nimport type {IntegrationConnection} from '#core/models.js';\nimport {useCompleteSlackCallbackMutation} from '#hooks/api/integrations.js';\nimport {\n classifySlackCallbackError,\n clearSlackInstallWorkspace,\n parseSlackCallbackQuery,\n readSlackInstallWorkspace,\n type SlackCallbackFailure,\n serializeSlackCallbackQuery,\n} from '#slack-callback.js';\n\n// Retain only recent completions: this bounds long-lived callback pages while\n// still covering StrictMode and immediate Back/Forward remounts.\nconst callbackRequests = createSingleFlight<string, IntegrationConnection>({\n maxTerminalResults: 32,\n});\nconst completedCallbacks = new Set<string>();\nconst toastedCallbacks = new Set<string>();\n\nexport function SlackCallbackPage() {\n const navigate = useNavigate();\n const refreshAuth = useRefreshAuth();\n const completeIntegrationCallback = useCompleteIntegrationCallback();\n const {mutateAsync: completeSlackCallback} = useCompleteSlackCallbackMutation();\n const params = useRouteSearch(parseSlackCallbackQuery);\n const workspaceId = useMemo(() => readSlackInstallWorkspace(sessionStorageOrUndefined()), []);\n const [failure, setFailure] = useState<SlackCallbackFailure>();\n const [completedWorkspaceId, setCompletedWorkspaceId] = useState<string>();\n\n useEffect(() => {\n if (!params) return;\n let disposed = false;\n const key = serializeSlackCallbackQuery(params);\n const request = callbackRequests.run(\n key,\n async () =>\n await completeIntegrationCallback({\n input: params,\n refreshAuth,\n complete: async (query, token) => await completeSlackCallback({query, token}),\n }),\n );\n request.then(\n async (connection) => {\n if (disposed) return;\n if (completedCallbacks.has(key)) {\n setCompletedWorkspaceId(connection.workspaceId);\n return;\n }\n completedCallbacks.add(key);\n try {\n clearSlackInstallWorkspace(sessionStorageOrUndefined());\n } catch {\n // The successful API response remains the source of truth.\n }\n if (disposed) return;\n if (!toastedCallbacks.has(key)) {\n toastedCallbacks.add(key);\n toast.success('Slack installed.');\n }\n try {\n await navigate({\n to: '/workspaces/$wid/settings/integrations',\n params: {wid: connection.workspaceId},\n replace: true,\n });\n } catch {\n if (!disposed) setCompletedWorkspaceId(connection.workspaceId);\n }\n },\n (error: unknown) => {\n if (!disposed) setFailure(classifySlackCallbackError(error));\n },\n );\n return () => {\n disposed = true;\n };\n }, [completeIntegrationCallback, completeSlackCallback, navigate, params, refreshAuth]);\n\n if (!params)\n return (\n <CallbackStatusShell\n title=\"Invalid Slack callback\"\n message=\"This Slack link is missing required parameters. Start the install again from workspace settings.\"\n startOver\n workspaceId={workspaceId}\n installPath=\"/workspaces/$wid/integrations/slack\"\n />\n );\n if (completedWorkspaceId)\n return (\n <CallbackStatusShell\n title=\"Slack connected\"\n message=\"Slack is connected. Continue in integrations settings.\"\n workspaceId={completedWorkspaceId}\n installPath=\"/workspaces/$wid/integrations/slack\"\n />\n );\n if (failure)\n return (\n <CallbackStatusShell\n {...failure}\n workspaceId={workspaceId}\n switchAccount={failure.signIn}\n installPath=\"/workspaces/$wid/integrations/slack\"\n />\n );\n return <FullPageLoader aria-label=\"Connecting Slack\" />;\n}\n"],"names":["useRefreshAuth","useRouteSearch","createSingleFlight","sessionStorageOrUndefined","FullPageLoader","toast","useNavigate","useEffect","useMemo","useState","useCompleteIntegrationCallback","CallbackStatusShell","useCompleteSlackCallbackMutation","classifySlackCallbackError","clearSlackInstallWorkspace","parseSlackCallbackQuery","readSlackInstallWorkspace","serializeSlackCallbackQuery","callbackRequests","maxTerminalResults","completedCallbacks","Set","toastedCallbacks","SlackCallbackPage","navigate","refreshAuth","completeIntegrationCallback","mutateAsync","completeSlackCallback","params","workspaceId","failure","setFailure","completedWorkspaceId","setCompletedWorkspaceId","disposed","key","request","run","input","complete","query","token","then","connection","has","add","success","to","wid","replace","error","title","message","startOver","installPath","switchAccount","signIn","aria-label"],"mappings":";AAAA,SAAQA,cAAc,QAAO,uBAAuB;AACpD,SAAQC,cAAc,QAAO,gCAAgC;AAC7D,SAAQC,kBAAkB,EAAEC,yBAAyB,QAAO,qBAAqB;AACjF,SAAQC,cAAc,QAAO,2BAA2B;AACxD,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,WAAW,QAAO,yBAAyB;AACnD,SAAQC,SAAS,EAAEC,OAAO,EAAEC,QAAQ,QAAO,QAAQ;AACnD,SAAQC,8BAA8B,QAAO,gDAAgD;AAC7F,SAAQC,mBAAmB,QAAO,uCAAuC;AAEzE,SAAQC,gCAAgC,QAAO,6BAA6B;AAC5E,SACEC,0BAA0B,EAC1BC,0BAA0B,EAC1BC,uBAAuB,EACvBC,yBAAyB,EAEzBC,2BAA2B,QACtB,qBAAqB;AAE5B,8EAA8E;AAC9E,iEAAiE;AACjE,MAAMC,mBAAmBhB,mBAAkD;IACzEiB,oBAAoB;AACtB;AACA,MAAMC,qBAAqB,IAAIC;AAC/B,MAAMC,mBAAmB,IAAID;AAE7B,OAAO,SAASE;IACd,MAAMC,WAAWlB;IACjB,MAAMmB,cAAczB;IACpB,MAAM0B,8BAA8BhB;IACpC,MAAM,EAACiB,aAAaC,qBAAqB,EAAC,GAAGhB;IAC7C,MAAMiB,SAAS5B,eAAec;IAC9B,MAAMe,cAActB,QAAQ,IAAMQ,0BAA0Bb,8BAA8B,EAAE;IAC5F,MAAM,CAAC4B,SAASC,WAAW,GAAGvB;IAC9B,MAAM,CAACwB,sBAAsBC,wBAAwB,GAAGzB;IAExDF,UAAU;QACR,IAAI,CAACsB,QAAQ;QACb,IAAIM,WAAW;QACf,MAAMC,MAAMnB,4BAA4BY;QACxC,MAAMQ,UAAUnB,iBAAiBoB,GAAG,CAClCF,KACA,UACE,MAAMV,4BAA4B;gBAChCa,OAAOV;gBACPJ;gBACAe,UAAU,OAAOC,OAAOC,QAAU,MAAMd,sBAAsB;wBAACa;wBAAOC;oBAAK;YAC7E;QAEJL,QAAQM,IAAI,CACV,OAAOC;YACL,IAAIT,UAAU;YACd,IAAIf,mBAAmByB,GAAG,CAACT,MAAM;gBAC/BF,wBAAwBU,WAAWd,WAAW;gBAC9C;YACF;YACAV,mBAAmB0B,GAAG,CAACV;YACvB,IAAI;gBACFtB,2BAA2BX;YAC7B,EAAE,OAAM;YACN,2DAA2D;YAC7D;YACA,IAAIgC,UAAU;YACd,IAAI,CAACb,iBAAiBuB,GAAG,CAACT,MAAM;gBAC9Bd,iBAAiBwB,GAAG,CAACV;gBACrB/B,MAAM0C,OAAO,CAAC;YAChB;YACA,IAAI;gBACF,MAAMvB,SAAS;oBACbwB,IAAI;oBACJnB,QAAQ;wBAACoB,KAAKL,WAAWd,WAAW;oBAAA;oBACpCoB,SAAS;gBACX;YACF,EAAE,OAAM;gBACN,IAAI,CAACf,UAAUD,wBAAwBU,WAAWd,WAAW;YAC/D;QACF,GACA,CAACqB;YACC,IAAI,CAAChB,UAAUH,WAAWnB,2BAA2BsC;QACvD;QAEF,OAAO;YACLhB,WAAW;QACb;IACF,GAAG;QAACT;QAA6BE;QAAuBJ;QAAUK;QAAQJ;KAAY;IAEtF,IAAI,CAACI,QACH,qBACE,KAAClB;QACCyC,OAAM;QACNC,SAAQ;QACRC,SAAS;QACTxB,aAAaA;QACbyB,aAAY;;IAGlB,IAAItB,sBACF,qBACE,KAACtB;QACCyC,OAAM;QACNC,SAAQ;QACRvB,aAAaG;QACbsB,aAAY;;IAGlB,IAAIxB,SACF,qBACE,KAACpB;QACE,GAAGoB,OAAO;QACXD,aAAaA;QACb0B,eAAezB,QAAQ0B,MAAM;QAC7BF,aAAY;;IAGlB,qBAAO,KAACnD;QAAesD,cAAW;;AACpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"slack-install-page.d.ts","sourceRoot":"","sources":["../../src/pages/slack-install-page.tsx"],"names":[],"mappings":"AAKA,wBAAgB,gBAAgB,gCAgB/B"}
1
+ {"version":3,"file":"slack-install-page.d.ts","sourceRoot":"","sources":["../../src/pages/slack-install-page.tsx"],"names":[],"mappings":"AAMA,wBAAgB,gBAAgB,gCAgB/B"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { sessionStorageOrUndefined } from '@shipfox/client-ui';
2
3
  import { useCallback } from 'react';
3
4
  import { RedirectInstallPage } from '#components/redirect-install-page.js';
4
5
  import { useCreateSlackInstallMutation } from '#hooks/api/integrations.js';
@@ -12,7 +13,7 @@ export function SlackInstallPage() {
12
13
  installRequest: installRequest,
13
14
  errorFallbackMessage: "Could not start Slack install.",
14
15
  loadingLabel: "Connecting Slack",
15
- beforeRedirect: (workspaceId)=>saveSlackInstallWorkspace(window.sessionStorage, workspaceId)
16
+ beforeRedirect: (workspaceId)=>saveSlackInstallWorkspace(sessionStorageOrUndefined(), workspaceId)
16
17
  });
17
18
  }
18
19
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/slack-install-page.tsx"],"sourcesContent":["import {useCallback} from 'react';\nimport {RedirectInstallPage} from '#components/redirect-install-page.js';\nimport {useCreateSlackInstallMutation} from '#hooks/api/integrations.js';\nimport {saveSlackInstallWorkspace} from '#slack-callback.js';\n\nexport function SlackInstallPage() {\n const createInstall = useCreateSlackInstallMutation();\n const installRequest = useCallback(\n async (body: {workspace_id: string}) => await createInstall.mutateAsync(body),\n [createInstall],\n );\n return (\n <RedirectInstallPage\n installRequest={installRequest}\n errorFallbackMessage=\"Could not start Slack install.\"\n loadingLabel=\"Connecting Slack\"\n beforeRedirect={(workspaceId) =>\n saveSlackInstallWorkspace(window.sessionStorage, workspaceId)\n }\n />\n );\n}\n"],"names":["useCallback","RedirectInstallPage","useCreateSlackInstallMutation","saveSlackInstallWorkspace","SlackInstallPage","createInstall","installRequest","body","mutateAsync","errorFallbackMessage","loadingLabel","beforeRedirect","workspaceId","window","sessionStorage"],"mappings":";AAAA,SAAQA,WAAW,QAAO,QAAQ;AAClC,SAAQC,mBAAmB,QAAO,uCAAuC;AACzE,SAAQC,6BAA6B,QAAO,6BAA6B;AACzE,SAAQC,yBAAyB,QAAO,qBAAqB;AAE7D,OAAO,SAASC;IACd,MAAMC,gBAAgBH;IACtB,MAAMI,iBAAiBN,YACrB,OAAOO,OAAiC,MAAMF,cAAcG,WAAW,CAACD,OACxE;QAACF;KAAc;IAEjB,qBACE,KAACJ;QACCK,gBAAgBA;QAChBG,sBAAqB;QACrBC,cAAa;QACbC,gBAAgB,CAACC,cACfT,0BAA0BU,OAAOC,cAAc,EAAEF;;AAIzD"}
1
+ {"version":3,"sources":["../../src/pages/slack-install-page.tsx"],"sourcesContent":["import {sessionStorageOrUndefined} from '@shipfox/client-ui';\nimport {useCallback} from 'react';\nimport {RedirectInstallPage} from '#components/redirect-install-page.js';\nimport {useCreateSlackInstallMutation} from '#hooks/api/integrations.js';\nimport {saveSlackInstallWorkspace} from '#slack-callback.js';\n\nexport function SlackInstallPage() {\n const createInstall = useCreateSlackInstallMutation();\n const installRequest = useCallback(\n async (body: {workspace_id: string}) => await createInstall.mutateAsync(body),\n [createInstall],\n );\n return (\n <RedirectInstallPage\n installRequest={installRequest}\n errorFallbackMessage=\"Could not start Slack install.\"\n loadingLabel=\"Connecting Slack\"\n beforeRedirect={(workspaceId) =>\n saveSlackInstallWorkspace(sessionStorageOrUndefined(), workspaceId)\n }\n />\n );\n}\n"],"names":["sessionStorageOrUndefined","useCallback","RedirectInstallPage","useCreateSlackInstallMutation","saveSlackInstallWorkspace","SlackInstallPage","createInstall","installRequest","body","mutateAsync","errorFallbackMessage","loadingLabel","beforeRedirect","workspaceId"],"mappings":";AAAA,SAAQA,yBAAyB,QAAO,qBAAqB;AAC7D,SAAQC,WAAW,QAAO,QAAQ;AAClC,SAAQC,mBAAmB,QAAO,uCAAuC;AACzE,SAAQC,6BAA6B,QAAO,6BAA6B;AACzE,SAAQC,yBAAyB,QAAO,qBAAqB;AAE7D,OAAO,SAASC;IACd,MAAMC,gBAAgBH;IACtB,MAAMI,iBAAiBN,YACrB,OAAOO,OAAiC,MAAMF,cAAcG,WAAW,CAACD,OACxE;QAACF;KAAc;IAEjB,qBACE,KAACJ;QACCK,gBAAgBA;QAChBG,sBAAqB;QACrBC,cAAa;QACbC,gBAAgB,CAACC,cACfT,0BAA0BJ,6BAA6Ba;;AAI/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"source-control-onboarding-page.d.ts","sourceRoot":"","sources":["../../src/pages/source-control-onboarding-page.tsx"],"names":[],"mappings":"AAKA,wBAAgB,2BAA2B,gCAoB1C"}
1
+ {"version":3,"file":"source-control-onboarding-page.d.ts","sourceRoot":"","sources":["../../src/pages/source-control-onboarding-page.tsx"],"names":[],"mappings":"AAKA,wBAAgB,2BAA2B,gCAwB1C"}
@@ -26,7 +26,11 @@ export function SourceControlOnboardingPage() {
26
26
  ]
27
27
  }),
28
28
  /*#__PURE__*/ _jsx(ProviderGrid, {
29
- providersQuery: providersQuery,
29
+ providers: providersQuery.data ?? [],
30
+ isPending: providersQuery.isPending,
31
+ isFetching: providersQuery.isFetching,
32
+ error: providersQuery.isError ? providersQuery.error : undefined,
33
+ onRetry: ()=>void providersQuery.refetch(),
30
34
  workspaceId: workspace.id,
31
35
  emptyMessage: "Enable at least one source-control provider in the application settings."
32
36
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/source-control-onboarding-page.tsx"],"sourcesContent":["import {useActiveWorkspace} from '@shipfox/client-auth';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {ProviderGrid} from '#components/provider-grid.js';\nimport {useIntegrationProvidersQuery} from '#hooks/api/integrations.js';\n\nexport function SourceControlOnboardingPage() {\n const workspace = useActiveWorkspace();\n const providersQuery = useIntegrationProvidersQuery({capability: 'source_control'});\n\n return (\n <div className=\"mx-auto flex w-full max-w-[640px] flex-col gap-20\">\n <header className=\"flex flex-col gap-8\">\n <Header variant=\"h1\">Install source control</Header>\n <Text size=\"md\" className=\"text-foreground-neutral-muted\">\n Shipfox needs a source control integration to import your repositories.\n </Text>\n </header>\n\n <ProviderGrid\n providersQuery={providersQuery}\n workspaceId={workspace.id}\n emptyMessage=\"Enable at least one source-control provider in the application settings.\"\n />\n </div>\n );\n}\n"],"names":["useActiveWorkspace","Header","Text","ProviderGrid","useIntegrationProvidersQuery","SourceControlOnboardingPage","workspace","providersQuery","capability","div","className","header","variant","size","workspaceId","id","emptyMessage"],"mappings":";AAAA,SAAQA,kBAAkB,QAAO,uBAAuB;AACxD,SAAQC,MAAM,EAAEC,IAAI,QAAO,+BAA+B;AAC1D,SAAQC,YAAY,QAAO,+BAA+B;AAC1D,SAAQC,4BAA4B,QAAO,6BAA6B;AAExE,OAAO,SAASC;IACd,MAAMC,YAAYN;IAClB,MAAMO,iBAAiBH,6BAA6B;QAACI,YAAY;IAAgB;IAEjF,qBACE,MAACC;QAAIC,WAAU;;0BACb,MAACC;gBAAOD,WAAU;;kCAChB,KAACT;wBAAOW,SAAQ;kCAAK;;kCACrB,KAACV;wBAAKW,MAAK;wBAAKH,WAAU;kCAAgC;;;;0BAK5D,KAACP;gBACCI,gBAAgBA;gBAChBO,aAAaR,UAAUS,EAAE;gBACzBC,cAAa;;;;AAIrB"}
1
+ {"version":3,"sources":["../../src/pages/source-control-onboarding-page.tsx"],"sourcesContent":["import {useActiveWorkspace} from '@shipfox/client-auth';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {ProviderGrid} from '#components/provider-grid.js';\nimport {useIntegrationProvidersQuery} from '#hooks/api/integrations.js';\n\nexport function SourceControlOnboardingPage() {\n const workspace = useActiveWorkspace();\n const providersQuery = useIntegrationProvidersQuery({capability: 'source_control'});\n\n return (\n <div className=\"mx-auto flex w-full max-w-[640px] flex-col gap-20\">\n <header className=\"flex flex-col gap-8\">\n <Header variant=\"h1\">Install source control</Header>\n <Text size=\"md\" className=\"text-foreground-neutral-muted\">\n Shipfox needs a source control integration to import your repositories.\n </Text>\n </header>\n\n <ProviderGrid\n providers={providersQuery.data ?? []}\n isPending={providersQuery.isPending}\n isFetching={providersQuery.isFetching}\n error={providersQuery.isError ? providersQuery.error : undefined}\n onRetry={() => void providersQuery.refetch()}\n workspaceId={workspace.id}\n emptyMessage=\"Enable at least one source-control provider in the application settings.\"\n />\n </div>\n );\n}\n"],"names":["useActiveWorkspace","Header","Text","ProviderGrid","useIntegrationProvidersQuery","SourceControlOnboardingPage","workspace","providersQuery","capability","div","className","header","variant","size","providers","data","isPending","isFetching","error","isError","undefined","onRetry","refetch","workspaceId","id","emptyMessage"],"mappings":";AAAA,SAAQA,kBAAkB,QAAO,uBAAuB;AACxD,SAAQC,MAAM,EAAEC,IAAI,QAAO,+BAA+B;AAC1D,SAAQC,YAAY,QAAO,+BAA+B;AAC1D,SAAQC,4BAA4B,QAAO,6BAA6B;AAExE,OAAO,SAASC;IACd,MAAMC,YAAYN;IAClB,MAAMO,iBAAiBH,6BAA6B;QAACI,YAAY;IAAgB;IAEjF,qBACE,MAACC;QAAIC,WAAU;;0BACb,MAACC;gBAAOD,WAAU;;kCAChB,KAACT;wBAAOW,SAAQ;kCAAK;;kCACrB,KAACV;wBAAKW,MAAK;wBAAKH,WAAU;kCAAgC;;;;0BAK5D,KAACP;gBACCW,WAAWP,eAAeQ,IAAI,IAAI,EAAE;gBACpCC,WAAWT,eAAeS,SAAS;gBACnCC,YAAYV,eAAeU,UAAU;gBACrCC,OAAOX,eAAeY,OAAO,GAAGZ,eAAeW,KAAK,GAAGE;gBACvDC,SAAS,IAAM,KAAKd,eAAee,OAAO;gBAC1CC,aAAajB,UAAUkB,EAAE;gBACzBC,cAAa;;;;AAIrB"}
@@ -1 +1 @@
1
- {"version":3,"file":"provider-catalog.d.ts","sourceRoot":"","sources":["../src/provider-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAIrD,MAAM,MAAM,iBAAiB,GACzB,sCAAsC,GACtC,qCAAqC,GACrC,sCAAsC,GACtC,sCAAsC,GACtC,qCAAqC,CAAC;AAE1C,UAAU,yBAAyB;IACjC,IAAI,EAAE,kBAAkB,GAAG,gBAAgB,CAAC;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED,UAAU,yBAAyB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,MAAM,oBAAoB,GAAG,yBAAyB,GAAG,yBAAyB,CAAC;AAEzF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CA8BjE,CAAC"}
1
+ {"version":3,"file":"provider-catalog.d.ts","sourceRoot":"","sources":["../src/provider-catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAIrD,MAAM,MAAM,iBAAiB,GACzB,sCAAsC,GACtC,qCAAqC,GACrC,sCAAsC,GACtC,sCAAsC,GACtC,qCAAqC,CAAC;AAE1C,UAAU,yBAAyB;IACjC,IAAI,EAAE,kBAAkB,GAAG,gBAAgB,CAAC;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED,UAAU,yBAAyB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,MAAM,oBAAoB,GAAG,yBAAyB,GAAG,yBAAyB,CAAC;AAEzF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CA8BjE,CAAC"}
@@ -1,32 +1,33 @@
1
+ import { PROVIDER_ICONS } from '@shipfox/integration-icons';
1
2
  export const PROVIDER_CATALOG = {
2
3
  github: {
3
4
  kind: 'redirect-install',
4
- iconName: 'github',
5
+ iconName: PROVIDER_ICONS.github,
5
6
  setupPath: '/workspaces/$wid/integrations/github'
6
7
  },
7
8
  sentry: {
8
9
  kind: 'redirect-install',
9
- iconName: 'sentry',
10
+ iconName: PROVIDER_ICONS.sentry,
10
11
  setupPath: '/workspaces/$wid/integrations/sentry'
11
12
  },
12
13
  linear: {
13
14
  kind: 'redirect-install',
14
- iconName: 'linear',
15
+ iconName: PROVIDER_ICONS.linear,
15
16
  setupPath: '/workspaces/$wid/integrations/linear'
16
17
  },
17
18
  slack: {
18
19
  kind: 'redirect-install',
19
- iconName: 'slack',
20
+ iconName: PROVIDER_ICONS.slack,
20
21
  setupPath: '/workspaces/$wid/integrations/slack'
21
22
  },
22
23
  gitea: {
23
24
  kind: 'direct-connect',
24
- iconName: 'gitea',
25
+ iconName: PROVIDER_ICONS.gitea,
25
26
  setupPath: '/workspaces/$wid/integrations/gitea'
26
27
  },
27
28
  webhook: {
28
29
  kind: 'modal-connect',
29
- iconName: 'webhookLine'
30
+ iconName: PROVIDER_ICONS.webhook
30
31
  }
31
32
  };
32
33
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider-catalog.ts"],"sourcesContent":["import type {IconName} from '@shipfox/react-ui/icon';\n\n// Literal union (not string) so `<Link to={catalog.setupPath}>` stays typed\n// against TanStack Router's route tree.\nexport type ProviderSetupPath =\n | '/workspaces/$wid/integrations/github'\n | '/workspaces/$wid/integrations/gitea'\n | '/workspaces/$wid/integrations/sentry'\n | '/workspaces/$wid/integrations/linear'\n | '/workspaces/$wid/integrations/slack';\n\ninterface RouteProviderCatalogEntry {\n kind: 'redirect-install' | 'direct-connect';\n iconName: IconName;\n setupPath: ProviderSetupPath;\n}\n\ninterface ModalProviderCatalogEntry {\n kind: 'modal-connect';\n iconName: IconName;\n}\n\nexport type ProviderCatalogEntry = RouteProviderCatalogEntry | ModalProviderCatalogEntry;\n\nexport const PROVIDER_CATALOG: Record<string, ProviderCatalogEntry> = {\n github: {\n kind: 'redirect-install',\n iconName: 'github',\n setupPath: '/workspaces/$wid/integrations/github',\n },\n sentry: {\n kind: 'redirect-install',\n iconName: 'sentry',\n setupPath: '/workspaces/$wid/integrations/sentry',\n },\n linear: {\n kind: 'redirect-install',\n iconName: 'linear',\n setupPath: '/workspaces/$wid/integrations/linear',\n },\n slack: {\n kind: 'redirect-install',\n iconName: 'slack',\n setupPath: '/workspaces/$wid/integrations/slack',\n },\n gitea: {\n kind: 'direct-connect',\n iconName: 'gitea',\n setupPath: '/workspaces/$wid/integrations/gitea',\n },\n webhook: {\n kind: 'modal-connect',\n iconName: 'webhookLine',\n },\n};\n"],"names":["PROVIDER_CATALOG","github","kind","iconName","setupPath","sentry","linear","slack","gitea","webhook"],"mappings":"AAwBA,OAAO,MAAMA,mBAAyD;IACpEC,QAAQ;QACNC,MAAM;QACNC,UAAU;QACVC,WAAW;IACb;IACAC,QAAQ;QACNH,MAAM;QACNC,UAAU;QACVC,WAAW;IACb;IACAE,QAAQ;QACNJ,MAAM;QACNC,UAAU;QACVC,WAAW;IACb;IACAG,OAAO;QACLL,MAAM;QACNC,UAAU;QACVC,WAAW;IACb;IACAI,OAAO;QACLN,MAAM;QACNC,UAAU;QACVC,WAAW;IACb;IACAK,SAAS;QACPP,MAAM;QACNC,UAAU;IACZ;AACF,EAAE"}
1
+ {"version":3,"sources":["../src/provider-catalog.ts"],"sourcesContent":["import {PROVIDER_ICONS} from '@shipfox/integration-icons';\nimport type {IconName} from '@shipfox/react-ui/icon';\n\n// Literal union (not string) so `<Link to={catalog.setupPath}>` stays typed\n// against TanStack Router's route tree.\nexport type ProviderSetupPath =\n | '/workspaces/$wid/integrations/github'\n | '/workspaces/$wid/integrations/gitea'\n | '/workspaces/$wid/integrations/sentry'\n | '/workspaces/$wid/integrations/linear'\n | '/workspaces/$wid/integrations/slack';\n\ninterface RouteProviderCatalogEntry {\n kind: 'redirect-install' | 'direct-connect';\n iconName: IconName;\n setupPath: ProviderSetupPath;\n}\n\ninterface ModalProviderCatalogEntry {\n kind: 'modal-connect';\n iconName: IconName;\n}\n\nexport type ProviderCatalogEntry = RouteProviderCatalogEntry | ModalProviderCatalogEntry;\n\nexport const PROVIDER_CATALOG: Record<string, ProviderCatalogEntry> = {\n github: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.github,\n setupPath: '/workspaces/$wid/integrations/github',\n },\n sentry: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.sentry,\n setupPath: '/workspaces/$wid/integrations/sentry',\n },\n linear: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.linear,\n setupPath: '/workspaces/$wid/integrations/linear',\n },\n slack: {\n kind: 'redirect-install',\n iconName: PROVIDER_ICONS.slack,\n setupPath: '/workspaces/$wid/integrations/slack',\n },\n gitea: {\n kind: 'direct-connect',\n iconName: PROVIDER_ICONS.gitea,\n setupPath: '/workspaces/$wid/integrations/gitea',\n },\n webhook: {\n kind: 'modal-connect',\n iconName: PROVIDER_ICONS.webhook,\n },\n};\n"],"names":["PROVIDER_ICONS","PROVIDER_CATALOG","github","kind","iconName","setupPath","sentry","linear","slack","gitea","webhook"],"mappings":"AAAA,SAAQA,cAAc,QAAO,6BAA6B;AAyB1D,OAAO,MAAMC,mBAAyD;IACpEC,QAAQ;QACNC,MAAM;QACNC,UAAUJ,eAAeE,MAAM;QAC/BG,WAAW;IACb;IACAC,QAAQ;QACNH,MAAM;QACNC,UAAUJ,eAAeM,MAAM;QAC/BD,WAAW;IACb;IACAE,QAAQ;QACNJ,MAAM;QACNC,UAAUJ,eAAeO,MAAM;QAC/BF,WAAW;IACb;IACAG,OAAO;QACLL,MAAM;QACNC,UAAUJ,eAAeQ,KAAK;QAC9BH,WAAW;IACb;IACAI,OAAO;QACLN,MAAM;QACNC,UAAUJ,eAAeS,KAAK;QAC9BJ,WAAW;IACb;IACAK,SAAS;QACPP,MAAM;QACNC,UAAUJ,eAAeU,OAAO;IAClC;AACF,EAAE"}
@@ -1,7 +1,23 @@
1
+ export interface GithubCallbackParams {
2
+ code: string;
3
+ installationId: number;
4
+ state: string;
5
+ setupAction?: string;
6
+ }
7
+ export interface GithubCallbackSearch {
8
+ code?: string;
9
+ installationId?: number;
10
+ state?: string;
11
+ setupAction?: string;
12
+ }
1
13
  declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
14
+ readonly validateSearch: typeof validateGithubCallbackSearch;
2
15
  readonly component: typeof GithubCallbackRoute;
3
16
  }>;
4
17
  export default _default;
5
18
  declare function GithubCallbackRoute(): import("react").JSX.Element;
19
+ export declare function validateGithubCallbackSearch(input: Record<string, unknown>): GithubCallbackSearch;
20
+ export declare function missingCallbackParams(search: GithubCallbackSearch): string[];
21
+ export declare function githubCallbackParams(search: GithubCallbackSearch): GithubCallbackParams | undefined;
6
22
  export declare function githubCallbackErrorMessage(error: unknown): string;
7
23
  //# sourceMappingURL=github-callback.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"github-callback.d.ts","sourceRoot":"","sources":["../../src/routes/github-callback.tsx"],"names":[],"mappings":";;;AAqBA,wBAA6D;AAE7D,iBAAS,mBAAmB,gCAsD3B;AAoCD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjE"}
1
+ {"version":3,"file":"github-callback.d.ts","sourceRoot":"","sources":["../../src/routes/github-callback.tsx"],"names":[],"mappings":"AAWA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;;;;;AAOD,wBAGG;AAEH,iBAAS,mBAAmB,gCAuD3B;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,oBAAoB,CAWjG;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAM5E;AACD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,oBAAoB,GAC3B,oBAAoB,GAAG,SAAS,CAKlC;AAoBD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjE"}
@@ -1,12 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { ApiError, apiRequest } from '@shipfox/client-api';
2
+ import { ApiError } from '@shipfox/client-api';
3
3
  import { useRefreshAuth } from '@shipfox/client-auth';
4
- import { defineRoute } from '@shipfox/client-shell/runtime';
4
+ import { defineRoute, useRouteSearch } from '@shipfox/client-shell/runtime';
5
5
  import { createSingleFlight } from '@shipfox/client-ui';
6
6
  import { FullPageLoader } from '@shipfox/react-ui/loader';
7
7
  import { toast } from '@shipfox/react-ui/toast';
8
- import { useNavigate, useSearch } from '@tanstack/react-router';
8
+ import { useNavigate } from '@tanstack/react-router';
9
9
  import { useEffect } from 'react';
10
+ import { useCompleteIntegrationCallback } from '#application/complete-integration-callback.js';
11
+ import { completeGithubCallback } from '#hooks/api/integrations.js';
10
12
  // Retain only recent completions: this bounds long-lived callback pages while
11
13
  // still covering StrictMode and immediate Back/Forward remounts.
12
14
  const callbackRequests = createSingleFlight({
@@ -14,14 +16,14 @@ const callbackRequests = createSingleFlight({
14
16
  });
15
17
  const toastedCallbacks = new Set();
16
18
  export default defineRoute({
19
+ validateSearch: validateGithubCallbackSearch,
17
20
  component: GithubCallbackRoute
18
21
  });
19
22
  function GithubCallbackRoute() {
20
- const search = useSearch({
21
- strict: false
22
- });
23
+ const search = useRouteSearch(validateGithubCallbackSearch);
23
24
  const navigate = useNavigate();
24
25
  const refreshAuth = useRefreshAuth();
26
+ const completeIntegrationCallback = useCompleteIntegrationCallback();
25
27
  useEffect(()=>{
26
28
  const missing = missingCallbackParams(search);
27
29
  if (missing.length > 0) {
@@ -43,12 +45,13 @@ function GithubCallbackRoute() {
43
45
  }
44
46
  let disposed = false;
45
47
  const key = encodeCallbackQuery(params);
46
- const request = callbackRequests.run(key, async ()=>await refreshAuth().then(async (session)=>{
47
- await apiRequest(`/integrations/github/callback/api?${key}`, {
48
- headers: {
49
- authorization: `Bearer ${session.accessToken}`
50
- }
51
- });
48
+ const request = callbackRequests.run(key, async ()=>void await completeIntegrationCallback({
49
+ input: params,
50
+ refreshAuth,
51
+ complete: async (input, token)=>await completeGithubCallback({
52
+ ...input,
53
+ token
54
+ })
52
55
  }));
53
56
  request.then(()=>{
54
57
  if (disposed) return;
@@ -75,25 +78,44 @@ function GithubCallbackRoute() {
75
78
  disposed = true;
76
79
  };
77
80
  }, [
81
+ completeIntegrationCallback,
78
82
  navigate,
79
83
  refreshAuth,
80
84
  search
81
85
  ]);
82
86
  return /*#__PURE__*/ _jsx(FullPageLoader, {});
83
87
  }
84
- function missingCallbackParams(search) {
88
+ export function validateGithubCallbackSearch(input) {
89
+ const code = stringParam(input.code);
90
+ const installationId = numberParam(input.installation_id);
91
+ const state = stringParam(input.state);
92
+ const setupAction = stringParam(input.setup_action);
93
+ return {
94
+ ...code ? {
95
+ code
96
+ } : {},
97
+ ...installationId === undefined ? {} : {
98
+ installationId
99
+ },
100
+ ...state ? {
101
+ state
102
+ } : {},
103
+ ...setupAction ? {
104
+ setupAction
105
+ } : {}
106
+ };
107
+ }
108
+ export function missingCallbackParams(search) {
85
109
  const missing = [];
86
- if (!stringParam(search.code)) missing.push('code');
87
- if (!numberParam(search.installation_id)) missing.push('installation_id');
88
- if (!stringParam(search.state)) missing.push('state');
110
+ if (!search.code) missing.push('code');
111
+ if (search.installationId === undefined) missing.push('installation_id');
112
+ if (!search.state) missing.push('state');
89
113
  return missing;
90
114
  }
91
- function githubCallbackParams(search) {
92
- const code = stringParam(search.code);
93
- const installationId = numberParam(search.installation_id);
94
- const state = stringParam(search.state);
115
+ export function githubCallbackParams(search) {
116
+ const { code, installationId, state } = search;
95
117
  if (!code || installationId === undefined || !state) return undefined;
96
- const setupAction = stringParam(search.setup_action);
118
+ const { setupAction } = search;
97
119
  return setupAction ? {
98
120
  code,
99
121
  installationId,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/github-callback.tsx"],"sourcesContent":["import {ApiError, apiRequest} from '@shipfox/client-api';\nimport {useRefreshAuth} from '@shipfox/client-auth';\nimport {defineRoute} from '@shipfox/client-shell/runtime';\nimport {createSingleFlight} from '@shipfox/client-ui';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate, useSearch} from '@tanstack/react-router';\nimport {useEffect} from 'react';\n\ninterface GithubCallbackParams {\n code: string;\n installationId: number;\n state: string;\n setupAction?: string;\n}\n\n// Retain only recent completions: this bounds long-lived callback pages while\n// still covering StrictMode and immediate Back/Forward remounts.\nconst callbackRequests = createSingleFlight<string, void>({maxTerminalResults: 32});\nconst toastedCallbacks = new Set<string>();\n\nexport default defineRoute({component: GithubCallbackRoute});\n\nfunction GithubCallbackRoute() {\n const search = useSearch({strict: false});\n const navigate = useNavigate();\n const refreshAuth = useRefreshAuth();\n\n useEffect(() => {\n const missing = missingCallbackParams(search);\n if (missing.length > 0) {\n toast.error(\n `GitHub callback is missing: ${missing.join(', ')}. ${missing.includes('code') ? 'Enable \"Request user authorization (OAuth) during installation\" on the Shipfox GitHub App.' : ''}`,\n );\n navigate({to: '/', replace: true});\n return;\n }\n const params = githubCallbackParams(search);\n if (!params) {\n toast.error('GitHub callback is missing required parameters.');\n navigate({to: '/', replace: true});\n return;\n }\n let disposed = false;\n const key = encodeCallbackQuery(params);\n const request = callbackRequests.run(\n key,\n async () =>\n await refreshAuth().then(async (session) => {\n await apiRequest(`/integrations/github/callback/api?${key}`, {\n headers: {authorization: `Bearer ${session.accessToken}`},\n });\n }),\n );\n request\n .then(() => {\n if (disposed) return;\n if (!toastedCallbacks.has(key)) {\n toastedCallbacks.add(key);\n toast.success('GitHub installed.');\n }\n navigate({to: '/', replace: true});\n })\n .catch((error: unknown) => {\n if (disposed) return;\n if (!toastedCallbacks.has(key)) {\n toastedCallbacks.add(key);\n toast.error(githubCallbackErrorMessage(error));\n }\n navigate({to: '/', replace: true});\n });\n return () => {\n disposed = true;\n };\n }, [navigate, refreshAuth, search]);\n\n return <FullPageLoader />;\n}\n\nfunction missingCallbackParams(search: Record<string, unknown>): string[] {\n const missing: string[] = [];\n if (!stringParam(search.code)) missing.push('code');\n if (!numberParam(search.installation_id)) missing.push('installation_id');\n if (!stringParam(search.state)) missing.push('state');\n return missing;\n}\nfunction githubCallbackParams(search: Record<string, unknown>): GithubCallbackParams | undefined {\n const code = stringParam(search.code);\n const installationId = numberParam(search.installation_id);\n const state = stringParam(search.state);\n if (!code || installationId === undefined || !state) return undefined;\n const setupAction = stringParam(search.setup_action);\n return setupAction ? {code, installationId, state, setupAction} : {code, installationId, state};\n}\nfunction encodeCallbackQuery(params: GithubCallbackParams): string {\n const search = new URLSearchParams();\n search.set('code', params.code);\n search.set('installation_id', params.installationId.toString());\n search.set('state', params.state);\n if (params.setupAction) search.set('setup_action', params.setupAction);\n return search.toString();\n}\nfunction stringParam(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\nfunction numberParam(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string' && value.length > 0) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n }\n return undefined;\n}\nexport function githubCallbackErrorMessage(error: unknown): string {\n if (error instanceof ApiError) return 'GitHub could not be installed. Try again from settings.';\n return 'Could not install GitHub.';\n}\n"],"names":["ApiError","apiRequest","useRefreshAuth","defineRoute","createSingleFlight","FullPageLoader","toast","useNavigate","useSearch","useEffect","callbackRequests","maxTerminalResults","toastedCallbacks","Set","component","GithubCallbackRoute","search","strict","navigate","refreshAuth","missing","missingCallbackParams","length","error","join","includes","to","replace","params","githubCallbackParams","disposed","key","encodeCallbackQuery","request","run","then","session","headers","authorization","accessToken","has","add","success","catch","githubCallbackErrorMessage","stringParam","code","push","numberParam","installation_id","state","installationId","undefined","setupAction","setup_action","URLSearchParams","set","toString","value","Number","isFinite","parsed"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/routes/github-callback.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {useRefreshAuth} from '@shipfox/client-auth';\nimport {defineRoute, useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {createSingleFlight} from '@shipfox/client-ui';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useEffect} from 'react';\nimport {useCompleteIntegrationCallback} from '#application/complete-integration-callback.js';\nimport {completeGithubCallback} from '#hooks/api/integrations.js';\n\nexport interface GithubCallbackParams {\n code: string;\n installationId: number;\n state: string;\n setupAction?: string;\n}\n\nexport interface GithubCallbackSearch {\n code?: string;\n installationId?: number;\n state?: string;\n setupAction?: string;\n}\n\n// Retain only recent completions: this bounds long-lived callback pages while\n// still covering StrictMode and immediate Back/Forward remounts.\nconst callbackRequests = createSingleFlight<string, void>({maxTerminalResults: 32});\nconst toastedCallbacks = new Set<string>();\n\nexport default defineRoute({\n validateSearch: validateGithubCallbackSearch,\n component: GithubCallbackRoute,\n});\n\nfunction GithubCallbackRoute() {\n const search = useRouteSearch(validateGithubCallbackSearch);\n const navigate = useNavigate();\n const refreshAuth = useRefreshAuth();\n const completeIntegrationCallback = useCompleteIntegrationCallback();\n\n useEffect(() => {\n const missing = missingCallbackParams(search);\n if (missing.length > 0) {\n toast.error(\n `GitHub callback is missing: ${missing.join(', ')}. ${missing.includes('code') ? 'Enable \"Request user authorization (OAuth) during installation\" on the Shipfox GitHub App.' : ''}`,\n );\n navigate({to: '/', replace: true});\n return;\n }\n const params = githubCallbackParams(search);\n if (!params) {\n toast.error('GitHub callback is missing required parameters.');\n navigate({to: '/', replace: true});\n return;\n }\n let disposed = false;\n const key = encodeCallbackQuery(params);\n const request = callbackRequests.run(\n key,\n async () =>\n void (await completeIntegrationCallback({\n input: params,\n refreshAuth,\n complete: async (input, token) => await completeGithubCallback({...input, token}),\n })),\n );\n request\n .then(() => {\n if (disposed) return;\n if (!toastedCallbacks.has(key)) {\n toastedCallbacks.add(key);\n toast.success('GitHub installed.');\n }\n navigate({to: '/', replace: true});\n })\n .catch((error: unknown) => {\n if (disposed) return;\n if (!toastedCallbacks.has(key)) {\n toastedCallbacks.add(key);\n toast.error(githubCallbackErrorMessage(error));\n }\n navigate({to: '/', replace: true});\n });\n return () => {\n disposed = true;\n };\n }, [completeIntegrationCallback, navigate, refreshAuth, search]);\n\n return <FullPageLoader />;\n}\n\nexport function validateGithubCallbackSearch(input: Record<string, unknown>): GithubCallbackSearch {\n const code = stringParam(input.code);\n const installationId = numberParam(input.installation_id);\n const state = stringParam(input.state);\n const setupAction = stringParam(input.setup_action);\n return {\n ...(code ? {code} : {}),\n ...(installationId === undefined ? {} : {installationId}),\n ...(state ? {state} : {}),\n ...(setupAction ? {setupAction} : {}),\n };\n}\n\nexport function missingCallbackParams(search: GithubCallbackSearch): string[] {\n const missing: string[] = [];\n if (!search.code) missing.push('code');\n if (search.installationId === undefined) missing.push('installation_id');\n if (!search.state) missing.push('state');\n return missing;\n}\nexport function githubCallbackParams(\n search: GithubCallbackSearch,\n): GithubCallbackParams | undefined {\n const {code, installationId, state} = search;\n if (!code || installationId === undefined || !state) return undefined;\n const {setupAction} = search;\n return setupAction ? {code, installationId, state, setupAction} : {code, installationId, state};\n}\nfunction encodeCallbackQuery(params: GithubCallbackParams): string {\n const search = new URLSearchParams();\n search.set('code', params.code);\n search.set('installation_id', params.installationId.toString());\n search.set('state', params.state);\n if (params.setupAction) search.set('setup_action', params.setupAction);\n return search.toString();\n}\nfunction stringParam(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\nfunction numberParam(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string' && value.length > 0) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n }\n return undefined;\n}\nexport function githubCallbackErrorMessage(error: unknown): string {\n if (error instanceof ApiError) return 'GitHub could not be installed. Try again from settings.';\n return 'Could not install GitHub.';\n}\n"],"names":["ApiError","useRefreshAuth","defineRoute","useRouteSearch","createSingleFlight","FullPageLoader","toast","useNavigate","useEffect","useCompleteIntegrationCallback","completeGithubCallback","callbackRequests","maxTerminalResults","toastedCallbacks","Set","validateSearch","validateGithubCallbackSearch","component","GithubCallbackRoute","search","navigate","refreshAuth","completeIntegrationCallback","missing","missingCallbackParams","length","error","join","includes","to","replace","params","githubCallbackParams","disposed","key","encodeCallbackQuery","request","run","input","complete","token","then","has","add","success","catch","githubCallbackErrorMessage","code","stringParam","installationId","numberParam","installation_id","state","setupAction","setup_action","undefined","push","URLSearchParams","set","toString","value","Number","isFinite","parsed"],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  import { LinearCallbackPage } from '#pages/linear-callback-page.js';
2
+ import { parseLinearCallbackQuery } from '../linear-callback.js';
2
3
  declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
4
+ readonly validateSearch: typeof parseLinearCallbackQuery;
3
5
  readonly component: typeof LinearCallbackPage;
4
6
  }>;
5
7
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"linear-callback.d.ts","sourceRoot":"","sources":["../../src/routes/linear-callback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;;;;AAElE,wBAA4D"}
1
+ {"version":3,"file":"linear-callback.d.ts","sourceRoot":"","sources":["../../src/routes/linear-callback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAC,wBAAwB,EAAC,MAAM,uBAAuB,CAAC;;;;;AAE/D,wBAGG"}
@@ -1,6 +1,8 @@
1
1
  import { defineRoute } from '@shipfox/client-shell/runtime';
2
2
  import { LinearCallbackPage } from '#pages/linear-callback-page.js';
3
+ import { parseLinearCallbackQuery } from '../linear-callback.js';
3
4
  export default defineRoute({
5
+ validateSearch: parseLinearCallbackQuery,
4
6
  component: LinearCallbackPage
5
7
  });
6
8
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/linear-callback.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {LinearCallbackPage} from '#pages/linear-callback-page.js';\n\nexport default defineRoute({component: LinearCallbackPage});\n"],"names":["defineRoute","LinearCallbackPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,kBAAkB,QAAO,iCAAiC;AAElE,eAAeD,YAAY;IAACE,WAAWD;AAAkB,GAAG"}
1
+ {"version":3,"sources":["../../src/routes/linear-callback.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {LinearCallbackPage} from '#pages/linear-callback-page.js';\nimport {parseLinearCallbackQuery} from '../linear-callback.js';\n\nexport default defineRoute({\n validateSearch: parseLinearCallbackQuery,\n component: LinearCallbackPage,\n});\n"],"names":["defineRoute","LinearCallbackPage","parseLinearCallbackQuery","validateSearch","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,kBAAkB,QAAO,iCAAiC;AAClE,SAAQC,wBAAwB,QAAO,wBAAwB;AAE/D,eAAeF,YAAY;IACzBG,gBAAgBD;IAChBE,WAAWH;AACb,GAAG"}
@@ -1,5 +1,7 @@
1
1
  import { SentryCallbackPage } from '#pages/sentry-callback-page.js';
2
+ import { parseSentryCallbackParams } from '../sentry-callback.js';
2
3
  declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
4
+ readonly validateSearch: typeof parseSentryCallbackParams;
3
5
  readonly component: typeof SentryCallbackPage;
4
6
  }>;
5
7
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"sentry-callback.d.ts","sourceRoot":"","sources":["../../src/routes/sentry-callback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;;;;AAElE,wBAA4D"}
1
+ {"version":3,"file":"sentry-callback.d.ts","sourceRoot":"","sources":["../../src/routes/sentry-callback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAC,yBAAyB,EAAC,MAAM,uBAAuB,CAAC;;;;;AAEhE,wBAGG"}
@@ -1,6 +1,8 @@
1
1
  import { defineRoute } from '@shipfox/client-shell/runtime';
2
2
  import { SentryCallbackPage } from '#pages/sentry-callback-page.js';
3
+ import { parseSentryCallbackParams } from '../sentry-callback.js';
3
4
  export default defineRoute({
5
+ validateSearch: parseSentryCallbackParams,
4
6
  component: SentryCallbackPage
5
7
  });
6
8
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/sentry-callback.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {SentryCallbackPage} from '#pages/sentry-callback-page.js';\n\nexport default defineRoute({component: SentryCallbackPage});\n"],"names":["defineRoute","SentryCallbackPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,kBAAkB,QAAO,iCAAiC;AAElE,eAAeD,YAAY;IAACE,WAAWD;AAAkB,GAAG"}
1
+ {"version":3,"sources":["../../src/routes/sentry-callback.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {SentryCallbackPage} from '#pages/sentry-callback-page.js';\nimport {parseSentryCallbackParams} from '../sentry-callback.js';\n\nexport default defineRoute({\n validateSearch: parseSentryCallbackParams,\n component: SentryCallbackPage,\n});\n"],"names":["defineRoute","SentryCallbackPage","parseSentryCallbackParams","validateSearch","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,kBAAkB,QAAO,iCAAiC;AAClE,SAAQC,yBAAyB,QAAO,wBAAwB;AAEhE,eAAeF,YAAY;IACzBG,gBAAgBD;IAChBE,WAAWH;AACb,GAAG"}
@@ -1,5 +1,7 @@
1
1
  import { SlackCallbackPage } from '#pages/slack-callback-page.js';
2
+ import { parseSlackCallbackQuery } from '../slack-callback.js';
2
3
  declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
4
+ readonly validateSearch: typeof parseSlackCallbackQuery;
3
5
  readonly component: typeof SlackCallbackPage;
4
6
  }>;
5
7
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"slack-callback.d.ts","sourceRoot":"","sources":["../../src/routes/slack-callback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,+BAA+B,CAAC;;;;AAChE,wBAA2D"}
1
+ {"version":3,"file":"slack-callback.d.ts","sourceRoot":"","sources":["../../src/routes/slack-callback.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAC,uBAAuB,EAAC,MAAM,sBAAsB,CAAC;;;;;AAE7D,wBAGG"}
@@ -1,6 +1,8 @@
1
1
  import { defineRoute } from '@shipfox/client-shell/runtime';
2
2
  import { SlackCallbackPage } from '#pages/slack-callback-page.js';
3
+ import { parseSlackCallbackQuery } from '../slack-callback.js';
3
4
  export default defineRoute({
5
+ validateSearch: parseSlackCallbackQuery,
4
6
  component: SlackCallbackPage
5
7
  });
6
8
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/slack-callback.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {SlackCallbackPage} from '#pages/slack-callback-page.js';\nexport default defineRoute({component: SlackCallbackPage});\n"],"names":["defineRoute","SlackCallbackPage","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,iBAAiB,QAAO,gCAAgC;AAChE,eAAeD,YAAY;IAACE,WAAWD;AAAiB,GAAG"}
1
+ {"version":3,"sources":["../../src/routes/slack-callback.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {SlackCallbackPage} from '#pages/slack-callback-page.js';\nimport {parseSlackCallbackQuery} from '../slack-callback.js';\n\nexport default defineRoute({\n validateSearch: parseSlackCallbackQuery,\n component: SlackCallbackPage,\n});\n"],"names":["defineRoute","SlackCallbackPage","parseSlackCallbackQuery","validateSearch","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,iBAAiB,QAAO,gCAAgC;AAChE,SAAQC,uBAAuB,QAAO,uBAAuB;AAE7D,eAAeF,YAAY;IACzBG,gBAAgBD;IAChBE,WAAWH;AACb,GAAG"}
@@ -1,5 +1,6 @@
1
+ import { type BrowserStorage } from '@shipfox/client-ui';
1
2
  export declare const SENTRY_INSTALL_WORKSPACE_KEY = "shipfox.sentry-install.workspace-id";
2
- type WorkspaceStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
3
+ type WorkspaceStorage = BrowserStorage | undefined;
3
4
  export declare function saveSentryInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void;
4
5
  export declare function readSentryInstallWorkspace(storage: WorkspaceStorage): string | undefined;
5
6
  export declare function clearSentryInstallWorkspace(storage: WorkspaceStorage): void;
@@ -1 +1 @@
1
- {"version":3,"file":"sentry-callback.d.ts","sourceRoot":"","sources":["../src/sentry-callback.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,4BAA4B,wCAAwC,CAAC;AAElF,KAAK,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC,CAAC;AAK5E,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAM/F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAMxF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAM3E;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,oBAAoB,GAAG,SAAS,CAQlC;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,2BAA2B,GACnC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GACjD;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,CAAC;AAKnB,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,UAAU,EAAE,qBAAqB,EAAE,GAClC,2BAA2B,CAO7B;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GAC5E;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAC,CAAC;AAE5D,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CA8C/E"}
1
+ {"version":3,"file":"sentry-callback.d.ts","sourceRoot":"","sources":["../src/sentry-callback.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,4BAA4B,wCAAwC,CAAC;AAElF,KAAK,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;AAanD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAE/F;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAExF;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE3E;AAMD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,oBAAoB,GAAG,SAAS,CAQlC;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,2BAA2B,GACnC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GACjD;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,CAAC;AAKnB,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,UAAU,EAAE,qBAAqB,EAAE,GAClC,2BAA2B,CAO7B;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GAC5E;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAC,CAAC;AAE5D,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAiD/E"}