@shipfox/client-onboarding 12.0.2 → 13.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 +21 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/dist/workspace-setup-route.d.ts +3 -8
- package/dist/workspace-setup-route.d.ts.map +1 -1
- package/dist/workspace-setup-route.js +27 -27
- package/dist/workspace-setup-route.js.map +1 -1
- package/package.json +5 -5
- package/src/workspace-setup-route.test.tsx +44 -44
- package/src/workspace-setup-route.ts +29 -31
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import { type WorkspaceSetupState } from '@shipfox/client-shell/runtime';
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
queryClient: QueryClient;
|
|
5
|
-
workspaceId: string;
|
|
6
|
-
pathname: string;
|
|
7
|
-
}
|
|
8
|
-
export declare function loadWorkspaceSetupRoute({ queryClient, workspaceId, pathname, }: WorkspaceSetupRouteOptions): Promise<WorkspaceSetupState>;
|
|
1
|
+
import { type WorkspaceSetupRouteOptions, type WorkspaceSetupState } from '@shipfox/client-shell/runtime';
|
|
2
|
+
export type { WorkspaceSetupRouteOptions } from '@shipfox/client-shell/runtime';
|
|
3
|
+
export declare function loadWorkspaceSetupRoute({ queryClient, workspaceId, workspaceSlug, pathname, }: WorkspaceSetupRouteOptions): Promise<WorkspaceSetupState>;
|
|
9
4
|
//# sourceMappingURL=workspace-setup-route.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-setup-route.d.ts","sourceRoot":"","sources":["../src/workspace-setup-route.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace-setup-route.d.ts","sourceRoot":"","sources":["../src/workspace-setup-route.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACzB,MAAM,+BAA+B,CAAC;AAMvC,YAAY,EAAC,0BAA0B,EAAC,MAAM,+BAA+B,CAAC;AAE9E,wBAAsB,uBAAuB,CAAC,EAC5C,WAAW,EACX,WAAW,EACX,aAAa,EACb,QAAQ,GACT,EAAE,0BAA0B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsE3D"}
|
|
@@ -4,7 +4,7 @@ import { projectExistenceQueryOptions } from '@shipfox/client-projects';
|
|
|
4
4
|
import { userWorkspacesQueryOptions, WorkspaceSetupLoadError } from '@shipfox/client-shell/runtime';
|
|
5
5
|
import { redirect } from '@tanstack/react-router';
|
|
6
6
|
const TRAILING_SLASHES_RE = /\/+$/u;
|
|
7
|
-
export async function loadWorkspaceSetupRoute({ queryClient, workspaceId, pathname }) {
|
|
7
|
+
export async function loadWorkspaceSetupRoute({ queryClient, workspaceId, workspaceSlug, pathname }) {
|
|
8
8
|
const workspace = await fetchWorkspaceSummary(queryClient, workspaceId);
|
|
9
9
|
const workspaceStatus = workspace?.status;
|
|
10
10
|
switch(workspaceStatus){
|
|
@@ -23,11 +23,11 @@ export async function loadWorkspaceSetupRoute({ queryClient, workspaceId, pathna
|
|
|
23
23
|
const projects = await fetchWorkspaceProjectExistence(queryClient, workspaceId);
|
|
24
24
|
const normalizedPathname = normalizePath(pathname);
|
|
25
25
|
if (projects.projects.length > 0) {
|
|
26
|
-
if (isIntegrationsIndexPath(normalizedPathname,
|
|
26
|
+
if (isIntegrationsIndexPath(normalizedPathname, workspaceSlug)) {
|
|
27
27
|
throw redirect({
|
|
28
|
-
to: '/
|
|
28
|
+
to: '/w/$workspaceSlug/settings/integrations',
|
|
29
29
|
params: {
|
|
30
|
-
|
|
30
|
+
workspaceSlug
|
|
31
31
|
},
|
|
32
32
|
replace: true
|
|
33
33
|
});
|
|
@@ -39,48 +39,48 @@ export async function loadWorkspaceSetupRoute({ queryClient, workspaceId, pathna
|
|
|
39
39
|
const sourceConnections = await fetchWorkspaceSourceConnections(queryClient, workspaceId);
|
|
40
40
|
const hasSourceConnection = sourceConnections.length > 0;
|
|
41
41
|
if (!hasSourceConnection) {
|
|
42
|
-
if (isIntegrationSetupPath(normalizedPathname,
|
|
42
|
+
if (isIntegrationSetupPath(normalizedPathname, workspaceSlug)) {
|
|
43
43
|
return {
|
|
44
44
|
hideProjectNavigation: true
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
throw redirect({
|
|
48
|
-
to: '/
|
|
48
|
+
to: '/w/$workspaceSlug/integrations',
|
|
49
49
|
params: {
|
|
50
|
-
|
|
50
|
+
workspaceSlug
|
|
51
51
|
},
|
|
52
52
|
replace: true
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
if (isAgentSettingsPath(normalizedPathname,
|
|
55
|
+
if (isAgentSettingsPath(normalizedPathname, workspaceSlug)) {
|
|
56
56
|
return {
|
|
57
57
|
hideProjectNavigation: true
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
const providerHandled = await hasHandledModelProviderOnboarding(queryClient, workspaceId);
|
|
61
61
|
if (!providerHandled) {
|
|
62
|
-
if (isModelProviderOnboardingPath(normalizedPathname,
|
|
62
|
+
if (isModelProviderOnboardingPath(normalizedPathname, workspaceSlug)) {
|
|
63
63
|
return {
|
|
64
64
|
hideProjectNavigation: true
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
throw redirect({
|
|
68
|
-
to: '/
|
|
68
|
+
to: '/w/$workspaceSlug/model-provider',
|
|
69
69
|
params: {
|
|
70
|
-
|
|
70
|
+
workspaceSlug
|
|
71
71
|
},
|
|
72
72
|
replace: true
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
-
if (isProjectCreationPath(normalizedPathname,
|
|
75
|
+
if (isProjectCreationPath(normalizedPathname, workspaceSlug)) {
|
|
76
76
|
return {
|
|
77
77
|
hideProjectNavigation: true
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
throw redirect({
|
|
81
|
-
to: '/
|
|
81
|
+
to: '/w/$workspaceSlug/projects/new',
|
|
82
82
|
params: {
|
|
83
|
-
|
|
83
|
+
workspaceSlug
|
|
84
84
|
},
|
|
85
85
|
replace: true
|
|
86
86
|
});
|
|
@@ -131,24 +131,24 @@ function normalizePath(pathname) {
|
|
|
131
131
|
if (pathname === '/') return pathname;
|
|
132
132
|
return pathname.replace(TRAILING_SLASHES_RE, '');
|
|
133
133
|
}
|
|
134
|
-
function workspacePath(
|
|
135
|
-
return `/
|
|
134
|
+
function workspacePath(workspaceSlug, suffix) {
|
|
135
|
+
return `/w/${workspaceSlug}${suffix}`;
|
|
136
136
|
}
|
|
137
|
-
function isIntegrationsIndexPath(pathname,
|
|
138
|
-
return pathname === workspacePath(
|
|
137
|
+
function isIntegrationsIndexPath(pathname, workspaceSlug) {
|
|
138
|
+
return pathname === workspacePath(workspaceSlug, '/integrations');
|
|
139
139
|
}
|
|
140
|
-
function isIntegrationSetupPath(pathname,
|
|
141
|
-
const basePath = workspacePath(
|
|
142
|
-
return pathname === basePath || pathname.startsWith(`${basePath}/`) || pathname === workspacePath(
|
|
140
|
+
function isIntegrationSetupPath(pathname, workspaceSlug) {
|
|
141
|
+
const basePath = workspacePath(workspaceSlug, '/integrations');
|
|
142
|
+
return pathname === basePath || pathname.startsWith(`${basePath}/`) || pathname === workspacePath(workspaceSlug, '/settings/integrations');
|
|
143
143
|
}
|
|
144
|
-
function isProjectCreationPath(pathname,
|
|
145
|
-
return pathname === workspacePath(
|
|
144
|
+
function isProjectCreationPath(pathname, workspaceSlug) {
|
|
145
|
+
return pathname === workspacePath(workspaceSlug, '/projects/new');
|
|
146
146
|
}
|
|
147
|
-
function isModelProviderOnboardingPath(pathname,
|
|
148
|
-
return pathname === workspacePath(
|
|
147
|
+
function isModelProviderOnboardingPath(pathname, workspaceSlug) {
|
|
148
|
+
return pathname === workspacePath(workspaceSlug, '/model-provider');
|
|
149
149
|
}
|
|
150
|
-
function isAgentSettingsPath(pathname,
|
|
151
|
-
return pathname === workspacePath(
|
|
150
|
+
function isAgentSettingsPath(pathname, workspaceSlug) {
|
|
151
|
+
return pathname === workspacePath(workspaceSlug, '/settings/agents');
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
//# sourceMappingURL=workspace-setup-route.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workspace-setup-route.ts"],"sourcesContent":["import {\n isModelProviderOnboardingDismissed,\n modelProviderConfigsQueryOptions,\n} from '@shipfox/client-agent';\nimport {\n type IntegrationConnection,\n sourceConnectionsQueryOptions,\n} from '@shipfox/client-integrations';\nimport {projectExistenceQueryOptions} from '@shipfox/client-projects';\nimport {\n userWorkspacesQueryOptions,\n WorkspaceSetupLoadError,\n type WorkspaceSetupState,\n} from '@shipfox/client-shell/runtime';\nimport type {QueryClient} from '@tanstack/react-query';\nimport {redirect} from '@tanstack/react-router';\n\nconst TRAILING_SLASHES_RE = /\\/+$/u;\n\nexport interface WorkspaceSetupRouteOptions {\n queryClient: QueryClient;\n workspaceId: string;\n pathname: string;\n}\n\nexport async function loadWorkspaceSetupRoute({\n queryClient,\n workspaceId,\n pathname,\n}: WorkspaceSetupRouteOptions): Promise<WorkspaceSetupState> {\n const workspace = await fetchWorkspaceSummary(queryClient, workspaceId);\n const workspaceStatus = workspace?.status;\n switch (workspaceStatus) {\n case undefined:\n case 'active':\n break;\n case 'suspended':\n case 'deleted':\n return {hideProjectNavigation: true, unavailable: true};\n default:\n return assertNever(workspaceStatus);\n }\n\n const projects = await fetchWorkspaceProjectExistence(queryClient, workspaceId);\n const normalizedPathname = normalizePath(pathname);\n\n if (projects.projects.length > 0) {\n if (isIntegrationsIndexPath(normalizedPathname, workspaceId)) {\n throw redirect({\n to: '/workspaces/$wid/settings/integrations',\n params: {wid: workspaceId},\n replace: true,\n });\n }\n\n return {hideProjectNavigation: false};\n }\n\n const sourceConnections = await fetchWorkspaceSourceConnections(queryClient, workspaceId);\n const hasSourceConnection = sourceConnections.length > 0;\n\n if (!hasSourceConnection) {\n if (isIntegrationSetupPath(normalizedPathname, workspaceId)) {\n return {hideProjectNavigation: true};\n }\n\n throw redirect({\n to: '/workspaces/$wid/integrations',\n params: {wid: workspaceId},\n replace: true,\n });\n }\n\n if (isAgentSettingsPath(normalizedPathname, workspaceId)) {\n return {hideProjectNavigation: true};\n }\n\n const providerHandled = await hasHandledModelProviderOnboarding(queryClient, workspaceId);\n if (!providerHandled) {\n if (isModelProviderOnboardingPath(normalizedPathname, workspaceId)) {\n return {hideProjectNavigation: true};\n }\n\n throw redirect({\n to: '/workspaces/$wid/model-provider',\n params: {wid: workspaceId},\n replace: true,\n });\n }\n\n if (isProjectCreationPath(normalizedPathname, workspaceId)) {\n return {hideProjectNavigation: true};\n }\n\n throw redirect({\n to: '/workspaces/$wid/projects/new',\n params: {wid: workspaceId},\n replace: true,\n });\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`Unhandled workspace status: ${JSON.stringify(value)}`);\n}\n\nasync function fetchWorkspaceSummary(queryClient: QueryClient, workspaceId: string) {\n try {\n const result = await queryClient.fetchQuery(userWorkspacesQueryOptions());\n return result.memberships.find((workspace) => workspace.id === workspaceId);\n } catch (error) {\n throw new WorkspaceSetupLoadError(error);\n }\n}\n\nasync function fetchWorkspaceProjectExistence(queryClient: QueryClient, workspaceId: string) {\n const options = projectExistenceQueryOptions(workspaceId);\n\n try {\n return await queryClient.fetchQuery(options);\n } catch (error) {\n const cached = queryClient.getQueryData<{projects: unknown[]}>(options.queryKey);\n if (cached !== undefined) return cached;\n throw new WorkspaceSetupLoadError(error);\n }\n}\n\nasync function fetchWorkspaceSourceConnections(queryClient: QueryClient, workspaceId: string) {\n const options = sourceConnectionsQueryOptions(workspaceId);\n\n try {\n return await queryClient.fetchQuery(options);\n } catch (error) {\n const cached = queryClient.getQueryData<IntegrationConnection[]>(options.queryKey);\n if (cached !== undefined) return cached;\n throw new WorkspaceSetupLoadError(error);\n }\n}\n\nasync function hasHandledModelProviderOnboarding(\n queryClient: QueryClient,\n workspaceId: string,\n): Promise<boolean> {\n if (isModelProviderOnboardingDismissed(workspaceId)) return true;\n\n const options = modelProviderConfigsQueryOptions(workspaceId);\n try {\n const configs = await queryClient.fetchQuery(options);\n return configs.configs.length > 0;\n } catch {\n const cached = queryClient.getQueryData<{configs: unknown[]}>(options.queryKey);\n return cached?.configs.length !== 0;\n }\n}\n\nfunction normalizePath(pathname: string) {\n if (pathname === '/') return pathname;\n return pathname.replace(TRAILING_SLASHES_RE, '');\n}\n\nfunction workspacePath(workspaceId: string, suffix: string) {\n return `/workspaces/${workspaceId}${suffix}`;\n}\n\nfunction isIntegrationsIndexPath(pathname: string, workspaceId: string) {\n return pathname === workspacePath(workspaceId, '/integrations');\n}\n\nfunction isIntegrationSetupPath(pathname: string, workspaceId: string) {\n const basePath = workspacePath(workspaceId, '/integrations');\n return (\n pathname === basePath ||\n pathname.startsWith(`${basePath}/`) ||\n pathname === workspacePath(workspaceId, '/settings/integrations')\n );\n}\n\nfunction isProjectCreationPath(pathname: string, workspaceId: string) {\n return pathname === workspacePath(workspaceId, '/projects/new');\n}\n\nfunction isModelProviderOnboardingPath(pathname: string, workspaceId: string) {\n return pathname === workspacePath(workspaceId, '/model-provider');\n}\n\nfunction isAgentSettingsPath(pathname: string, workspaceId: string) {\n return pathname === workspacePath(workspaceId, '/settings/agents');\n}\n"],"names":["isModelProviderOnboardingDismissed","modelProviderConfigsQueryOptions","sourceConnectionsQueryOptions","projectExistenceQueryOptions","userWorkspacesQueryOptions","WorkspaceSetupLoadError","redirect","TRAILING_SLASHES_RE","loadWorkspaceSetupRoute","queryClient","workspaceId","pathname","workspace","fetchWorkspaceSummary","workspaceStatus","status","undefined","hideProjectNavigation","unavailable","assertNever","projects","fetchWorkspaceProjectExistence","normalizedPathname","normalizePath","length","isIntegrationsIndexPath","to","params","wid","replace","sourceConnections","fetchWorkspaceSourceConnections","hasSourceConnection","isIntegrationSetupPath","isAgentSettingsPath","providerHandled","hasHandledModelProviderOnboarding","isModelProviderOnboardingPath","isProjectCreationPath","value","Error","JSON","stringify","result","fetchQuery","memberships","find","id","error","options","cached","getQueryData","queryKey","configs","workspacePath","suffix","basePath","startsWith"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/workspace-setup-route.ts"],"sourcesContent":["import {\n isModelProviderOnboardingDismissed,\n modelProviderConfigsQueryOptions,\n} from '@shipfox/client-agent';\nimport {\n type IntegrationConnection,\n sourceConnectionsQueryOptions,\n} from '@shipfox/client-integrations';\nimport {projectExistenceQueryOptions} from '@shipfox/client-projects';\nimport {\n userWorkspacesQueryOptions,\n WorkspaceSetupLoadError,\n type WorkspaceSetupRouteOptions,\n type WorkspaceSetupState,\n} from '@shipfox/client-shell/runtime';\nimport type {QueryClient} from '@tanstack/react-query';\nimport {redirect} from '@tanstack/react-router';\n\nconst TRAILING_SLASHES_RE = /\\/+$/u;\n\nexport type {WorkspaceSetupRouteOptions} from '@shipfox/client-shell/runtime';\n\nexport async function loadWorkspaceSetupRoute({\n queryClient,\n workspaceId,\n workspaceSlug,\n pathname,\n}: WorkspaceSetupRouteOptions): Promise<WorkspaceSetupState> {\n const workspace = await fetchWorkspaceSummary(queryClient, workspaceId);\n const workspaceStatus = workspace?.status;\n switch (workspaceStatus) {\n case undefined:\n case 'active':\n break;\n case 'suspended':\n case 'deleted':\n return {hideProjectNavigation: true, unavailable: true};\n default:\n return assertNever(workspaceStatus);\n }\n\n const projects = await fetchWorkspaceProjectExistence(queryClient, workspaceId);\n const normalizedPathname = normalizePath(pathname);\n\n if (projects.projects.length > 0) {\n if (isIntegrationsIndexPath(normalizedPathname, workspaceSlug)) {\n throw redirect({\n to: '/w/$workspaceSlug/settings/integrations',\n params: {workspaceSlug},\n replace: true,\n });\n }\n\n return {hideProjectNavigation: false};\n }\n\n const sourceConnections = await fetchWorkspaceSourceConnections(queryClient, workspaceId);\n const hasSourceConnection = sourceConnections.length > 0;\n\n if (!hasSourceConnection) {\n if (isIntegrationSetupPath(normalizedPathname, workspaceSlug)) {\n return {hideProjectNavigation: true};\n }\n\n throw redirect({\n to: '/w/$workspaceSlug/integrations',\n params: {workspaceSlug},\n replace: true,\n });\n }\n\n if (isAgentSettingsPath(normalizedPathname, workspaceSlug)) {\n return {hideProjectNavigation: true};\n }\n\n const providerHandled = await hasHandledModelProviderOnboarding(queryClient, workspaceId);\n if (!providerHandled) {\n if (isModelProviderOnboardingPath(normalizedPathname, workspaceSlug)) {\n return {hideProjectNavigation: true};\n }\n\n throw redirect({\n to: '/w/$workspaceSlug/model-provider',\n params: {workspaceSlug},\n replace: true,\n });\n }\n\n if (isProjectCreationPath(normalizedPathname, workspaceSlug)) {\n return {hideProjectNavigation: true};\n }\n\n throw redirect({\n to: '/w/$workspaceSlug/projects/new',\n params: {workspaceSlug},\n replace: true,\n });\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`Unhandled workspace status: ${JSON.stringify(value)}`);\n}\n\nasync function fetchWorkspaceSummary(queryClient: QueryClient, workspaceId: string) {\n try {\n const result = await queryClient.fetchQuery(userWorkspacesQueryOptions());\n return result.memberships.find((workspace) => workspace.id === workspaceId);\n } catch (error) {\n throw new WorkspaceSetupLoadError(error);\n }\n}\n\nasync function fetchWorkspaceProjectExistence(queryClient: QueryClient, workspaceId: string) {\n const options = projectExistenceQueryOptions(workspaceId);\n\n try {\n return await queryClient.fetchQuery(options);\n } catch (error) {\n const cached = queryClient.getQueryData<{projects: unknown[]}>(options.queryKey);\n if (cached !== undefined) return cached;\n throw new WorkspaceSetupLoadError(error);\n }\n}\n\nasync function fetchWorkspaceSourceConnections(queryClient: QueryClient, workspaceId: string) {\n const options = sourceConnectionsQueryOptions(workspaceId);\n\n try {\n return await queryClient.fetchQuery(options);\n } catch (error) {\n const cached = queryClient.getQueryData<IntegrationConnection[]>(options.queryKey);\n if (cached !== undefined) return cached;\n throw new WorkspaceSetupLoadError(error);\n }\n}\n\nasync function hasHandledModelProviderOnboarding(\n queryClient: QueryClient,\n workspaceId: string,\n): Promise<boolean> {\n if (isModelProviderOnboardingDismissed(workspaceId)) return true;\n\n const options = modelProviderConfigsQueryOptions(workspaceId);\n try {\n const configs = await queryClient.fetchQuery(options);\n return configs.configs.length > 0;\n } catch {\n const cached = queryClient.getQueryData<{configs: unknown[]}>(options.queryKey);\n return cached?.configs.length !== 0;\n }\n}\n\nfunction normalizePath(pathname: string) {\n if (pathname === '/') return pathname;\n return pathname.replace(TRAILING_SLASHES_RE, '');\n}\n\nfunction workspacePath(workspaceSlug: string, suffix: string) {\n return `/w/${workspaceSlug}${suffix}`;\n}\n\nfunction isIntegrationsIndexPath(pathname: string, workspaceSlug: string) {\n return pathname === workspacePath(workspaceSlug, '/integrations');\n}\n\nfunction isIntegrationSetupPath(pathname: string, workspaceSlug: string) {\n const basePath = workspacePath(workspaceSlug, '/integrations');\n return (\n pathname === basePath ||\n pathname.startsWith(`${basePath}/`) ||\n pathname === workspacePath(workspaceSlug, '/settings/integrations')\n );\n}\n\nfunction isProjectCreationPath(pathname: string, workspaceSlug: string) {\n return pathname === workspacePath(workspaceSlug, '/projects/new');\n}\n\nfunction isModelProviderOnboardingPath(pathname: string, workspaceSlug: string) {\n return pathname === workspacePath(workspaceSlug, '/model-provider');\n}\n\nfunction isAgentSettingsPath(pathname: string, workspaceSlug: string) {\n return pathname === workspacePath(workspaceSlug, '/settings/agents');\n}\n"],"names":["isModelProviderOnboardingDismissed","modelProviderConfigsQueryOptions","sourceConnectionsQueryOptions","projectExistenceQueryOptions","userWorkspacesQueryOptions","WorkspaceSetupLoadError","redirect","TRAILING_SLASHES_RE","loadWorkspaceSetupRoute","queryClient","workspaceId","workspaceSlug","pathname","workspace","fetchWorkspaceSummary","workspaceStatus","status","undefined","hideProjectNavigation","unavailable","assertNever","projects","fetchWorkspaceProjectExistence","normalizedPathname","normalizePath","length","isIntegrationsIndexPath","to","params","replace","sourceConnections","fetchWorkspaceSourceConnections","hasSourceConnection","isIntegrationSetupPath","isAgentSettingsPath","providerHandled","hasHandledModelProviderOnboarding","isModelProviderOnboardingPath","isProjectCreationPath","value","Error","JSON","stringify","result","fetchQuery","memberships","find","id","error","options","cached","getQueryData","queryKey","configs","workspacePath","suffix","basePath","startsWith"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-onboarding",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "13.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@swc/helpers": "^0.5.17",
|
|
21
|
-
"@shipfox/client-
|
|
22
|
-
"@shipfox/client-
|
|
23
|
-
"@shipfox/client-
|
|
24
|
-
"@shipfox/client-
|
|
21
|
+
"@shipfox/client-projects": "13.0.0",
|
|
22
|
+
"@shipfox/client-agent": "13.0.0",
|
|
23
|
+
"@shipfox/client-shell": "13.0.0",
|
|
24
|
+
"@shipfox/client-integrations": "13.0.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@tanstack/react-query": "^5.101.0",
|
|
@@ -28,6 +28,7 @@ import {act, cleanup, fireEvent, render, screen, waitFor} from '@testing-library
|
|
|
28
28
|
import {loadWorkspaceSetupRoute} from './workspace-setup-route.js';
|
|
29
29
|
|
|
30
30
|
const WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';
|
|
31
|
+
const WORKSPACE_SLUG = 'acme';
|
|
31
32
|
const MEMBERSHIP_ID = '44444444-4444-4444-8444-444444444444';
|
|
32
33
|
const USER_ID = '55555555-5555-4555-8555-555555555555';
|
|
33
34
|
|
|
@@ -56,7 +57,7 @@ function sourceConnection(overrides: {lifecycle_status?: string} = {}) {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
// The query cache stores the mapped domain shape (camelCase), not the wire
|
|
59
|
-
// DTO `sourceConnection()` mocks for fetch responses
|
|
60
|
+
// DTO `sourceConnection()` mocks for fetch responses: keep this seed in sync
|
|
60
61
|
// with `toIntegrationConnection` in client-integrations.
|
|
61
62
|
function cachedSourceConnection() {
|
|
62
63
|
return {
|
|
@@ -109,6 +110,7 @@ function setupFetch(options: SetupFetchOptions = {}) {
|
|
|
109
110
|
user_id: USER_ID,
|
|
110
111
|
workspace_id: WORKSPACE_ID,
|
|
111
112
|
workspace_name: 'Workspace',
|
|
113
|
+
workspace_slug: 'workspace',
|
|
112
114
|
workspace_status: workspaceStatus,
|
|
113
115
|
created_at: new Date().toISOString(),
|
|
114
116
|
updated_at: new Date().toISOString(),
|
|
@@ -160,7 +162,8 @@ function renderSetupRoute(
|
|
|
160
162
|
beforeLoad: ({context, location, params}) =>
|
|
161
163
|
loadWorkspaceSetupRoute({
|
|
162
164
|
queryClient: context.queryClient,
|
|
163
|
-
workspaceId:
|
|
165
|
+
workspaceId: WORKSPACE_ID,
|
|
166
|
+
workspaceSlug: (params as {workspaceSlug: string}).workspaceSlug,
|
|
164
167
|
pathname: location.pathname,
|
|
165
168
|
}),
|
|
166
169
|
pendingComponent: FullPageLoader,
|
|
@@ -168,13 +171,13 @@ function renderSetupRoute(
|
|
|
168
171
|
component: () => <GuardedRoute label={label} />,
|
|
169
172
|
});
|
|
170
173
|
const routeTree = rootRoute.addChildren([
|
|
171
|
-
guardedRoute('/
|
|
172
|
-
guardedRoute('/
|
|
173
|
-
guardedRoute('/
|
|
174
|
-
guardedRoute('/
|
|
175
|
-
guardedRoute('/
|
|
176
|
-
guardedRoute('/
|
|
177
|
-
guardedRoute('/
|
|
174
|
+
guardedRoute('/w/$workspaceSlug', 'Workspace home'),
|
|
175
|
+
guardedRoute('/w/$workspaceSlug/model-provider', 'Model provider onboarding'),
|
|
176
|
+
guardedRoute('/w/$workspaceSlug/integrations', 'VCS onboarding'),
|
|
177
|
+
guardedRoute('/w/$workspaceSlug/integrations/gitea', 'Gitea install'),
|
|
178
|
+
guardedRoute('/w/$workspaceSlug/projects/new', 'Create project'),
|
|
179
|
+
guardedRoute('/w/$workspaceSlug/settings/agents', 'Settings agents'),
|
|
180
|
+
guardedRoute('/w/$workspaceSlug/settings/integrations', 'Settings integrations'),
|
|
178
181
|
]);
|
|
179
182
|
const router = createRouter({
|
|
180
183
|
defaultPendingMs: 0,
|
|
@@ -210,6 +213,7 @@ function projectStub() {
|
|
|
210
213
|
id: '22222222-2222-4222-8222-222222222222',
|
|
211
214
|
workspace_id: WORKSPACE_ID,
|
|
212
215
|
name: 'Platform',
|
|
216
|
+
slug: 'platform',
|
|
213
217
|
source: {
|
|
214
218
|
connection_id: '33333333-3333-4333-8333-333333333333',
|
|
215
219
|
external_repository_id: 'platform',
|
|
@@ -243,14 +247,14 @@ describe('workspace setup route hook', () => {
|
|
|
243
247
|
});
|
|
244
248
|
|
|
245
249
|
test('renders a loader while the project existence query is pending', async () => {
|
|
246
|
-
renderSetupRoute(`/
|
|
250
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, setupFetch({projectsPending: true}));
|
|
247
251
|
|
|
248
252
|
expect(await screen.findByRole('status', {name: 'Loading'})).toBeInTheDocument();
|
|
249
253
|
expect(screen.queryByText('Workspace home')).not.toBeInTheDocument();
|
|
250
254
|
});
|
|
251
255
|
|
|
252
256
|
test('renders a retryable setup-status error when the project query fails', async () => {
|
|
253
|
-
renderSetupRoute(`/
|
|
257
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, setupFetch({projectsFail: true}));
|
|
254
258
|
|
|
255
259
|
expect(await screen.findByText('Could not load workspace setup')).toBeInTheDocument();
|
|
256
260
|
expect(screen.getByRole('button', {name: 'Retry'})).toBeInTheDocument();
|
|
@@ -263,7 +267,7 @@ describe('workspace setup route hook', () => {
|
|
|
263
267
|
] as const)('stops workspace setup for a %s workspace before loading workspace data', async (workspaceStatus) => {
|
|
264
268
|
const fetchImpl = setupFetch({workspaceStatus, projectsPending: true});
|
|
265
269
|
|
|
266
|
-
renderSetupRoute(`/
|
|
270
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl);
|
|
267
271
|
|
|
268
272
|
expect(await screen.findByText('Workspace unavailable')).toBeInTheDocument();
|
|
269
273
|
expect(screen.getByTestId('project-navigation')).toHaveTextContent('hidden');
|
|
@@ -273,7 +277,7 @@ describe('workspace setup route hook', () => {
|
|
|
273
277
|
test('allows normal workspace content and skips source connections when a project exists', async () => {
|
|
274
278
|
const fetchImpl = setupFetch({projects: [projectStub()]});
|
|
275
279
|
|
|
276
|
-
renderSetupRoute(`/
|
|
280
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl);
|
|
277
281
|
|
|
278
282
|
expect(await screen.findByText('Workspace home')).toBeInTheDocument();
|
|
279
283
|
expect(screen.getByTestId('project-navigation')).toHaveTextContent('visible');
|
|
@@ -285,7 +289,7 @@ describe('workspace setup route hook', () => {
|
|
|
285
289
|
test('keeps cached completed-workspace state when the project refetch fails', async () => {
|
|
286
290
|
const fetchImpl = setupFetch({projectsFail: true});
|
|
287
291
|
|
|
288
|
-
renderSetupRoute(`/
|
|
292
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl, {
|
|
289
293
|
seedQueryClient: (queryClient) => {
|
|
290
294
|
queryClient.setQueryData(projectExistenceQueryOptions(WORKSPACE_ID).queryKey, {
|
|
291
295
|
projects: [projectStub()] as never,
|
|
@@ -312,17 +316,14 @@ describe('workspace setup route hook', () => {
|
|
|
312
316
|
connections: [sourceConnection({lifecycle_status: 'disabled'})],
|
|
313
317
|
});
|
|
314
318
|
|
|
315
|
-
renderSetupRoute(`/
|
|
319
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl);
|
|
316
320
|
|
|
317
321
|
expect(await screen.findByText('VCS onboarding')).toBeInTheDocument();
|
|
318
322
|
expect(screen.getByTestId('project-navigation')).toHaveTextContent('hidden');
|
|
319
323
|
});
|
|
320
324
|
|
|
321
325
|
test('keeps integrations settings available before source-control onboarding', async () => {
|
|
322
|
-
renderSetupRoute(
|
|
323
|
-
`/workspaces/${WORKSPACE_ID}/settings/integrations`,
|
|
324
|
-
setupFetch({connections: []}),
|
|
325
|
-
);
|
|
326
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}/settings/integrations`, setupFetch({connections: []}));
|
|
326
327
|
|
|
327
328
|
expect(await screen.findByText('Settings integrations')).toBeInTheDocument();
|
|
328
329
|
expect(screen.getByTestId('project-navigation')).toHaveTextContent('hidden');
|
|
@@ -330,7 +331,7 @@ describe('workspace setup route hook', () => {
|
|
|
330
331
|
|
|
331
332
|
test('sends a workspace with active VCS and no project to project creation', async () => {
|
|
332
333
|
renderSetupRoute(
|
|
333
|
-
`/
|
|
334
|
+
`/w/${WORKSPACE_SLUG}/integrations`,
|
|
334
335
|
setupFetch({connections: [sourceConnection()]}),
|
|
335
336
|
);
|
|
336
337
|
|
|
@@ -340,7 +341,7 @@ describe('workspace setup route hook', () => {
|
|
|
340
341
|
|
|
341
342
|
test('sends a source-connected workspace with no provider config to provider onboarding', async () => {
|
|
342
343
|
renderSetupRoute(
|
|
343
|
-
`/
|
|
344
|
+
`/w/${WORKSPACE_SLUG}/integrations`,
|
|
344
345
|
setupFetch({
|
|
345
346
|
connections: [sourceConnection()],
|
|
346
347
|
providerConfigs: [],
|
|
@@ -354,7 +355,7 @@ describe('workspace setup route hook', () => {
|
|
|
354
355
|
|
|
355
356
|
test('keeps the provider onboarding route available while provider setup is pending', async () => {
|
|
356
357
|
renderSetupRoute(
|
|
357
|
-
`/
|
|
358
|
+
`/w/${WORKSPACE_SLUG}/model-provider`,
|
|
358
359
|
setupFetch({
|
|
359
360
|
connections: [sourceConnection()],
|
|
360
361
|
providerConfigs: [],
|
|
@@ -368,7 +369,7 @@ describe('workspace setup route hook', () => {
|
|
|
368
369
|
|
|
369
370
|
test('keeps model provider settings available before first project creation', async () => {
|
|
370
371
|
renderSetupRoute(
|
|
371
|
-
`/
|
|
372
|
+
`/w/${WORKSPACE_SLUG}/settings/agents`,
|
|
372
373
|
setupFetch({
|
|
373
374
|
connections: [sourceConnection()],
|
|
374
375
|
providerConfigs: [],
|
|
@@ -384,7 +385,7 @@ describe('workspace setup route hook', () => {
|
|
|
384
385
|
const fetchImpl = setupFetch({connections: [sourceConnection()]});
|
|
385
386
|
dismissModelProviderOnboarding(WORKSPACE_ID);
|
|
386
387
|
|
|
387
|
-
renderSetupRoute(`/
|
|
388
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl);
|
|
388
389
|
|
|
389
390
|
expect(await screen.findByText('Create project')).toBeInTheDocument();
|
|
390
391
|
expect(calledUrls(fetchImpl).some((url) => url.endsWith('/agent/model-providers'))).toBe(false);
|
|
@@ -393,7 +394,7 @@ describe('workspace setup route hook', () => {
|
|
|
393
394
|
test('uses cached provider config state when the provider config refetch fails', async () => {
|
|
394
395
|
const fetchImpl = setupFetch({connections: [sourceConnection()], providerConfigsFail: true});
|
|
395
396
|
|
|
396
|
-
renderSetupRoute(`/
|
|
397
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl, {
|
|
397
398
|
seedQueryClient: (queryClient) => {
|
|
398
399
|
queryClient.setQueryData(modelProviderConfigsQueryOptions(WORKSPACE_ID).queryKey, {
|
|
399
400
|
configs: [
|
|
@@ -422,7 +423,7 @@ describe('workspace setup route hook', () => {
|
|
|
422
423
|
|
|
423
424
|
test('fails open to project creation when provider configs cannot load without cache', async () => {
|
|
424
425
|
renderSetupRoute(
|
|
425
|
-
`/
|
|
426
|
+
`/w/${WORKSPACE_SLUG}`,
|
|
426
427
|
setupFetch({connections: [sourceConnection()], providerConfigsFail: true}),
|
|
427
428
|
);
|
|
428
429
|
|
|
@@ -433,7 +434,7 @@ describe('workspace setup route hook', () => {
|
|
|
433
434
|
test('keeps cached source-connection state when the source refetch fails', async () => {
|
|
434
435
|
const fetchImpl = setupFetch({connectionsFail: true});
|
|
435
436
|
|
|
436
|
-
renderSetupRoute(`/
|
|
437
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl, {
|
|
437
438
|
seedQueryClient: (queryClient) => {
|
|
438
439
|
queryClient.setQueryData(sourceConnectionsQueryOptions(WORKSPACE_ID).queryKey, [
|
|
439
440
|
cachedSourceConnection(),
|
|
@@ -452,10 +453,7 @@ describe('workspace setup route hook', () => {
|
|
|
452
453
|
});
|
|
453
454
|
|
|
454
455
|
test('redirects the completed workspace integrations index to settings integrations', async () => {
|
|
455
|
-
renderSetupRoute(
|
|
456
|
-
`/workspaces/${WORKSPACE_ID}/integrations`,
|
|
457
|
-
setupFetch({projects: [projectStub()]}),
|
|
458
|
-
);
|
|
456
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}/integrations`, setupFetch({projects: [projectStub()]}));
|
|
459
457
|
|
|
460
458
|
expect(await screen.findByText('Settings integrations')).toBeInTheDocument();
|
|
461
459
|
expect(screen.getByTestId('project-navigation')).toHaveTextContent('visible');
|
|
@@ -463,7 +461,7 @@ describe('workspace setup route hook', () => {
|
|
|
463
461
|
|
|
464
462
|
test('keeps completed workspace integration install routes available', async () => {
|
|
465
463
|
renderSetupRoute(
|
|
466
|
-
`/
|
|
464
|
+
`/w/${WORKSPACE_SLUG}/integrations/gitea`,
|
|
467
465
|
setupFetch({projects: [projectStub()]}),
|
|
468
466
|
);
|
|
469
467
|
|
|
@@ -472,7 +470,7 @@ describe('workspace setup route hook', () => {
|
|
|
472
470
|
});
|
|
473
471
|
|
|
474
472
|
test('renders a retryable setup-status error when the source connection query fails', async () => {
|
|
475
|
-
renderSetupRoute(`/
|
|
473
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, setupFetch({connectionsFail: true}));
|
|
476
474
|
|
|
477
475
|
expect(await screen.findByText('Could not load workspace setup')).toBeInTheDocument();
|
|
478
476
|
expect(screen.getByRole('button', {name: 'Retry'})).toBeInTheDocument();
|
|
@@ -492,6 +490,7 @@ describe('workspace setup route hook', () => {
|
|
|
492
490
|
user_id: USER_ID,
|
|
493
491
|
workspace_id: WORKSPACE_ID,
|
|
494
492
|
workspace_name: 'Workspace',
|
|
493
|
+
workspace_slug: 'workspace',
|
|
495
494
|
workspace_status: 'active',
|
|
496
495
|
created_at: new Date().toISOString(),
|
|
497
496
|
updated_at: new Date().toISOString(),
|
|
@@ -509,7 +508,7 @@ describe('workspace setup route hook', () => {
|
|
|
509
508
|
return Promise.resolve(jsonResponse({}, {status: 404}));
|
|
510
509
|
});
|
|
511
510
|
|
|
512
|
-
renderSetupRoute(`/
|
|
511
|
+
renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl);
|
|
513
512
|
|
|
514
513
|
fireEvent.click(await screen.findByRole('button', {name: 'Retry'}));
|
|
515
514
|
|
|
@@ -519,14 +518,14 @@ describe('workspace setup route hook', () => {
|
|
|
519
518
|
|
|
520
519
|
test('re-evaluates the guard on navigation between children without refetching fresh existence', async () => {
|
|
521
520
|
const fetchImpl = setupFetch({projects: [projectStub()]});
|
|
522
|
-
const {router} = renderSetupRoute(`/
|
|
521
|
+
const {router} = renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl);
|
|
523
522
|
|
|
524
523
|
expect(await screen.findByText('Workspace home')).toBeInTheDocument();
|
|
525
524
|
|
|
526
525
|
await act(async () => {
|
|
527
526
|
await router.navigate({
|
|
528
|
-
to: '/
|
|
529
|
-
params: {
|
|
527
|
+
to: '/w/$workspaceSlug/integrations',
|
|
528
|
+
params: {workspaceSlug: WORKSPACE_SLUG},
|
|
530
529
|
});
|
|
531
530
|
});
|
|
532
531
|
|
|
@@ -548,6 +547,7 @@ describe('workspace setup route hook', () => {
|
|
|
548
547
|
user_id: USER_ID,
|
|
549
548
|
workspace_id: WORKSPACE_ID,
|
|
550
549
|
workspace_name: 'Workspace',
|
|
550
|
+
workspace_slug: 'workspace',
|
|
551
551
|
workspace_status: 'active',
|
|
552
552
|
created_at: new Date().toISOString(),
|
|
553
553
|
updated_at: new Date().toISOString(),
|
|
@@ -564,7 +564,7 @@ describe('workspace setup route hook', () => {
|
|
|
564
564
|
}
|
|
565
565
|
return Promise.resolve(jsonResponse({}, {status: 404}));
|
|
566
566
|
});
|
|
567
|
-
const {queryClient, router} = renderSetupRoute(`/
|
|
567
|
+
const {queryClient, router} = renderSetupRoute(`/w/${WORKSPACE_SLUG}`, fetchImpl);
|
|
568
568
|
|
|
569
569
|
expect(await screen.findByText('Create project')).toBeInTheDocument();
|
|
570
570
|
projects = [projectStub()];
|
|
@@ -576,8 +576,8 @@ describe('workspace setup route hook', () => {
|
|
|
576
576
|
|
|
577
577
|
await act(async () => {
|
|
578
578
|
await router.navigate({
|
|
579
|
-
to: '/
|
|
580
|
-
params: {
|
|
579
|
+
to: '/w/$workspaceSlug',
|
|
580
|
+
params: {workspaceSlug: WORKSPACE_SLUG},
|
|
581
581
|
});
|
|
582
582
|
});
|
|
583
583
|
|
|
@@ -592,14 +592,14 @@ describe('workspace setup route hook', () => {
|
|
|
592
592
|
});
|
|
593
593
|
const throwingRoute = createRoute({
|
|
594
594
|
getParentRoute: () => rootRoute,
|
|
595
|
-
path: '/
|
|
595
|
+
path: '/w/$workspaceSlug',
|
|
596
596
|
beforeLoad: () => ({hideProjectNavigation: false}),
|
|
597
597
|
errorComponent: WorkspaceLayoutErrorRoute,
|
|
598
598
|
component: ThrowingWorkspaceRoute,
|
|
599
599
|
});
|
|
600
600
|
const router = createRouter({
|
|
601
601
|
defaultPendingMs: 0,
|
|
602
|
-
history: createMemoryHistory({initialEntries: [`/
|
|
602
|
+
history: createMemoryHistory({initialEntries: [`/w/${WORKSPACE_SLUG}`]}),
|
|
603
603
|
routeTree: rootRoute.addChildren([throwingRoute]),
|
|
604
604
|
context: {queryClient},
|
|
605
605
|
});
|
|
@@ -617,13 +617,13 @@ describe('workspace setup route hook', () => {
|
|
|
617
617
|
});
|
|
618
618
|
const layoutRoute = createRoute({
|
|
619
619
|
getParentRoute: () => rootRoute,
|
|
620
|
-
path: '/
|
|
620
|
+
path: '/w/$workspaceSlug',
|
|
621
621
|
beforeLoad: () => undefined,
|
|
622
622
|
component: WorkspaceLayoutParity,
|
|
623
623
|
});
|
|
624
624
|
const router = createRouter({
|
|
625
625
|
defaultPendingMs: 0,
|
|
626
|
-
history: createMemoryHistory({initialEntries: [`/
|
|
626
|
+
history: createMemoryHistory({initialEntries: [`/w/${WORKSPACE_SLUG}`]}),
|
|
627
627
|
routeTree: rootRoute.addChildren([layoutRoute]),
|
|
628
628
|
context: {queryClient},
|
|
629
629
|
});
|