@uipath/apollo-vertex 0.3.0 → 1.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/README.md CHANGED
@@ -27,14 +27,14 @@ Everything, including the app shell, charts, feature flags, Solution Tests and A
27
27
  from the package root. Translation JSON files are available at `@uipath/apollo-vertex/locales/*`.
28
28
 
29
29
  npm 7+ and pnpm install the packages in `peerDependencies` automatically. Install these three
30
- yourself, because they are not on the public npm registry:
30
+ yourself:
31
31
 
32
32
  - `@uipath/vs-core`
33
33
  - `@tanstack/react-db`
34
34
  - `@uipath/proteus-client`
35
35
 
36
36
  They are declared only in `peerDependenciesMeta` (as optional) so that pnpm does not try to install them
37
- in this workspace.
37
+ in this workspace. The two `@uipath` packages are not on the public npm registry.
38
38
 
39
39
  ## Layout
40
40
 
@@ -60,6 +60,7 @@ pnpm build # Generate theme.css, build dist/ with rslib, check every exp
60
60
  pnpm build:check # List the external packages each public entry reaches
61
61
  pnpm lint # oxlint (type-aware)
62
62
  pnpm lint:deps # Check that registry files only import from registry/ and lib/
63
+ pnpm -w knip # Find unused files, exports and dependencies (both workspaces)
63
64
  pnpm typecheck # tsc --noEmit
64
65
  pnpm format # Biome
