@shipfox/client-integrations 3.0.1 → 5.0.0

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 (41) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +37 -0
  3. package/dist/feature.d.ts +11 -0
  4. package/dist/feature.d.ts.map +1 -1
  5. package/dist/feature.js +14 -0
  6. package/dist/feature.js.map +1 -1
  7. package/dist/hooks/api/integrations.d.ts +6 -1
  8. package/dist/hooks/api/integrations.d.ts.map +1 -1
  9. package/dist/hooks/api/integrations.js +15 -12
  10. package/dist/hooks/api/integrations.js.map +1 -1
  11. package/dist/pages/linear-callback-page.d.ts.map +1 -1
  12. package/dist/pages/linear-callback-page.js +9 -13
  13. package/dist/pages/linear-callback-page.js.map +1 -1
  14. package/dist/pages/sentry-callback-page.d.ts.map +1 -1
  15. package/dist/pages/sentry-callback-page.js +5 -19
  16. package/dist/pages/sentry-callback-page.js.map +1 -1
  17. package/dist/pages/slack-callback-page.d.ts.map +1 -1
  18. package/dist/pages/slack-callback-page.js +9 -10
  19. package/dist/pages/slack-callback-page.js.map +1 -1
  20. package/dist/routes/github-callback.d.ts +1 -0
  21. package/dist/routes/github-callback.d.ts.map +1 -1
  22. package/dist/routes/github-callback.js +15 -12
  23. package/dist/routes/github-callback.js.map +1 -1
  24. package/dist/routes/integrations-settings.d.ts +6 -0
  25. package/dist/routes/integrations-settings.d.ts.map +1 -0
  26. package/dist/routes/integrations-settings.js +7 -0
  27. package/dist/routes/integrations-settings.js.map +1 -0
  28. package/dist/tsconfig.test.tsbuildinfo +1 -1
  29. package/package.json +12 -45
  30. package/src/feature.ts +14 -0
  31. package/src/hooks/api/integrations.ts +32 -8
  32. package/src/pages/linear-callback-page.test.tsx +1 -1
  33. package/src/pages/linear-callback-page.tsx +14 -13
  34. package/src/pages/sentry-callback-page.test.tsx +1 -1
  35. package/src/pages/sentry-callback-page.tsx +16 -26
  36. package/src/pages/slack-callback-page.tsx +14 -10
  37. package/src/routes/github-callback.test.ts +17 -0
  38. package/src/routes/github-callback.tsx +15 -12
  39. package/src/routes/integrations-settings.tsx +4 -0
  40. package/test/render.tsx +1 -5
  41. package/tsconfig.build.tsbuildinfo +1 -1
@@ -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 {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// Requests and committed keys remain for the document lifetime so Strict Mode,\n// browser Back, and remounts never replay Slack's single-use grant code.\nconst callbackRequests = new Map<string, Promise<SlackCallbackResponseDto>>();\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 let request = callbackRequests.get(key);\n if (!request) {\n request = refreshAuth().then(\n async (session) => await completeSlackCallback({query: params, token: session.token}),\n );\n callbackRequests.set(key, request);\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","FullPageLoader","toast","useQueryClient","useNavigate","useSearch","useEffect","useMemo","useState","CallbackStatusShell","integrationsQueryKeys","useCompleteSlackCallbackMutation","classifySlackCallbackError","clearSlackInstallWorkspace","parseSlackCallbackQuery","readSlackInstallWorkspace","serializeSlackCallbackQuery","callbackRequests","Map","completedCallbacks","Set","toastedCallbacks","SlackCallbackPage","search","strict","navigate","refreshAuth","queryClient","mutateAsync","completeSlackCallback","params","workspaceId","window","sessionStorage","failure","setFailure","completedWorkspaceId","setCompletedWorkspaceId","disposed","key","request","get","then","session","query","token","set","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 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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"}
@@ -3,4 +3,5 @@ declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
3
3
  }>;
4
4
  export default _default;
5
5
  declare function GithubCallbackRoute(): import("react").JSX.Element;
6
+ export declare function githubCallbackErrorMessage(error: unknown): string;
6
7
  //# 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":";;;AAkBA,wBAA6D;AAE7D,iBAAS,mBAAmB,gCAqD3B"}
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"}
@@ -2,11 +2,16 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { ApiError, apiRequest } from '@shipfox/client-api';
3
3
  import { useRefreshAuth } from '@shipfox/client-auth';
4
4
  import { defineRoute } from '@shipfox/client-shell/runtime';
