@shipfox/client-onboarding 12.0.2 → 14.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.
@@ -1,9 +1,4 @@
1
- import { type WorkspaceSetupState } from '@shipfox/client-shell/runtime';
2
- import type { QueryClient } from '@tanstack/react-query';
3
- export interface WorkspaceSetupRouteOptions {
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;AACvC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAC;AAKvD,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,uBAAuB,CAAC,EAC5C,WAAW,EACX,WAAW,EACX,QAAQ,GACT,EAAE,0BAA0B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsE3D"}
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, workspaceId)) {
26
+ if (isIntegrationsIndexPath(normalizedPathname, workspaceSlug)) {
27
27
  throw redirect({
28
- to: '/workspaces/$wid/settings/integrations',
28
+ to: '/w/$workspaceSlug/settings/integrations',
29
29
  params: {
30
- wid: workspaceId
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, workspaceId)) {
42
+ if (isIntegrationSetupPath(normalizedPathname, workspaceSlug)) {
43
43
  return {
44
44
  hideProjectNavigation: true
45
45
  };
46
46
  }
47
47
  throw redirect({
48
- to: '/workspaces/$wid/integrations',
48
+ to: '/w/$workspaceSlug/integrations',
49
49
  params: {
50
- wid: workspaceId
50
+ workspaceSlug
51
51
  },
52
52
  replace: true
53
53
  });
54
54
  }
55
- if (isAgentSettingsPath(normalizedPathname, workspaceId)) {
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, workspaceId)) {
62
+ if (isModelProviderOnboardingPath(normalizedPathname, workspaceSlug)) {
63
63
  return {
64
64
  hideProjectNavigation: true
65
65
  };
66
66
  }
67
67
  throw redirect({
68
- to: '/workspaces/$wid/model-provider',
68
+ to: '/w/$workspaceSlug/model-provider',
69
69
  params: {
70
- wid: workspaceId
70
+ workspaceSlug
71
71
  },
72
72
  replace: true
73
73
  });
74
74
  }
75
- if (isProjectCreationPath(normalizedPathname, workspaceId)) {
75
+ if (isProjectCreationPath(normalizedPathname, workspaceSlug)) {
76
76
  return {
77
77
  hideProjectNavigation: true
78
78
  };
79
79
  }
80
80
  throw redirect({
81
- to: '/workspaces/$wid/projects/new',
81
+ to: '/w/$workspaceSlug/projects/new',
82
82
  params: {
83
- wid: workspaceId
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(workspaceId, suffix) {
135
- return `/workspaces/${workspaceId}${suffix}`;
134
+ function workspacePath(workspaceSlug, suffix) {
135
+ return `/w/${workspaceSlug}${suffix}`;
136
136
  }
137
- function isIntegrationsIndexPath(pathname, workspaceId) {
138
- return pathname === workspacePath(workspaceId, '/integrations');
137
+ function isIntegrationsIndexPath(pathname, workspaceSlug) {
138
+ return pathname === workspacePath(workspaceSlug, '/integrations');
139
139
  }
140
- function isIntegrationSetupPath(pathname, workspaceId) {
141
- const basePath = workspacePath(workspaceId, '/integrations');
142
- return pathname === basePath || pathname.startsWith(`${basePath}/`) || pathname === workspacePath(workspaceId, '/settings/integrations');
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, workspaceId) {
145
- return pathname === workspacePath(workspaceId, '/projects/new');
144
+ function isProjectCreationPath(pathname, workspaceSlug) {
145
+ return pathname === workspacePath(workspaceSlug, '/projects/new');
146
146
  }
147
- function isModelProviderOnboardingPath(pathname, workspaceId) {
148
- return pathname === workspacePath(workspaceId, '/model-provider');
147
+ function isModelProviderOnboardingPath(pathname, workspaceSlug) {
148
+ return pathname === workspacePath(workspaceSlug, '/model-provider');
149
149
  }
150
- function isAgentSettingsPath(pathname, workspaceId) {
151
- return pathname === workspacePath(workspaceId, '/settings/agents');
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": "12.0.2",
4
+ "version": "14.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-agent": "12.0.2",
22
- "@shipfox/client-integrations": "12.0.2",
23
- "@shipfox/client-projects": "12.0.2",
24
- "@shipfox/client-shell": "12.0.2"
21
+ "@shipfox/client-agent": "14.0.0",
22
+ "@shipfox/client-integrations": "14.0.0",
23
+ "@shipfox/client-projects": "14.0.0",
24
+ "@shipfox/client-shell": "14.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 keep this seed in sync
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: (params as {wid: string}).wid,
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('/workspaces/$wid', 'Workspace home'),
172
- guardedRoute('/workspaces/$wid/model-provider', 'Model provider onboarding'),
173
- guardedRoute('/workspaces/$wid/integrations', 'VCS onboarding'),
174
- guardedRoute('/workspaces/$wid/integrations/gitea', 'Gitea install'),
175
- guardedRoute('/workspaces/$wid/projects/new', 'Create project'),
176
- guardedRoute('/workspaces/$wid/settings/agents', 'Settings agents'),
177
- guardedRoute('/workspaces/$wid/settings/integrations', 'Settings integrations'),
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(`/workspaces/${WORKSPACE_ID}`, setupFetch({projectsPending: true}));
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(`/workspaces/${WORKSPACE_ID}`, setupFetch({projectsFail: true}));
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl);
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl);
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl, {
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl);
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
- `/workspaces/${WORKSPACE_ID}/integrations`,
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
- `/workspaces/${WORKSPACE_ID}/integrations`,
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
- `/workspaces/${WORKSPACE_ID}/model-provider`,
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
- `/workspaces/${WORKSPACE_ID}/settings/agents`,
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl);
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl, {
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
- `/workspaces/${WORKSPACE_ID}`,
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl, {
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
- `/workspaces/${WORKSPACE_ID}/integrations/gitea`,
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(`/workspaces/${WORKSPACE_ID}`, setupFetch({connectionsFail: true}));
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl);
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl);
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: '/workspaces/$wid/integrations',
529
- params: {wid: WORKSPACE_ID},
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(`/workspaces/${WORKSPACE_ID}`, fetchImpl);
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: '/workspaces/$wid',
580
- params: {wid: WORKSPACE_ID},
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: '/workspaces/$wid',
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: [`/workspaces/${WORKSPACE_ID}`]}),
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: '/workspaces/$wid',
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: [`/workspaces/${WORKSPACE_ID}`]}),
626
+ history: createMemoryHistory({initialEntries: [`/w/${WORKSPACE_SLUG}`]}),
627
627
  routeTree: rootRoute.addChildren([layoutRoute]),
628
628
  context: {queryClient},
629
629
  });