65
66
  ```
@@ -58,7 +58,7 @@ export * from "../registry/toggle-group/toggle-group";
58
58
  export * from "../registry/tooltip/tooltip";
59
59
  export * from "../registry/confidence-signal/confidence-signal";
60
60
  export * from "../registry/data-table";
61
- export { CheckboxField, type CheckboxFieldProps, type FieldOption, RadioGroupField, type RadioGroupFieldProps, SelectField, type SelectFieldProps, SubmitButton, type SubmitButtonProps, SwitchField, type SwitchFieldProps, TextareaField, type TextareaFieldProps, TextField, type TextFieldProps, useAppForm, useFieldContext, useFormContext, useTranslatedErrors, withForm, } from "../registry/form";
61
+ export { CheckboxField, type CheckboxFieldProps, FieldError as FormFieldError, type FieldErrorProps as FormFieldErrorProps, type FieldOption, RadioGroupField, type RadioGroupFieldProps, SelectField, type SelectFieldProps, SubmitButton, type SubmitButtonProps, SwitchField, type SwitchFieldProps, TextareaField, type TextareaFieldProps, TextField, type TextFieldProps, useAppForm, useFieldContext, useFormContext, useTranslatedErrors, withForm, } from "../registry/form";
62
62
  export * from "../registry/form-wizard";
63
63
  export * from "../registry/onboarding-tour-joyride";
64
64
  export * from "../registry/sidebar/sidebar";
@@ -67,6 +67,8 @@ export * from "../registry/timeline";
67
67
  export { useIsMobile } from "../registry/use-mobile/use-mobile";
68
68
  export { type UseDataTableOptions, useDataTable, } from "../registry/use-data-table/useDataTable";
69
69
  export { cn } from "../lib/utils";
70
+ export { assertDateOrString } from "../lib/asserts/assert-date-or-string";
71
+ export { assertString } from "../lib/asserts/assert-string";
70
72
  export { addVertexTranslations } from "../lib/i18n";
71
73
  export * from "../registry/chart/chart";
72
74
  export * from "../registry/bar-chart/bar-chart";
@@ -84,19 +86,20 @@ export { LocaleProvider } from "../registry/shell/shell-locale-provider";
84
86
  export { ThemeProvider } from "../registry/shell/shell-theme-provider";
85
87
  export { MembershipDenied, VerifyingMembership, type MembershipDeniedProps, } from "../registry/shell/group-membership-screens";
86
88
  export { Role } from "../registry/shell/shell-roles";
89
+ export { STALE_TIME_MS } from "../registry/shell/shell-constants";
87
90
  export { useAccessToken } from "../registry/shell/shell-auth-provider";
88
91
  export { useTheme } from "../registry/shell/shell-theme-provider";
89
92
  export { ShellLogin } from "../registry/shell/shell-login";
90
93
  export { ShellLayout } from "../registry/shell/shell-layout";
91
94
  export { ShellSidebar } from "../registry/shell/shell-sidebar";
92
- export { ShellUserProvider } from "../registry/shell/shell-user-provider";
95
+ export { ShellUserProvider, useUser, type User, } from "../registry/shell/shell-user-provider";
93
96
  export { SUPPORTED_LOCALES, configurei18n, type SupportedLocale, } from "../lib/i18n";
94
- export { STORAGE_KEYS, TOKEN_QUERY_KEY, ensureValidToken, login, logout, resolveReturnPath, toCodedAppFilePath, } from "../lib/auth";
95
- export { GroupMembershipGuard } from "../registry/shell/group-membership-guard";
96
- export { useGroupId } from "../registry/shell/use-group-id";
97
- export { useGroupMembers } from "../registry/shell/use-group-members";
97
+ export { STORAGE_KEYS, TOKEN_QUERY_KEY, type TokenData, ensureValidToken, getStoredIdToken, getTokenQueryKey, login, logout, resolveReturnPath, toCodedAppFilePath, } from "../lib/auth";
98
+ export { GroupMembershipGuard, type GroupMembershipGuardProps, } from "../registry/shell/group-membership-guard";
99
+ export { useGroupId, type UseGroupIdOptions, type UseGroupIdResult, } from "../registry/shell/use-group-id";
100
+ export { useGroupMembers, type UseGroupMembersOptions, type UseGroupMembersResult, } from "../registry/shell/use-group-members";
98
101
  export { useIsGroupMember } from "../registry/shell/use-is-group-member";
99
- export { useEntityDataTable } from "../registry/use-data-table/useEntityDataTable";
102
+ export { useEntityDataTable, type UseEntityDataTableOptions, } from "../registry/use-data-table/useEntityDataTable";
100
103
  export { useEntityColumns } from "../registry/use-data-table/useEntityColumns";
101
104
  export { FeatureFlagContext, FeatureFlagProvider, useFeatureFlagProvider, } from "../registry/feature-flag-provider/feature-flag-provider";
102
105
  export { FeatureFlagGuard } from "../registry/feature-flag-guard/feature-flag-guard";
@@ -128,7 +131,7 @@ export { CaseEvaluationOutputSchema } from "../registry/solution-tests/outputs/c
128
131
  export type { CaseEvaluationOutput, EvaluationRecord, } from "../registry/solution-tests/outputs/case-evaluation/schema";
129
132
  export type { IxpOutput, IxpDocumentExtraction, IxpExtractionGroup, IxpFieldValue, } from "../registry/solution-tests/outputs/ixp-extraction/schema";
130
133
  export { SolutionTestStatus, RunStatus, RunResultStatus, JobRole, } from "../registry/solution-tests/types";
131
- export type { SolutionTest, SolutionTestJob, SolutionTestRun, SolutionTestBatchRun, SolutionTestRunResult, UserMessageItem, ResultAttachmentField, } from "../registry/solution-tests/types";
134
+ export type { SolutionTest, SolutionTestJob, SolutionTestRun, SolutionTestBatchRun, SolutionTestRunResult, UserMessageItem, ResultAttachmentField, SolutionTestStatusValue, RunStatusValue, RunResultStatusValue, JobRoleValue, } from "../registry/solution-tests/types";
132
135
  export type { RunConfirmTarget } from "../registry/solution-tests/solution-tests-view";
133
136
  export { RunDetailsView } from "../registry/solution-tests/run-details-view";
134
137
  export type { BaselineJobMap, RunDetailsViewProps, } from "../registry/solution-tests/run-details-view";
@@ -152,9 +155,11 @@ export { createConversationalAgentConnection } from "../registry/ai-chat/adapter
152
155
  export type { ConversationalAgentAdapterConfig } from "../registry/ai-chat/adapters/conversational-agent/adapter";
153
156
  export { AiChat } from "../registry/ai-chat/components/ai-chat";
154
157
  export { AiChatEmptyState } from "../registry/ai-chat/components/ai-chat-empty-state";
158
+ export { NoDataMessage } from "../registry/ai-chat/charts/no-data-message";
155
159
  export { AutopilotGradientIcon } from "../registry/ai-chat/components/icons/autopilot-gradient";
156
160
  export * from "../registry/ai-chat/tools/choices";
157
161
  export type { Entity } from "../registry/ai-chat/tools/data-fabric/util/entities";
162
+ export type { JoinInput } from "../registry/ai-chat/tools/data-fabric/util/joins";
158
163
  export * from "../registry/ai-chat/tools/data-fabric-bar";
159
164
  export * from "../registry/ai-chat/tools/data-fabric-distribution";
160
165
  export * from "../registry/ai-chat/tools/data-fabric-kpi";
@@ -1,7 +1,9 @@
1
- import { CheckboxField, RadioGroupField, SelectField, SubmitButton, SwitchField, TextField, TextareaField, useAppForm, useFieldContext, useFormContext, useTranslatedErrors, withForm } from "../registry/form/index.js";
1
+ import { CheckboxField, FieldError, RadioGroupField, SelectField, SubmitButton, SwitchField, TextField, TextareaField, useAppForm, useFieldContext, useFormContext, useTranslatedErrors, withForm } from "../registry/form/index.js";
2
2
  import { useIsMobile } from "../registry/use-mobile/use-mobile.js";
3
3
  import { useDataTable } from "../registry/use-data-table/useDataTable.js";
4
4
  import { cn } from "../lib/utils.js";
5
+ import { assertDateOrString } from "../lib/asserts/assert-date-or-string.js";
6
+ import { assertString } from "../lib/asserts/assert-string.js";
5
7
  import { SUPPORTED_LOCALES, addVertexTranslations, configurei18n } from "../lib/i18n.js";
6
8
  import { ApolloShell } from "../registry/shell/shell.js";
7
9
  import { AuthContext, ShellAuthProvider, useAccessToken, useAuth } from "../registry/shell/shell-auth-provider.js";
@@ -9,11 +11,12 @@ import { LocaleProvider } from "../registry/shell/shell-locale-provider.js";
9
11
  import { ThemeProvider, useTheme } from "../registry/shell/shell-theme-provider.js";
10
12
  import { MembershipDenied, VerifyingMembership } from "../registry/shell/group-membership-screens.js";
11
13
  import { Role } from "../registry/shell/shell-roles.js";
14
+ import { STALE_TIME_MS } from "../registry/shell/shell-constants.js";
12
15
  import { ShellLogin } from "../registry/shell/shell-login.js";
13
16
  import { ShellLayout } from "../registry/shell/shell-layout.js";
14
17
  import { ShellSidebar } from "../registry/shell/shell-sidebar.js";
15
- import { ShellUserProvider } from "../registry/shell/shell-user-provider.js";
16
- import { STORAGE_KEYS, TOKEN_QUERY_KEY, ensureValidToken, login, logout, resolveReturnPath, toCodedAppFilePath } from "../lib/auth.js";
18
+ import { ShellUserProvider, useUser } from "../registry/shell/shell-user-provider.js";
19
+ import { STORAGE_KEYS, TOKEN_QUERY_KEY, ensureValidToken, getStoredIdToken, getTokenQueryKey, login, logout, resolveReturnPath, toCodedAppFilePath } from "../lib/auth.js";
17
20
  import { GroupMembershipGuard } from "../registry/shell/group-membership-guard.js";
18
21
  import { useGroupId } from "../registry/shell/use-group-id.js";
19
22
  import { useGroupMembers } from "../registry/shell/use-group-members.js";
@@ -54,6 +57,7 @@ import { createAgentHubConnection } from "../registry/ai-chat/adapters/agenthub/
54
57
  import { createConversationalAgentConnection } from "../registry/ai-chat/adapters/conversational-agent/adapter.js";
55
58
  import { AiChat } from "../registry/ai-chat/components/ai-chat.js";
56
59
  import { AiChatEmptyState } from "../registry/ai-chat/components/ai-chat-empty-state.js";
60
+ import { NoDataMessage } from "../registry/ai-chat/charts/no-data-message.js";
57
61
  import { AutopilotGradientIcon } from "../registry/ai-chat/components/icons/autopilot-gradient.js";
58
62
  export * from "../registry/accordion/accordion.js";
59
63
  export * from "../registry/ai-caveat/ai-caveat.js";
@@ -138,4 +142,4 @@ export * from "../registry/ai-chat/tools/data-fabric-line/index.js";
138
142
  export * from "../registry/ai-chat/tools/data-fabric-multi-line/index.js";
139
143
  export * from "../registry/ai-chat/tools/data-fabric-table/index.js";
140
144
  export * from "../registry/ai-chat/tools/data-fabric/index.js";
141
- export { AiChat, AiChatEmptyState, ApolloShell, AuthContext, AutopilotGradientIcon, BaselineCompare, CASE_EVALUATION_EVALUATOR_ID, CASE_EVALUATION_OUTPUT_RENDERER, CaseEvaluationDetailsSchema, CaseEvaluationOutputResult, CaseEvaluationOutputSchema, CheckboxField, EvaluationPairStatus, ExpandedAgents, ExpandedAgentsView, ExpandedRunTests, ExpandedRunTestsView, FeatureFlagContext, FeatureFlagGuard, FeatureFlagProvider, GENERIC_RENDERER, GroupMembershipGuard, IXP_EXTRACTION_EVALUATOR_ID, IXP_OUTPUT_RENDERER, IxpDetailsSchema, IxpDocStatus, IxpOutputResult, IxpOutputSchema, IxpVerdict, JSON_SIMILARITY_EVALUATOR_ID, JobRole, LLM_JUDGE_EVALUATOR_ID, LocaleProvider, MembershipDenied, ProcessOutputView, ProtectedFeatureRoute, RadioGroupField, Role, RunDetails, RunDetailsView, RunResultStatus, RunStatus, STORAGE_KEYS, SUPPORTED_LOCALES, SaveAsTestButton, SelectField, ShellAuthProvider, ShellLayout, ShellLogin, ShellSidebar, ShellUserProvider, SolutionTestActionError, SolutionTestStatus, SolutionTests, SolutionTestsProvider, SolutionTestsView, SubmitButton, SummaryVerdict, SwitchField, TOKEN_QUERY_KEY, TextField, TextareaField, ThemeProvider, VerifyingMembership, VersionDeltaGlyph, addVertexTranslations, cn, configurei18n, createAgentHubConnection, createConversationalAgentConnection, createProteusProvider, ensureValidToken, genericRenderer, login, logout, makeProcessOutputRenderer, makeRenderer, resolveEvaluatorRenderer, resolveProcessOutputRenderer, resolveReturnPath, toCodedAppFilePath, useAccessToken, useAdoptJob, useAppForm, useAuth, useBaselineJobs, useCreateTest, useDataTable, useDeleteTest, useEntityColumns, useEntityDataTable, useFeatureFlag, useFeatureFlagProvider, useFieldContext, useForceStopBatch, useForceStopRun, useFormContext, useGroupId, useGroupMembers, useIsGroupMember, useIsMobile, useJobExpectedOutput, useRemoveJobBaseline, useResultAttachment, useRunResults, useRunTests, useSolutionTestBatchRuns, useSolutionTestRuns, useSolutionTests, useSolutionTestsConfig, useSolutionTestsContext, useTheme, useToggleTestActive, useTranslatedErrors, useUpdateBaseline, versionDelta, withForm };
145
+ export { AiChat, AiChatEmptyState, ApolloShell, AuthContext, AutopilotGradientIcon, BaselineCompare, CASE_EVALUATION_EVALUATOR_ID, CASE_EVALUATION_OUTPUT_RENDERER, CaseEvaluationDetailsSchema, CaseEvaluationOutputResult, CaseEvaluationOutputSchema, CheckboxField, EvaluationPairStatus, ExpandedAgents, ExpandedAgentsView, ExpandedRunTests, ExpandedRunTestsView, FeatureFlagContext, FeatureFlagGuard, FeatureFlagProvider, FieldError as FormFieldError, GENERIC_RENDERER, GroupMembershipGuard, IXP_EXTRACTION_EVALUATOR_ID, IXP_OUTPUT_RENDERER, IxpDetailsSchema, IxpDocStatus, IxpOutputResult, IxpOutputSchema, IxpVerdict, JSON_SIMILARITY_EVALUATOR_ID, JobRole, LLM_JUDGE_EVALUATOR_ID, LocaleProvider, MembershipDenied, NoDataMessage, ProcessOutputView, ProtectedFeatureRoute, RadioGroupField, Role, RunDetails, RunDetailsView, RunResultStatus, RunStatus, STALE_TIME_MS, STORAGE_KEYS, SUPPORTED_LOCALES, SaveAsTestButton, SelectField, ShellAuthProvider, ShellLayout, ShellLogin, ShellSidebar, ShellUserProvider, SolutionTestActionError, SolutionTestStatus, SolutionTests, SolutionTestsProvider, SolutionTestsView, SubmitButton, SummaryVerdict, SwitchField, TOKEN_QUERY_KEY, TextField, TextareaField, ThemeProvider, VerifyingMembership, VersionDeltaGlyph, addVertexTranslations, assertDateOrString, assertString, cn, configurei18n, createAgentHubConnection, createConversationalAgentConnection, createProteusProvider, ensureValidToken, genericRenderer, getStoredIdToken, getTokenQueryKey, login, logout, makeProcessOutputRenderer, makeRenderer, resolveEvaluatorRenderer, resolveProcessOutputRenderer, resolveReturnPath, toCodedAppFilePath, useAccessToken, useAdoptJob, useAppForm, useAuth, useBaselineJobs, useCreateTest, useDataTable, useDeleteTest, useEntityColumns, useEntityDataTable, useFeatureFlag, useFeatureFlagProvider, useFieldContext, useForceStopBatch, useForceStopRun, useFormContext, useGroupId, useGroupMembers, useIsGroupMember, useIsMobile, useJobExpectedOutput, useRemoveJobBaseline, useResultAttachment, useRunResults, useRunTests, useSolutionTestBatchRuns, useSolutionTestRuns, useSolutionTests, useSolutionTestsConfig, useSolutionTestsContext, useTheme, useToggleTestActive, useTranslatedErrors, useUpdateBaseline, useUser, versionDelta, withForm };
@@ -5,6 +5,7 @@ export declare const STORAGE_KEYS: {
5
5
  readonly TOKEN: "uipath_token";
6
6
  readonly CODE_VERIFIER: "uipath_code_verifier";
7
7
  readonly STATE: "uipath_state";
8
+ readonly REQUESTED_SCOPE: "uipath_requested_scope";
8
9
  readonly AUTH_RETURN_TO: "auth_return_to";
9
10
  readonly LOGOUT_RETURN_TO: "logout_return_to";
10
11
  };
@@ -19,7 +20,9 @@ declare const TokenDataSchema: z.ZodObject<{
19
20
  expiresAt: z.ZodNumber;
20
21
  }, z.core.$strip>;
21
22
  export type TokenData = z.infer<typeof TokenDataSchema>;
22
- export declare const ensureValidToken: (queryClient: ReturnType<typeof useQueryClient>, clientId: string, baseUrl: string, redirectPath?: string) => Promise<string | null>;
23
+ export declare const getTokenQueryKey: (clientId: string, scope: string) => readonly ["auth", "token", string, string];
24
+ export declare const getStoredIdToken: (clientId: string, scope: string) => string | null;
25
+ export declare const ensureValidToken: (clientId: string, scope: string, baseUrl: string, redirectPath?: string) => Promise<string | null>;
23
26
  export declare const login: (clientId: string, scope: string, baseUrl: string, redirectPath?: string) => Promise<void>;
24
- export declare const logout: (queryClient: ReturnType<typeof useQueryClient>) => void;
27
+ export declare const logout: (queryClient: ReturnType<typeof useQueryClient>, clientId: string) => void;
25
28
  export {};
package/dist/lib/auth.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { jwtDecode } from "jwt-decode";
1
2
  import pkce_challenge from "pkce-challenge";
2
3
  import { toast } from "sonner";
3
4
  import { z } from "zod";
@@ -15,6 +16,7 @@ const STORAGE_KEYS = {
15
16
  TOKEN: "uipath_token",
16
17
  CODE_VERIFIER: "uipath_code_verifier",
17
18
  STATE: "uipath_state",
19
+ REQUESTED_SCOPE: "uipath_requested_scope",
18
20
  AUTH_RETURN_TO: "auth_return_to",
19
21
  LOGOUT_RETURN_TO: "logout_return_to"
20
22
  };
@@ -56,11 +58,35 @@ const shouldRefreshToken = (tokenData)=>{
56
58
  const timeUntilExpiry = tokenData.expiresAt - Date.now();
57
59
  return timeUntilExpiry < REFRESH_THRESHOLD_MS;
58
60
  };
59
- const saveTokenData = (tokenData)=>{
60
- localStorage.setItem(STORAGE_KEYS.TOKEN, JSON.stringify(tokenData));
61
+ const normalizeScope = (scope)=>[
62
+ ...new Set(scope.split(/\s+/).filter(Boolean))
63
+ ].toSorted().join(" ");
64
+ const getTokenStorageKey = (clientId, scope)=>`${STORAGE_KEYS.TOKEN}:${clientId}:${normalizeScope(scope)}`;
65
+ const getTokenQueryKey = (clientId, scope)=>[
66
+ ...TOKEN_QUERY_KEY,
67
+ clientId,
68
+ normalizeScope(scope)
69
+ ];
70
+ const readTokenData = (storageKey)=>{
71
+ try {
72
+ const { data, success } = TokenDataSchema.safeParse(JSON.parse(localStorage.getItem(storageKey) ?? "null"));
73
+ return success ? data : null;
74
+ } catch {
75
+ return null;
76
+ }
77
+ };
78
+ const getStoredIdToken = (clientId, scope)=>readTokenData(getTokenStorageKey(clientId, scope))?.id_token ?? null;
79
+ const saveTokenData = (storageKey, tokenData)=>{
80
+ localStorage.setItem(storageKey, JSON.stringify(tokenData));
61
81
  };
62
- const clearTokenData = ()=>{
63
- localStorage.removeItem(STORAGE_KEYS.TOKEN);
82
+ const clearTokenData = (storageKey)=>{
83
+ localStorage.removeItem(storageKey);
84
+ };
85
+ const clearPendingLogin = ()=>{
86
+ sessionStorage.removeItem(STORAGE_KEYS.CODE_VERIFIER);
87
+ sessionStorage.removeItem(STORAGE_KEYS.STATE);
88
+ sessionStorage.removeItem(STORAGE_KEYS.REQUESTED_SCOPE);
89
+ sessionStorage.removeItem(STORAGE_KEYS.AUTH_RETURN_TO);
64
90
  };
65
91
  const fetchTokenData = async (baseUrl, body)=>{
66
92
  const response = await fetch(getTokenEndpoint(baseUrl), {
@@ -88,14 +114,14 @@ const refreshAccessToken = (refreshToken, clientId, baseUrl)=>{
88
114
  });
89
115
  return fetchTokenData(baseUrl, body);
90
116
  };
91
- const refreshTokenIfNeeded = async (tokenData, clientId, baseUrl)=>{
117
+ const refreshTokenIfNeeded = async (tokenData, clientId, baseUrl, storageKey)=>{
92
118
  if (!tokenData.refresh_token) return null;
93
119
  try {
94
120
  const newTokenData = await refreshAccessToken(tokenData.refresh_token, clientId, baseUrl);
95
- saveTokenData(newTokenData);
121
+ saveTokenData(storageKey, newTokenData);
96
122
  return newTokenData.access_token;
97
123
  } catch {
98
- clearTokenData();
124
+ clearTokenData(storageKey);
99
125
  return null;
100
126
  }
101
127
  };
@@ -109,59 +135,50 @@ const exchangeCodeForToken = (code, codeVerifier, clientId, baseUrl, redirectPat
109
135
  });
110
136
  return fetchTokenData(baseUrl, body);
111
137
  };
112
- const handleOAuthCallback = async (clientId, baseUrl, redirectPath)=>{
138
+ const handleOAuthCallback = async (clientId, scope, baseUrl, redirectPath)=>{
113
139
  const params = new URLSearchParams(window.location.search);
114
140
  const code = params.get("code");
115
141
  const state = params.get("state");
116
142
  const error = params.get("error");
117
- if (error) throw new Error(`OAuth error: ${error}`);
118
- if (!code || !state) return;
143
+ if (!state || !code && !error) return;
119
144
  const storedState = sessionStorage.getItem(STORAGE_KEYS.STATE);
120
- if (state !== storedState) throw new Error("State mismatch");
121
145
  const codeVerifier = sessionStorage.getItem(STORAGE_KEYS.CODE_VERIFIER);
122
- if (!codeVerifier) throw new Error("Code verifier not found");
146
+ const requestedScope = sessionStorage.getItem(STORAGE_KEYS.REQUESTED_SCOPE);
147
+ const returnTo = sessionStorage.getItem(STORAGE_KEYS.AUTH_RETURN_TO) ?? window.location.pathname;
148
+ clearPendingLogin();
123
149
  try {
150
+ if (null !== error || null === code) throw new Error(`OAuth error: ${error ?? "no code returned"}`);
151
+ if (state !== storedState) throw new Error("State mismatch");
152
+ if (!codeVerifier) throw new Error("Code verifier not found");
153
+ const loginUsedCurrentScope = null !== requestedScope && normalizeScope(requestedScope) === normalizeScope(scope);
154
+ if (!loginUsedCurrentScope) return void toast.info("Your sign-in settings changed. Please sign in again.");
124
155
  const tokenData = await exchangeCodeForToken(code, codeVerifier, clientId, baseUrl, redirectPath);
125
- sessionStorage.removeItem(STORAGE_KEYS.CODE_VERIFIER);
126
- sessionStorage.removeItem(STORAGE_KEYS.STATE);
127
- saveTokenData(tokenData);
128
- const returnTo = sessionStorage.getItem(STORAGE_KEYS.AUTH_RETURN_TO);
129
- sessionStorage.removeItem(STORAGE_KEYS.AUTH_RETURN_TO);
130
- const targetPath = returnTo ?? window.location.pathname;
131
- window.history.replaceState({}, document.title, targetPath);
156
+ saveTokenData(getTokenStorageKey(clientId, scope), tokenData);
132
157
  } catch (authError) {
133
- clearTokenData();
134
- sessionStorage.removeItem(STORAGE_KEYS.AUTH_RETURN_TO);
135
158
  const message = authError instanceof Error ? authError.message : "Unknown error";
136
159
  toast.error(`Authentication failed: ${message}`);
137
- throw authError;
160
+ } finally{
161
+ window.history.replaceState({}, document.title, returnTo);
138
162
  }
139
163
  };
140
- const ensureValidToken = async (queryClient, clientId, baseUrl, redirectPath)=>{
141
- const params = new URLSearchParams(window.location.search);
142
- const isInOAuthCallback = params.has("code") && params.has("state");
143
- if (isInOAuthCallback) try {
144
- await handleOAuthCallback(clientId, baseUrl, redirectPath);
145
- } catch {
146
- queryClient.resetQueries({
147
- queryKey: TOKEN_QUERY_KEY
148
- });
149
- }
150
- const tokenDataStr = localStorage.getItem(STORAGE_KEYS.TOKEN);
164
+ const ensureValidToken = async (clientId, scope, baseUrl, redirectPath)=>{
165
+ await handleOAuthCallback(clientId, scope, baseUrl, redirectPath);
166
+ const storageKey = getTokenStorageKey(clientId, scope);
167
+ const tokenDataStr = localStorage.getItem(storageKey);
151
168
  if (!tokenDataStr) return null;
152
169
  let parsed;
153
170
  try {
154
171
  parsed = JSON.parse(tokenDataStr);
155
172
  } catch {
156
- clearTokenData();
173
+ clearTokenData(storageKey);
157
174
  return null;
158
175
  }
159
176
  const { data: tokenData, success } = TokenDataSchema.safeParse(parsed);
160
177
  if (!success) {
161
- clearTokenData();
178
+ clearTokenData(storageKey);
162
179
  return null;
163
180
  }
164
- if (isTokenExpired(tokenData) || shouldRefreshToken(tokenData)) return refreshTokenIfNeeded(tokenData, clientId, baseUrl);
181
+ if (isTokenExpired(tokenData) || shouldRefreshToken(tokenData)) return refreshTokenIfNeeded(tokenData, clientId, baseUrl, storageKey);
165
182
  return tokenData.access_token;
166
183
  };
167
184
  const login = async (clientId, scope, baseUrl, redirectPath)=>{
@@ -169,6 +186,7 @@ const login = async (clientId, scope, baseUrl, redirectPath)=>{
169
186
  const state = generateRandomString(32);
170
187
  sessionStorage.setItem(STORAGE_KEYS.CODE_VERIFIER, pkce.code_verifier);
171
188
  sessionStorage.setItem(STORAGE_KEYS.STATE, state);
189
+ sessionStorage.setItem(STORAGE_KEYS.REQUESTED_SCOPE, scope);
172
190
  if (redirectPath && window.location.pathname !== redirectPath) sessionStorage.setItem(STORAGE_KEYS.AUTH_RETURN_TO, window.location.pathname);
173
191
  const params = new URLSearchParams({
174
192
  client_id: clientId,
@@ -181,13 +199,28 @@ const login = async (clientId, scope, baseUrl, redirectPath)=>{
181
199
  });
182
200
  window.location.href = `${getAuthorizationEndpoint(baseUrl)}?${params.toString()}`;
183
201
  };
184
- const logout = (queryClient)=>{
185
- clearTokenData();
186
- sessionStorage.removeItem(STORAGE_KEYS.CODE_VERIFIER);
187
- sessionStorage.removeItem(STORAGE_KEYS.STATE);
188
- sessionStorage.removeItem(STORAGE_KEYS.AUTH_RETURN_TO);
202
+ const isUnscopedSessionOf = (clientId)=>{
203
+ const tokenData = readTokenData(STORAGE_KEYS.TOKEN);
204
+ if (null === tokenData) return true;
205
+ try {
206
+ const { aud } = jwtDecode(tokenData.id_token);
207
+ return Array.isArray(aud) ? aud.includes(clientId) : aud === clientId;
208
+ } catch {
209
+ return true;
210
+ }
211
+ };
212
+ const logout = (queryClient, clientId)=>{
213
+ const clientPrefix = `${STORAGE_KEYS.TOKEN}:${clientId}:`;
214
+ const sessionKeys = Array.from({
215
+ length: localStorage.length
216
+ }, (_, index)=>localStorage.key(index)).filter((key)=>key === STORAGE_KEYS.TOKEN ? isUnscopedSessionOf(clientId) : key?.startsWith(clientPrefix) === true);
217
+ for (const key of sessionKeys)clearTokenData(key);
218
+ clearPendingLogin();
189
219
  queryClient.resetQueries({
190
- queryKey: TOKEN_QUERY_KEY
220
+ queryKey: [
221
+ ...TOKEN_QUERY_KEY,
222
+ clientId
223
+ ]
191
224
  });
192
225
  };
193
- export { STORAGE_KEYS, TOKEN_QUERY_KEY, ensureValidToken, login, logout, resolveReturnPath, toCodedAppFilePath };
226
+ export { STORAGE_KEYS, TOKEN_QUERY_KEY, ensureValidToken, getStoredIdToken, getTokenQueryKey, login, logout, resolveReturnPath, toCodedAppFilePath };
@@ -7,8 +7,10 @@ declare const cardVariants: (props?: ({
7
7
  interface CardProps extends React.ComponentProps<"div">, VariantProps<typeof cardVariants> {
8
8
  selectable?: "standard" | "ai";
9
9
  selected?: boolean;
10
+ /** Classes for the outermost element, e.g. `col-span-*`. When `selectable` is set, `className` and all other props go to the inner button. */
11
+ wrapperClassName?: string;
10
12
  }
11
- declare function Card({ className, variant, selectable, selected, children, ...props }: CardProps): import("react/jsx-runtime").JSX.Element;
13
+ declare function Card({ className, wrapperClassName, variant, selectable, selected, children, ...props }: CardProps): import("react/jsx-runtime").JSX.Element;
12
14
  declare function CardHeader({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
13
15
  declare function CardTitle({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
14
16
  declare function CardDescription({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
@@ -24,11 +24,11 @@ const cardVariants = cva("flex flex-col gap-6 py-6 text-card-foreground", {
24
24
  variant: "default"
25
25
  }
26
26
  });
27
- function Card({ className, variant, selectable, selected = false, children, ...props }) {
27
+ function Card({ className, wrapperClassName, variant, selectable, selected = false, children, ...props }) {
28
28
  if (selectable) {
29
29
  const isAi = "ai" === selectable;
30
30
  return /*#__PURE__*/ jsxs("div", {
31
- className: "relative group/card h-full",
31
+ className: cn("relative group/card h-full", wrapperClassName),
32
32
  "data-slot": "card",
33
33
  "data-variant": variant,
34
34
  "data-selectable": selectable,
@@ -58,9 +58,8 @@ function Card({ className, variant, selectable, selected = false, children, ...p
58
58
  "data-slot": "card",
59
59
  "data-variant": variant,
60
60
  className: cn(cardVariants({
61
- variant,
62
- className
63
- })),
61
+ variant
62
+ }), className, wrapperClassName),
64
63
  ...props,
65
64
  children: children
66
65
  });
@@ -15,8 +15,8 @@ export { type LineChartConfiguration, LineChartConfigurationSchema, } from "./mo
15
15
  export { type MultiLineChartConfiguration, MultiLineChartConfigurationSchema, } from "./models/configurations/multi-line-chart-configuration";
16
16
  export { type TableChartConfiguration, TableChartConfigurationSchema, } from "./models/configurations/table-chart-configuration";
17
17
  export type { DataModelAggregate, AggregationKind, MetricExpression, } from "./models/expression";
18
- export type { ListFilter } from "./models/filter";
19
- export type { FilterValues } from "./models/filter-values";
18
+ export type { ListFilter, PeriodFilter } from "./models/filter";
19
+ export type { BooleanListFilterValues, FilterValues, ListFilterValues, NumberListFilterValues, NumberRangeFilterValues, PeriodFilterValues, StringListFilterValues, StringSearchFilterValues, } from "./models/filter-values";
20
20
  export { type FromConfig, FromConfigSchema, type JoinConfig, JoinConfigSchema, } from "./models/join-schema";
21
21
  export type { PrimitiveValue } from "./models/primitive-value";
22
22
  export { SpinnerWithChildren } from "./spinner-with-children";
@@ -45,4 +45,5 @@ interface DataTableProps<TData, TValue> {
45
45
  declare function DataTable<TData, TValue>({ columns, data, className, isLoading, onRowClick, sorting, onSortingChange, columnFilters, onColumnFiltersChange, columnVisibility, onColumnVisibilityChange, columnOrder, onColumnOrderChange, rowSelection, onRowSelectionChange, globalFilter, onGlobalFilterChange, pagination, onPaginationChange, getRowId, globalFilterFn, enableSearch, enableViewOptions, enableColumnResizing, toolbarContent, noResultsMessage, stickyHeader, expanded, onExpandedChange, renderExpandedRow, getRowClassName, skeletonColumnWidths, plain, }: DataTableProps<TData, TValue>): import("react/jsx-runtime").JSX.Element;
46
46
  export { DataTable };
47
47
  export type { DataTableProps };
48
+ /** @public */
48
49
  export { DataTableColumnHeader } from "./data-table-column-header";
@@ -18,8 +18,9 @@ interface UseAccessTokenProps {
18
18
  clientId: string;
19
19
  baseUrl: string;
20
20
  redirectPath?: string;
21
+ scope: string;
21
22
  }
22
- export declare const useAccessToken: ({ clientId, baseUrl, redirectPath, }: UseAccessTokenProps) => {
23
+ export declare const useAccessToken: ({ clientId, baseUrl, redirectPath, scope, }: UseAccessTokenProps) => {
23
24
  accessToken: string | null;
24
25
  isLoading: boolean;
25
26
  };
@@ -4,7 +4,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
4
4
  import { jwtDecode } from "jwt-decode";
5
5
  import { createContext, useContext } from "react";
6
6
  import { z } from "zod";
7
- import { TOKEN_QUERY_KEY, ensureValidToken, login, logout } from "../../lib/auth.js";
7
+ import { ensureValidToken, getTokenQueryKey, login, logout } from "../../lib/auth.js";
8
8
  const JwtPayloadSchema = z.object({
9
9
  email: z.email(),
10
10
  first_name: z.string(),
@@ -36,11 +36,10 @@ const useAuth = ()=>{
36
36
  const context = useContext(AuthContext);
37
37
  return context ?? noAuthDefaults;
38
38
  };
39
- const useAccessToken = ({ clientId, baseUrl, redirectPath })=>{
40
- const queryClient = useQueryClient();
39
+ const useAccessToken = ({ clientId, baseUrl, redirectPath, scope })=>{
41
40
  const { data: token, isLoading } = useQuery({
42
- queryKey: TOKEN_QUERY_KEY,
43
- queryFn: ()=>ensureValidToken(queryClient, clientId, baseUrl, redirectPath),
41
+ queryKey: getTokenQueryKey(clientId, scope),
42
+ queryFn: ()=>ensureValidToken(clientId, scope, baseUrl, redirectPath),
44
43
  refetchInterval: 60000,
45
44
  enabled: "u" > typeof window
46
45
  });
@@ -54,7 +53,8 @@ const ShellAuthProvider = ({ children, clientId, scope, baseUrl, redirectPath })
54
53
  const { accessToken, isLoading } = useAccessToken({
55
54
  clientId,
56
55
  baseUrl,
57
- redirectPath
56
+ redirectPath,
57
+ scope
58
58
  });
59
59
  const user = accessToken ? decodeJWT(accessToken) : null;
60
60
  const isAuthenticated = null != accessToken;
@@ -63,7 +63,7 @@ const ShellAuthProvider = ({ children, clientId, scope, baseUrl, redirectPath })
63
63
  isAuthenticated,
64
64
  isLoading,
65
65
  login: ()=>login(clientId, scope, baseUrl, redirectPath),
66
- logout: ()=>logout(queryClient),
66
+ logout: ()=>logout(queryClient, clientId),
67
67
  accessToken
68
68
  };
69
69
  return /*#__PURE__*/ jsx(AuthContext.Provider, {
@@ -26,4 +26,3 @@ export { useRunResults, useAdoptJob, useUpdateBaseline, useResultAttachment, } f
26
26
  export type { UseRunResultsResult } from "./use-run-results";
27
27
  export { useForceStopBatch, useForceStopRun } from "./use-force-stop";
28
28
  export type { MutationHook, AttachmentFetcher } from "./mutations";
29
- export type { ResultAttachmentField } from "./types";
@@ -1,7 +1 @@
1
- export { Stepper, type StepperProps, stepperVariants } from "./stepper";
2
- export { StepperContent, StepperDescription, StepperTitle, } from "./stepper-content";
3
- export type { StepperItemState, StepperOrientation, } from "./stepper-context";
4
- export { StepperIndicator } from "./stepper-indicator";
5
- export { StepperItem, type StepperItemProps, stepperItemVariants, } from "./stepper-item";
6
- export { StepperSeparator } from "./stepper-separator";
7
- export { StepperTrigger } from "./stepper-trigger";
1
+ export { Stepper, StepperContent, StepperDescription, StepperIndicator, StepperItem, type StepperItemProps, type StepperItemState, type StepperOrientation, type StepperProps, StepperSeparator, StepperTitle, StepperTrigger, stepperItemVariants, stepperVariants, } from "./stepper";
@@ -1,7 +1,2 @@
1
- import { Stepper, stepperVariants } from "./stepper.js";
2
- import { StepperContent, StepperDescription, StepperTitle } from "./stepper-content.js";
3
- import { StepperIndicator } from "./stepper-indicator.js";
4
- import { StepperItem, stepperItemVariants } from "./stepper-item.js";
5
- import { StepperSeparator } from "./stepper-separator.js";
6
- import { StepperTrigger } from "./stepper-trigger.js";
1
+ import { Stepper, StepperContent, StepperDescription, StepperIndicator, StepperItem, StepperSeparator, StepperTitle, StepperTrigger, stepperItemVariants, stepperVariants } from "./stepper.js";
7
2
  export { Stepper, StepperContent, StepperDescription, StepperIndicator, StepperItem, StepperSeparator, StepperTitle, StepperTrigger, stepperItemVariants, stepperVariants };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/apollo-vertex",
3
- "version": "0.3.0",
3
+ "version": "1.0.0",
4
4
  "description": "UiPath Apollo Vertex design system components",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -120,16 +120,20 @@
120
120
  "@tanstack/intent": "0.4.0",
121
121
  "@tanstack/react-query": "^5.104.0",
122
122
  "@tanstack/react-router": "^1.170.40",
123
+ "@testing-library/dom": "^10.4.1",
124
+ "@testing-library/react": "^16.3.0",
123
125
  "@ts-rest/core": "^3.53.0-rc.1",
124
126
  "@types/node": "^24.10.1",
125
127
  "@types/react": "^19.2.6",
126
128
  "@types/react-dom": "^19.2.2",
127
129
  "@uipath/uipath-typescript": "^1.8.0",
130
+ "@vitest/coverage-v8": "^4.1.5",
128
131
  "dependency-cruiser": "^17.3.6",
129
132
  "eslint-plugin-react": "^7.37.5",
130
133
  "eventsource-parser": "^3.0.6",
131
134
  "highlight.js": "^11.11.1",
132
135
  "i18next": "^26.4.2",
136
+ "jsdom": "^27.2.0",
133
137
  "jwt-decode": "^4.0.0",
134
138
  "oxlint": "^1.38.0",
135
139
  "oxlint-tsgolint": "^0.18.1",
@@ -142,7 +146,9 @@
142
146
  "recharts": "2.15.4",
143
147
  "remark-breaks": "^4.0.0",
144
148
  "remark-gfm": "^4.0.1",
145
- "tailwindcss": "^4.1.17"
149
+ "tailwindcss": "^4.1.17",
150
+ "typescript": "^5.9.3",
151
+ "vitest": "^4.1.11"
146
152
  },
147
153
  "publishConfig": {
148
154
  "access": "public"
@@ -164,6 +170,8 @@
164
170
  "lint:fix": "oxlint --fix --type-aware",
165
171
  "lint:deps": "depcruise registry --config .dependency-cruiser.cjs",
166
172
  "typecheck": "tsc --noEmit",
167
- "lint:skills": "intent validate skills"
173
+ "lint:skills": "intent validate skills",
174
+ "test": "vitest run",
175
+ "test:coverage": "vitest run --coverage"
168
176
  }
169
177
  }