5
+ import { createSingleFlight } from '@shipfox/client-ui';
5
6
  import { FullPageLoader } from '@shipfox/react-ui/loader';
6
7
  import { toast } from '@shipfox/react-ui/toast';
7
8
  import { useNavigate, useSearch } from '@tanstack/react-router';
8
9
  import { useEffect } from 'react';
9
- const callbackRequests = new Map();
10
+ // Retain only recent completions: this bounds long-lived callback pages while
11
+ // still covering StrictMode and immediate Back/Forward remounts.
12
+ const callbackRequests = createSingleFlight({
13
+ maxTerminalResults: 32
14
+ });
10
15
  const toastedCallbacks = new Set();
11
16
  export default defineRoute({
12
17
  component: GithubCallbackRoute
@@ -38,14 +43,13 @@ function GithubCallbackRoute() {
38
43
  }
39
44
  let disposed = false;
40
45
  const key = encodeCallbackQuery(params);
41
- const request = callbackRequests.get(key) ?? refreshAuth().then(async (session)=>{
42
- await apiRequest(`/integrations/github/callback/api?${key}`, {
43
- headers: {
44
- authorization: `Bearer ${session.token}`
45
- }
46
- });
47
- });
48
- callbackRequests.set(key, request);
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
+ });
52
+ }));
49
53
  request.then(()=>{
50
54
  if (disposed) return;
51
55
  if (!toastedCallbacks.has(key)) {
@@ -120,9 +124,8 @@ function numberParam(value) {
120
124
  }
121
125
  return undefined;
122
126
  }
123
- function githubCallbackErrorMessage(error) {
124
- if (error instanceof ApiError) return error.message;
125
- if (error instanceof Error) return error.message;
127
+ export function githubCallbackErrorMessage(error) {
128
+ if (error instanceof ApiError) return 'GitHub could not be installed. Try again from settings.';
126
129
  return 'Could not install GitHub.';
127
130
  }
128
131
 
@@ -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 {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\nconst callbackRequests = new Map<string, Promise<void>>();\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 =\n callbackRequests.get(key) ??\n refreshAuth().then(async (session) => {\n await apiRequest(`/integrations/github/callback/api?${key}`, {\n headers: {authorization: `Bearer ${session.token}`},\n });\n });\n callbackRequests.set(key, request);\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}\nfunction githubCallbackErrorMessage(error: unknown): string {\n if (error instanceof ApiError) return error.message;\n if (error instanceof Error) return error.message;\n return 'Could not install GitHub.';\n}\n"],"names":["ApiError","apiRequest","useRefreshAuth","defineRoute","FullPageLoader","toast","useNavigate","useSearch","useEffect","callbackRequests","Map","toastedCallbacks","Set","component","GithubCallbackRoute","search","strict","navigate","refreshAuth","missing","missingCallbackParams","length","error","join","includes","to","replace","params","githubCallbackParams","disposed","key","encodeCallbackQuery","request","get","then","session","headers","authorization","token","set","has","add","success","catch","githubCallbackErrorMessage","stringParam","code","push","numberParam","installation_id","state","installationId","undefined","setupAction","setup_action","URLSearchParams","toString","value","Number","isFinite","parsed","message","Error"],"mappings":"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"}
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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"}
@@ -0,0 +1,6 @@
1
+ import { IntegrationGallery } from '#index.js';
2
+ declare const _default: import("@shipfox/client-shell/runtime").RouteImpl<{
3
+ readonly component: typeof IntegrationGallery;
4
+ }>;
5
+ export default _default;
6
+ //# sourceMappingURL=integrations-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integrations-settings.d.ts","sourceRoot":"","sources":["../../src/routes/integrations-settings.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,WAAW,CAAC;;;;AAE7C,wBAA4D"}
@@ -0,0 +1,7 @@
1
+ import { defineRoute } from '@shipfox/client-shell/runtime';
2
+ import { IntegrationGallery } from '#index.js';
3
+ export default defineRoute({
4
+ component: IntegrationGallery
5
+ });
6
+
7
+ //# sourceMappingURL=integrations-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/routes/integrations-settings.tsx"],"sourcesContent":["import {defineRoute} from '@shipfox/client-shell/runtime';\nimport {IntegrationGallery} from '#index.js';\n\nexport default defineRoute({component: IntegrationGallery});\n"],"names":["defineRoute","IntegrationGallery","component"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,kBAAkB,QAAO,YAAY;AAE7C,eAAeD,YAAY;IAACE,WAAWD;AAAkB,GAAG"}