@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +37 -0
- package/dist/feature.d.ts +11 -0
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +14 -0
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/integrations.d.ts +6 -1
- package/dist/hooks/api/integrations.d.ts.map +1 -1
- package/dist/hooks/api/integrations.js +15 -12
- package/dist/hooks/api/integrations.js.map +1 -1
- package/dist/pages/linear-callback-page.d.ts.map +1 -1
- package/dist/pages/linear-callback-page.js +9 -13
- package/dist/pages/linear-callback-page.js.map +1 -1
- package/dist/pages/sentry-callback-page.d.ts.map +1 -1
- package/dist/pages/sentry-callback-page.js +5 -19
- package/dist/pages/sentry-callback-page.js.map +1 -1
- package/dist/pages/slack-callback-page.d.ts.map +1 -1
- package/dist/pages/slack-callback-page.js +9 -10
- package/dist/pages/slack-callback-page.js.map +1 -1
- package/dist/routes/github-callback.d.ts +1 -0
- package/dist/routes/github-callback.d.ts.map +1 -1
- package/dist/routes/github-callback.js +15 -12
- package/dist/routes/github-callback.js.map +1 -1
- package/dist/routes/integrations-settings.d.ts +6 -0
- package/dist/routes/integrations-settings.d.ts.map +1 -0
- package/dist/routes/integrations-settings.js +7 -0
- package/dist/routes/integrations-settings.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +12 -45
- package/src/feature.ts +14 -0
- package/src/hooks/api/integrations.ts +32 -8
- package/src/pages/linear-callback-page.test.tsx +1 -1
- package/src/pages/linear-callback-page.tsx +14 -13
- package/src/pages/sentry-callback-page.test.tsx +1 -1
- package/src/pages/sentry-callback-page.tsx +16 -26
- package/src/pages/slack-callback-page.tsx +14 -10
- package/src/routes/github-callback.test.ts +17 -0
- package/src/routes/github-callback.tsx +15 -12
- package/src/routes/integrations-settings.tsx +4 -0
- package/test/render.tsx +1 -5
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-integrations",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -31,17 +31,17 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@swc/helpers": "^0.5.17",
|
|
33
33
|
"@tanstack/react-form": "^1.32.0",
|
|
34
|
-
"@shipfox/api-integration-core-dto": "
|
|
35
|
-
"@shipfox/api-integration-gitea-dto": "
|
|
36
|
-
"@shipfox/api-integration-github-dto": "
|
|
37
|
-
"@shipfox/api-integration-linear-dto": "
|
|
38
|
-
"@shipfox/api-integration-slack-dto": "
|
|
39
|
-
"@shipfox/api-integration-sentry-dto": "
|
|
40
|
-
"@shipfox/api-integration-webhook-dto": "
|
|
41
|
-
"@shipfox/client-api": "
|
|
42
|
-
"@shipfox/client-auth": "
|
|
43
|
-
"@shipfox/client-shell": "
|
|
44
|
-
"@shipfox/client-ui": "
|
|
34
|
+
"@shipfox/api-integration-core-dto": "8.0.0",
|
|
35
|
+
"@shipfox/api-integration-gitea-dto": "8.0.0",
|
|
36
|
+
"@shipfox/api-integration-github-dto": "8.0.0",
|
|
37
|
+
"@shipfox/api-integration-linear-dto": "8.0.0",
|
|
38
|
+
"@shipfox/api-integration-slack-dto": "8.0.0",
|
|
39
|
+
"@shipfox/api-integration-sentry-dto": "8.0.0",
|
|
40
|
+
"@shipfox/api-integration-webhook-dto": "8.0.0",
|
|
41
|
+
"@shipfox/client-api": "4.0.0",
|
|
42
|
+
"@shipfox/client-auth": "5.0.0",
|
|
43
|
+
"@shipfox/client-shell": "5.0.0",
|
|
44
|
+
"@shipfox/client-ui": "4.0.0",
|
|
45
45
|
"@shipfox/react-ui": "0.3.5"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
@@ -51,39 +51,6 @@
|
|
|
51
51
|
"react": "^19.0.0",
|
|
52
52
|
"react-dom": "^19.0.0"
|
|
53
53
|
},
|
|
54
|
-
"devDependencies": {
|
|
55
|
-
"@argos-ci/cli": "^5.0.0",
|
|
56
|
-
"@argos-ci/storybook": "^6.0.0",
|
|
57
|
-
"@storybook/addon-vitest": "^10.3.6",
|
|
58
|
-
"@storybook/react": "^10.0.0",
|
|
59
|
-
"@storybook/react-vite": "^10.0.0",
|
|
60
|
-
"@tailwindcss/vite": "^4.1.13",
|
|
61
|
-
"@tanstack/react-query": "^5.101.0",
|
|
62
|
-
"@tanstack/react-router": "^1.170.16",
|
|
63
|
-
"@testing-library/jest-dom": "^6.6.3",
|
|
64
|
-
"@testing-library/react": "^16.3.2",
|
|
65
|
-
"@testing-library/user-event": "^14.6.1",
|
|
66
|
-
"@types/react": "^19.1.11",
|
|
67
|
-
"@types/react-dom": "^19.1.7",
|
|
68
|
-
"@vitejs/plugin-react": "^6.0.0",
|
|
69
|
-
"@vitest/browser-playwright": "^4.1.5",
|
|
70
|
-
"jotai": "^2.19.1",
|
|
71
|
-
"jsdom": "^29.0.0",
|
|
72
|
-
"react": "^19.1.1",
|
|
73
|
-
"react-dom": "^19.1.1",
|
|
74
|
-
"storybook": "^10.0.0",
|
|
75
|
-
"storybook-addon-pseudo-states": "^10.0.0",
|
|
76
|
-
"tailwindcss": "^4.1.13",
|
|
77
|
-
"vite": "^8.0.3",
|
|
78
|
-
"vitest": "^4.1.5",
|
|
79
|
-
"@shipfox/biome": "1.8.2",
|
|
80
|
-
"@shipfox/client-test-setup": "0.0.3",
|
|
81
|
-
"@shipfox/swc": "1.2.6",
|
|
82
|
-
"@shipfox/ts-config": "1.3.8",
|
|
83
|
-
"@shipfox/typescript": "1.1.7",
|
|
84
|
-
"@shipfox/vite": "1.2.5",
|
|
85
|
-
"@shipfox/vitest": "1.2.3"
|
|
86
|
-
},
|
|
87
54
|
"scripts": {
|
|
88
55
|
"build": "shipfox-swc",
|
|
89
56
|
"check": "shipfox-biome-check",
|
package/src/feature.ts
CHANGED
|
@@ -53,5 +53,19 @@ export const integrationsFeature = defineClientFeature({
|
|
|
53
53
|
parent: 'workspaceLayout',
|
|
54
54
|
impl: '@shipfox/client-integrations/routes/slack',
|
|
55
55
|
},
|
|
56
|
+
{
|
|
57
|
+
path: '/workspaces/$wid/settings/integrations',
|
|
58
|
+
parent: 'workspaceSettings',
|
|
59
|
+
impl: '@shipfox/client-integrations/routes/integrations-settings',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
settingsSections: [
|
|
63
|
+
{
|
|
64
|
+
id: 'settings.integrations',
|
|
65
|
+
pathSegment: 'integrations',
|
|
66
|
+
label: 'Integrations',
|
|
67
|
+
icon: 'plugLine',
|
|
68
|
+
order: 700,
|
|
69
|
+
},
|
|
56
70
|
],
|
|
57
71
|
});
|
|
@@ -33,7 +33,14 @@ import type {
|
|
|
33
33
|
SlackCallbackResponseDto,
|
|
34
34
|
} from '@shipfox/api-integration-slack-dto';
|
|
35
35
|
import {apiRequest} from '@shipfox/client-api';
|
|
36
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
type FetchQueryOptions,
|
|
38
|
+
queryOptions,
|
|
39
|
+
useInfiniteQuery,
|
|
40
|
+
useMutation,
|
|
41
|
+
useQuery,
|
|
42
|
+
useQueryClient,
|
|
43
|
+
} from '@tanstack/react-query';
|
|
37
44
|
import {serializeLinearCallbackQuery} from '#linear-callback.js';
|
|
38
45
|
import {serializeSlackCallbackQuery} from '#slack-callback.js';
|
|
39
46
|
|
|
@@ -54,6 +61,17 @@ export const integrationsQueryKeys = {
|
|
|
54
61
|
[...integrationsQueryKeys.all, 'repositories', connectionId, search] as const,
|
|
55
62
|
};
|
|
56
63
|
|
|
64
|
+
type SourceConnectionsQueryKey =
|
|
65
|
+
| ReturnType<typeof integrationsQueryKeys.sourceConnections>
|
|
66
|
+
| readonly ['integrations', 'source-connections'];
|
|
67
|
+
|
|
68
|
+
type SourceConnectionsQueryOptions = FetchQueryOptions<
|
|
69
|
+
ListIntegrationConnectionsResponseDto,
|
|
70
|
+
Error,
|
|
71
|
+
ListIntegrationConnectionsResponseDto,
|
|
72
|
+
SourceConnectionsQueryKey
|
|
73
|
+
>;
|
|
74
|
+
|
|
57
75
|
export async function listIntegrationProviders({
|
|
58
76
|
capability,
|
|
59
77
|
signal,
|
|
@@ -108,6 +126,18 @@ export async function listSourceConnections({
|
|
|
108
126
|
};
|
|
109
127
|
}
|
|
110
128
|
|
|
129
|
+
export function sourceConnectionsQueryOptions(
|
|
130
|
+
workspaceId: string | undefined,
|
|
131
|
+
): SourceConnectionsQueryOptions {
|
|
132
|
+
return queryOptions({
|
|
133
|
+
queryKey: workspaceId
|
|
134
|
+
? integrationsQueryKeys.sourceConnections(workspaceId)
|
|
135
|
+
: ([...integrationsQueryKeys.all, 'source-connections'] as const),
|
|
136
|
+
enabled: Boolean(workspaceId),
|
|
137
|
+
queryFn: ({signal}) => listSourceConnections({workspaceId: workspaceId ?? '', signal}),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
111
141
|
export async function createGiteaConnection(body: CreateGiteaConnectionBodyDto) {
|
|
112
142
|
return await apiRequest<CreateGiteaConnectionResponseDto>('/integrations/gitea/connections', {
|
|
113
143
|
method: 'POST',
|
|
@@ -228,13 +258,7 @@ export function useIntegrationProvidersQuery(params?: {capability?: IntegrationC
|
|
|
228
258
|
}
|
|
229
259
|
|
|
230
260
|
export function useSourceConnectionsQuery(workspaceId: string | undefined) {
|
|
231
|
-
return useQuery(
|
|
232
|
-
queryKey: workspaceId
|
|
233
|
-
? integrationsQueryKeys.sourceConnections(workspaceId)
|
|
234
|
-
: [...integrationsQueryKeys.all, 'source-connections'],
|
|
235
|
-
enabled: Boolean(workspaceId),
|
|
236
|
-
queryFn: ({signal}) => listSourceConnections({workspaceId: workspaceId ?? '', signal}),
|
|
237
|
-
});
|
|
261
|
+
return useQuery(sourceConnectionsQueryOptions(workspaceId));
|
|
238
262
|
}
|
|
239
263
|
|
|
240
264
|
export function useIntegrationConnectionsQuery(workspaceId: string | undefined) {
|
|
@@ -13,7 +13,7 @@ vi.mock('@shipfox/client-auth', async (importOriginal) => {
|
|
|
13
13
|
const actual = await importOriginal<typeof import('@shipfox/client-auth')>();
|
|
14
14
|
return {
|
|
15
15
|
...actual,
|
|
16
|
-
useRefreshAuth: () => () => Promise.resolve({
|
|
16
|
+
useRefreshAuth: () => () => Promise.resolve({accessToken: 'test-token'}),
|
|
17
17
|
};
|
|
18
18
|
});
|
|
19
19
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {LinearCallbackResponseDto} from '@shipfox/api-integration-linear-dto';
|
|
2
2
|
import {useRefreshAuth} from '@shipfox/client-auth';
|
|
3
|
+
import {createSingleFlight} from '@shipfox/client-ui';
|
|
3
4
|
import {FullPageLoader} from '@shipfox/react-ui/loader';
|
|
4
5
|
import {toast} from '@shipfox/react-ui/toast';
|
|
5
6
|
import {useQueryClient} from '@tanstack/react-query';
|
|
@@ -16,12 +17,11 @@ import {
|
|
|
16
17
|
serializeLinearCallbackQuery,
|
|
17
18
|
} from '#linear-callback.js';
|
|
18
19
|
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const callbackRequests = new Map<string, Promise<LinearCallbackResponseDto>>();
|
|
20
|
+
// Retain only recent completions: this bounds long-lived callback pages while
|
|
21
|
+
// still covering StrictMode and immediate Back/Forward remounts.
|
|
22
|
+
const callbackRequests = createSingleFlight<string, LinearCallbackResponseDto>({
|
|
23
|
+
maxTerminalResults: 32,
|
|
24
|
+
});
|
|
25
25
|
// Keeps the success toast firing once per distinct callback even though the
|
|
26
26
|
// effect re-runs against the cached request as the mutation identity churns.
|
|
27
27
|
const toastedCallbacks = new Set<string>();
|
|
@@ -46,13 +46,14 @@ export function LinearCallbackPage() {
|
|
|
46
46
|
|
|
47
47
|
let disposed = false;
|
|
48
48
|
const key = serializeLinearCallbackQuery(params);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
const request = callbackRequests.run(
|
|
50
|
+
key,
|
|
51
|
+
async () =>
|
|
52
|
+
await refreshAuth().then(
|
|
53
|
+
async (session) =>
|
|
54
|
+
await completeLinearCallback({query: params, token: session.accessToken}),
|
|
55
|
+
),
|
|
56
|
+
);
|
|
56
57
|
|
|
57
58
|
request.then(
|
|
58
59
|
async (connection) => {
|
|
@@ -13,7 +13,7 @@ vi.mock('@shipfox/client-auth', async (importOriginal) => {
|
|
|
13
13
|
const actual = await importOriginal<typeof import('@shipfox/client-auth')>();
|
|
14
14
|
return {
|
|
15
15
|
...actual,
|
|
16
|
-
useRefreshAuth: () => () => Promise.resolve({
|
|
16
|
+
useRefreshAuth: () => () => Promise.resolve({accessToken: 'test-token'}),
|
|
17
17
|
};
|
|
18
18
|
});
|
|
19
19
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {SentryConnectResponseDto} from '@shipfox/api-integration-sentry-dto';
|
|
2
2
|
import {useAuthState, useRefreshAuth} from '@shipfox/client-auth';
|
|
3
|
+
import {createSingleFlight} from '@shipfox/client-ui';
|
|
3
4
|
import {Button, ButtonLink} from '@shipfox/react-ui/button';
|
|
4
5
|
import {Callout} from '@shipfox/react-ui/callout';
|
|
5
6
|
import {Card} from '@shipfox/react-ui/card';
|
|
@@ -19,12 +20,7 @@ import {
|
|
|
19
20
|
type SentryConnectFailure,
|
|
20
21
|
} from '#sentry-callback.js';
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
// (double click, remount while in flight). Entries are evicted once the
|
|
24
|
-
// request settles, so Retry, and any later attempt carrying a fresh grant
|
|
25
|
-
// code, always issues a genuinely new request instead of replaying a cached
|
|
26
|
-
// outcome.
|
|
27
|
-
const connectRequests = new Map<string, Promise<SentryConnectResponseDto>>();
|
|
23
|
+
const connectRequests = createSingleFlight<string, SentryConnectResponseDto>();
|
|
28
24
|
|
|
29
25
|
export function SentryCallbackPage() {
|
|
30
26
|
const search = useSearch({strict: false});
|
|
@@ -102,26 +98,20 @@ export function SentryCallbackPage() {
|
|
|
102
98
|
setConnectingId(workspaceId);
|
|
103
99
|
setFailure(undefined);
|
|
104
100
|
const key = `${params.installationId}|${workspaceId}|${params.code}`;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// rejection as unhandled — the main chain below owns the user-facing
|
|
120
|
-
// failure handling.
|
|
121
|
-
const evict = () => connectRequests.delete(key);
|
|
122
|
-
request.then(evict, evict);
|
|
123
|
-
connectRequests.set(key, request);
|
|
124
|
-
}
|
|
101
|
+
const request = connectRequests.run(
|
|
102
|
+
key,
|
|
103
|
+
async () =>
|
|
104
|
+
await refreshAuth().then((session) =>
|
|
105
|
+
connectSentry({
|
|
106
|
+
body: {
|
|
107
|
+
workspace_id: workspaceId,
|
|
108
|
+
code: params.code,
|
|
109
|
+
installation_id: params.installationId,
|
|
110
|
+
},
|
|
111
|
+
token: session.accessToken,
|
|
112
|
+
}),
|
|
113
|
+
),
|
|
114
|
+
);
|
|
125
115
|
|
|
126
116
|
request
|
|
127
117
|
.then(async () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {SlackCallbackResponseDto} from '@shipfox/api-integration-slack-dto';
|
|
2
2
|
import {useRefreshAuth} from '@shipfox/client-auth';
|
|
3
|
+
import {createSingleFlight} from '@shipfox/client-ui';
|
|
3
4
|
import {FullPageLoader} from '@shipfox/react-ui/loader';
|
|
4
5
|
import {toast} from '@shipfox/react-ui/toast';
|
|
5
6
|
import {useQueryClient} from '@tanstack/react-query';
|
|
@@ -16,9 +17,11 @@ import {
|
|
|
16
17
|
serializeSlackCallbackQuery,
|
|
17
18
|
} from '#slack-callback.js';
|
|
18
19
|
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
const callbackRequests =
|
|
20
|
+
// Retain only recent completions: this bounds long-lived callback pages while
|
|
21
|
+
// still covering StrictMode and immediate Back/Forward remounts.
|
|
22
|
+
const callbackRequests = createSingleFlight<string, SlackCallbackResponseDto>({
|
|
23
|
+
maxTerminalResults: 32,
|
|
24
|
+
});
|
|
22
25
|
const completedCallbacks = new Set<string>();
|
|
23
26
|
const toastedCallbacks = new Set<string>();
|
|
24
27
|
|
|
@@ -37,13 +40,14 @@ export function SlackCallbackPage() {
|
|
|
37
40
|
if (!params) return;
|
|
38
41
|
let disposed = false;
|
|
39
42
|
const key = serializeSlackCallbackQuery(params);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
const request = callbackRequests.run(
|
|
44
|
+
key,
|
|
45
|
+
async () =>
|
|
46
|
+
await refreshAuth().then(
|
|
47
|
+
async (session) =>
|
|
48
|
+
await completeSlackCallback({query: params, token: session.accessToken}),
|
|
49
|
+
),
|
|
50
|
+
);
|
|
47
51
|
request.then(
|
|
48
52
|
async (connection) => {
|
|
49
53
|
if (disposed) return;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {ApiError} from '@shipfox/client-api';
|
|
2
|
+
import {githubCallbackErrorMessage} from './github-callback.js';
|
|
3
|
+
|
|
4
|
+
describe('githubCallbackErrorMessage', () => {
|
|
5
|
+
it('does not expose an API error message or request URL', () => {
|
|
6
|
+
const error = new ApiError({
|
|
7
|
+
code: 'request-failed',
|
|
8
|
+
message: 'GET https://api.example.test/integrations/github/callback failed',
|
|
9
|
+
status: 500,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const message = githubCallbackErrorMessage(error);
|
|
13
|
+
|
|
14
|
+
expect(message).toBe('GitHub could not be installed. Try again from settings.');
|
|
15
|
+
expect(message).not.toContain('https://');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {ApiError, apiRequest} from '@shipfox/client-api';
|
|
2
2
|
import {useRefreshAuth} from '@shipfox/client-auth';
|
|
3
3
|
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
4
|
+
import {createSingleFlight} from '@shipfox/client-ui';
|
|
4
5
|
import {FullPageLoader} from '@shipfox/react-ui/loader';
|
|
5
6
|
import {toast} from '@shipfox/react-ui/toast';
|
|
6
7
|
import {useNavigate, useSearch} from '@tanstack/react-router';
|
|
@@ -13,7 +14,9 @@ interface GithubCallbackParams {
|
|
|
13
14
|
setupAction?: string;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
// Retain only recent completions: this bounds long-lived callback pages while
|
|
18
|
+
// still covering StrictMode and immediate Back/Forward remounts.
|
|
19
|
+
const callbackRequests = createSingleFlight<string, void>({maxTerminalResults: 32});
|
|
17
20
|
const toastedCallbacks = new Set<string>();
|
|
18
21
|
|
|
19
22
|
export default defineRoute({component: GithubCallbackRoute});
|
|
@@ -40,14 +43,15 @@ function GithubCallbackRoute() {
|
|
|
40
43
|
}
|
|
41
44
|
let disposed = false;
|
|
42
45
|
const key = encodeCallbackQuery(params);
|
|
43
|
-
const request =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
await
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
const request = callbackRequests.run(
|
|
47
|
+
key,
|
|
48
|
+
async () =>
|
|
49
|
+
await refreshAuth().then(async (session) => {
|
|
50
|
+
await apiRequest(`/integrations/github/callback/api?${key}`, {
|
|
51
|
+
headers: {authorization: `Bearer ${session.accessToken}`},
|
|
52
|
+
});
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
51
55
|
request
|
|
52
56
|
.then(() => {
|
|
53
57
|
if (disposed) return;
|
|
@@ -107,8 +111,7 @@ function numberParam(value: unknown): number | undefined {
|
|
|
107
111
|
}
|
|
108
112
|
return undefined;
|
|
109
113
|
}
|
|
110
|
-
function githubCallbackErrorMessage(error: unknown): string {
|
|
111
|
-
if (error instanceof ApiError) return
|
|
112
|
-
if (error instanceof Error) return error.message;
|
|
114
|
+
export function githubCallbackErrorMessage(error: unknown): string {
|
|
115
|
+
if (error instanceof ApiError) return 'GitHub could not be installed. Try again from settings.';
|
|
113
116
|
return 'Could not install GitHub.';
|
|
114
117
|
}
|
package/test/render.tsx
CHANGED
|
@@ -41,11 +41,7 @@ function authState(workspaces: TestWorkspace[]): AuthState {
|
|
|
41
41
|
user: {
|
|
42
42
|
id: '22222222-2222-4222-8222-222222222222',
|
|
43
43
|
email: 'user@example.com',
|
|
44
|
-
|
|
45
|
-
email_verified_at: new Date().toISOString(),
|
|
46
|
-
status: 'active',
|
|
47
|
-
created_at: new Date().toISOString(),
|
|
48
|
-
updated_at: new Date().toISOString(),
|
|
44
|
+
emailVerifiedAt: new Date().toISOString(),
|
|
49
45
|
},
|
|
50
46
|
workspaces,
|
|
51
47
|
};
|