@strivacity/sdk-react 3.0.3 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +1562 -406
  3. package/dist/assets/rolldown-runtime.cjs +1 -0
  4. package/dist/assets/rolldown-runtime.mjs +1 -0
  5. package/dist/components.cjs +2 -0
  6. package/dist/components.cjs.map +1 -0
  7. package/dist/components.d.ts +11 -0
  8. package/dist/components.mjs +2 -0
  9. package/dist/components.mjs.map +1 -0
  10. package/dist/errors.cjs +2 -0
  11. package/dist/errors.cjs.map +1 -0
  12. package/dist/errors.d.ts +1 -0
  13. package/dist/errors.mjs +2 -0
  14. package/dist/errors.mjs.map +1 -0
  15. package/dist/hooks.cjs +2 -0
  16. package/dist/hooks.cjs.map +1 -0
  17. package/dist/hooks.d.ts +33 -0
  18. package/dist/hooks.mjs +2 -0
  19. package/dist/hooks.mjs.map +1 -0
  20. package/dist/index.cjs +2 -2
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.ts +9 -13
  23. package/dist/index.mjs +2 -2
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/storages.cjs +1 -0
  26. package/dist/storages.d.ts +1 -0
  27. package/dist/storages.mjs +1 -0
  28. package/dist/types.cjs +1 -2
  29. package/dist/types.d.ts +147 -110
  30. package/dist/types.mjs +1 -2
  31. package/dist/utils.cjs +1 -0
  32. package/dist/utils.d.ts +1 -0
  33. package/dist/utils.mjs +1 -0
  34. package/package.json +51 -7
  35. package/testing/tests/components.spec.tsx +146 -0
  36. package/testing/tests/errors.spec.ts +10 -0
  37. package/testing/tests/guard.spec.tsx +119 -0
  38. package/testing/tests/hooks.spec.tsx +418 -0
  39. package/testing/tests/index.spec.ts +105 -0
  40. package/testing/tests/storages.spec.ts +10 -0
  41. package/testing/tests/utils.spec.ts +10 -0
  42. package/testing/utils/common.tsx +63 -0
  43. package/dist/AuthProvider.cjs +0 -2
  44. package/dist/AuthProvider.cjs.map +0 -1
  45. package/dist/AuthProvider.d.ts +0 -7
  46. package/dist/AuthProvider.mjs +0 -2
  47. package/dist/AuthProvider.mjs.map +0 -1
  48. package/dist/LoginRenderer.cjs +0 -2
  49. package/dist/LoginRenderer.cjs.map +0 -1
  50. package/dist/LoginRenderer.d.ts +0 -20
  51. package/dist/LoginRenderer.mjs +0 -2
  52. package/dist/LoginRenderer.mjs.map +0 -1
  53. package/dist/composables.cjs +0 -2
  54. package/dist/composables.cjs.map +0 -1
  55. package/dist/composables.d.ts +0 -12
  56. package/dist/composables.mjs +0 -2
  57. package/dist/composables.mjs.map +0 -1
  58. package/dist/types.cjs.map +0 -1
  59. package/dist/types.mjs.map +0 -1
package/dist/types.d.ts CHANGED
@@ -1,176 +1,213 @@
1
- import { IdTokenClaims, LoginFlowMessage, LoginFlowState, SDKOptions } from '@strivacity/sdk-core';
2
- import { PopupFlow } from '@strivacity/sdk-core/flows/PopupFlow';
3
- import { RedirectFlow } from '@strivacity/sdk-core/flows/RedirectFlow';
4
- import { NativeFlow } from '@strivacity/sdk-core/flows/NativeFlow';
5
- export type Children = React.ReactElement | React.ReactNode | Array<React.ReactElement | React.ReactNode>;
6
- /**
7
- * Represents the session state, including authentication details and token information.
8
- */
9
- export type Session = {
10
- /**
11
- * Indicates if the session is being loaded.
1
+ import { ReactNode, DetailedHTMLProps, HTMLAttributes } from 'react';
2
+ import { LoginComponent, NotificationComponent, LandingComponent, LanguageSelectorComponent, SDKInitConfig, SessionData, IdTokenClaims, RedirectFlow, PopupFlow, NativeFlow, EmbeddedFlow, initFlow, NativeFlowState, NativeFlowMessage, NativeParams } from '@strivacity/sdk-core';
3
+ import { FallbackError } from '@strivacity/sdk-core/utils';
4
+ export * from '@strivacity/sdk-core/types';
5
+ declare module 'react' {
6
+ namespace JSX {
7
+ interface IntrinsicElements {
8
+ 'sty-login': ElementProps<LoginComponent, {
9
+ onLogin?: () => void;
10
+ onClose?: () => void;
11
+ onError?: (e: CustomEvent<string>) => void;
12
+ }>;
13
+ 'sty-notifications': ElementProps<NotificationComponent>;
14
+ 'sty-language-selector': ElementProps<LanguageSelectorComponent>;
15
+ 'sty-landing': ElementProps<LandingComponent>;
16
+ }
17
+ }
18
+ }
19
+ export type ElementProps<T extends HTMLElement, Extra = object> = DetailedHTMLProps<Omit<HTMLAttributes<T>, keyof Extra> & Partial<Omit<T, keyof HTMLElement>> & Extra, T>;
20
+ export type SDKContext<Flow extends RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow> = {
21
+ /**
22
+ * The underlying Strivacity SDK flow instance.
12
23
  */
13
- loading: boolean;
24
+ readonly sdk: Flow;
14
25
  /**
15
- * The SDK options used to configure the session.
26
+ * A boolean indicating whether the SDK is still loading. This can be used to show a loading spinner or similar UI while the SDK is initializing.
16
27
  */
17
- options: SDKOptions;
28
+ readonly loading: boolean;
18
29
  /**
19
- * Indicates whether the user is authenticated.
30
+ * BCP 47 language code representing the current language of the SDK.
20
31
  */
21
- isAuthenticated: boolean;
32
+ readonly language: string;
22
33
  /**
23
- * Claims from the ID token or `null` if not available.
34
+ * A boolean indicating whether the user is authenticated. This can be used to conditionally render UI based on the user's authentication state.
24
35
  */
25
- idTokenClaims: IdTokenClaims | null;
36
+ readonly isAuthenticated: boolean;
26
37
  /**
27
- * The access token or `null` if not available.
38
+ * The claims from the ID token, if available. This can be used to access user information and other claims provided by the authentication server.
28
39
  */
29
- accessToken: string | null;
40
+ readonly idTokenClaims: IdTokenClaims | null;
30
41
  /**
31
- * The refresh token or `null` if not available.
42
+ * The access token, if available. This can be used to authenticate API requests or other interactions with the backend.
32
43
  */
33
- refreshToken: string | null;
44
+ readonly accessToken: string | null;
34
45
  /**
35
- * Indicates if the access token has expired.
46
+ * The refresh token, if available. This can be used to obtain new access tokens when the current one expires.
36
47
  */
37
- accessTokenExpired: boolean;
48
+ readonly refreshToken: string | null;
38
49
  /**
39
- * Expiration date of the access token or `null` if not set.
50
+ * A boolean indicating whether the access token has expired. This can be used to trigger a refresh of the access token or to log the user out.
40
51
  */
41
- accessTokenExpirationDate: number | null;
42
- };
43
- /**
44
- * Represents the available authentication flows and operations for Popup-based interactions.
45
- */
46
- export type PopupSDK = {
52
+ readonly accessTokenExpired: boolean;
47
53
  /**
48
- * Represents the SDK instance.
54
+ * The expiration date of the access token, if available. This can be used to determine when to refresh the access token or to log the user out.
49
55
  */
50
- sdk: InstanceType<typeof PopupFlow>;
56
+ readonly accessTokenExpirationDate: number | null;
51
57
  /**
52
- * Initiates the login process.
58
+ * Subscribes to a specific SDK event.
53
59
  */
54
- login: InstanceType<typeof PopupFlow>['login'];
60
+ subscribeToEvent: Flow['subscribeToEvent'];
55
61
  /**
56
- * Registers a new user.
62
+ * Subscribes to all SDK events.
57
63
  */
58
- register: InstanceType<typeof PopupFlow>['register'];
64
+ subscribeToAllEvents: Flow['subscribeToAllEvents'];
59
65
  /**
60
- * Initiates the entry process.
66
+ * Checks whether the user is currently authenticated, optionally triggering a token refresh.
61
67
  */
62
- entry: InstanceType<typeof PopupFlow>['entry'];
68
+ checkAuthentication: Flow['checkAuthentication'];
63
69
  /**
64
- * Refreshes the user's session.
70
+ * Initializes the SDK, loading metadata and restoring any persisted session.
65
71
  */
66
- refresh: InstanceType<typeof PopupFlow>['refresh'];
72
+ init: Flow['init'];
67
73
  /**
68
- * Revokes the current session tokens.
74
+ * Performs a token exchange using the provided parameters.
69
75
  */
70
- revoke: InstanceType<typeof PopupFlow>['revoke'];
76
+ tokenExchange: Flow['tokenExchange'];
71
77
  /**
72
- * Logs out the user.
78
+ * Handles the authentication callback by parsing the response from the given URL.
73
79
  */
74
- logout: InstanceType<typeof PopupFlow>['logout'];
80
+ handleCallback: Flow['handleCallback'];
75
81
  /**
76
- * Handles the callback after authentication or token exchange.
82
+ * Refreshes the current access token using the refresh token.
77
83
  */
78
- handleCallback: InstanceType<typeof PopupFlow>['handleCallback'];
79
- };
80
- /**
81
- * Represents the available authentication flows and operations for Redirect-based interactions.
82
- */
83
- export type RedirectSDK = {
84
+ refresh: Flow['refresh'];
84
85
  /**
85
- * Represents the SDK instance.
86
+ * Revokes the current tokens.
86
87
  */
87
- sdk: InstanceType<typeof RedirectFlow>;
88
+ revoke: Flow['revoke'];
88
89
  /**
89
- * Initiates the login process.
90
+ * Logs the user out and optionally redirects to a post-logout URI.
90
91
  */
91
- login: InstanceType<typeof RedirectFlow>['login'];
92
+ logout: Flow['logout'];
92
93
  /**
93
- * Registers a new user.
94
+ * Initiates the login flow.
94
95
  */
95
- register: InstanceType<typeof RedirectFlow>['register'];
96
+ login: Flow['login'];
96
97
  /**
97
- * Initiates the entry process.
98
+ * Initiates the registration flow.
98
99
  */
99
- entry: InstanceType<typeof RedirectFlow>['entry'];
100
+ register: Flow['register'];
100
101
  /**
101
- * Refreshes the user's session.
102
+ * Handles the entry point URL of the authentication flow, typically used for embedded or native modes.
102
103
  */
103
- refresh: InstanceType<typeof RedirectFlow>['refresh'];
104
+ entry: Flow['entry'];
105
+ };
106
+ export type SDKInstance = ReturnType<typeof initFlow>;
107
+ export type StyAuthProviderProps = {
104
108
  /**
105
- * Revokes the current session tokens.
109
+ * The options to be used by the AuthProvider. These options are passed to the SDK during initialization.
106
110
  */
107
- revoke: InstanceType<typeof RedirectFlow>['revoke'];
111
+ options: SDKInitConfig;
108
112
  /**
109
- * Logs out the user.
113
+ * The session data to be used by the AuthProvider.
114
+ *
115
+ * If not provided, the AuthProvider will attempt to retrieve the session data from the SDK.
110
116
  */
111
- logout: InstanceType<typeof RedirectFlow>['logout'];
117
+ session?: SessionData | null;
112
118
  /**
113
- * Handles the callback after authentication or token exchange.
119
+ * The children to be rendered by the AuthProvider. This is typically the rest of your application.
114
120
  */
115
- handleCallback: InstanceType<typeof RedirectFlow>['handleCallback'];
121
+ children: ReactNode;
116
122
  };
117
- /**
118
- * Represents the available authentication flows and operations for Native-based interactions.
119
- */
120
- export type NativeSDK = {
123
+ export type LoginContext = {
124
+ /**
125
+ * Whether a form submission or session initialization is in progress.
126
+ */
127
+ loading: boolean;
121
128
  /**
122
- * Represents the SDK instance.
129
+ * Current form field values, keyed by form ID then widget ID.
123
130
  */
124
- sdk: InstanceType<typeof NativeFlow>;
131
+ forms: Record<string, Record<string, unknown>>;
125
132
  /**
126
- * Initiates the login process.
133
+ * Current validation and info messages, keyed by form ID then widget ID.
127
134
  */
128
- login: InstanceType<typeof NativeFlow>['login'];
135
+ messages: Record<string, Record<string, NativeFlowMessage>>;
129
136
  /**
130
- * Registers a new user.
137
+ * The current login flow state returned by the backend.
131
138
  */
132
- register: InstanceType<typeof NativeFlow>['register'];
139
+ state: Partial<NativeFlowState>;
133
140
  /**
134
- * Initiates the entry process.
141
+ * Submits the form with the given ID and advances the login flow.
142
+ *
143
+ * @param formId - The ID of the form to submit.
144
+ * @param customBody - Optional custom body to send with the form submission. If not provided, the current form values will be used.
145
+ * @returns A promise that resolves when the form submission is complete.
135
146
  */
136
- entry: InstanceType<typeof NativeFlow>['entry'];
147
+ submitForm: (formId: string, customBody?: Record<string, unknown>) => Promise<void>;
137
148
  /**
138
- * Refreshes the user's session.
149
+ * Redirects to the hosted login UI.
150
+ *
151
+ * @param message - Optional message to log before redirecting.
152
+ * @returns A promise that resolves when the redirect is initiated.
139
153
  */
140
- refresh: InstanceType<typeof NativeFlow>['refresh'];
154
+ triggerFallback: (message?: string) => void;
141
155
  /**
142
- * Revokes the current session tokens.
156
+ * Signals that the login flow was closed by the user.
143
157
  */
144
- revoke: InstanceType<typeof NativeFlow>['revoke'];
158
+ triggerClose: () => void;
145
159
  /**
146
- * Logs out the user.
160
+ * Updates a single field value within a form before submission.
161
+ *
162
+ * @param formId - The ID of the form containing the field.
163
+ * @param widgetId - The ID of the widget (field) to update.
164
+ * @param value - The new value to set for the field.
147
165
  */
148
- logout: InstanceType<typeof NativeFlow>['logout'];
166
+ setFormValue: (formId: string, widgetId: string, value: unknown) => void;
149
167
  /**
150
- * Handles the callback after authentication or token exchange.
168
+ * Sets a validation or info message on a specific widget.
169
+ *
170
+ * @param formId - The ID of the form containing the widget.
171
+ * @param widgetId - The ID of the widget to set the message for.
172
+ * @param value - The message to set, which can be a string or a structured NativeFlowMessage.
151
173
  */
152
- handleCallback: InstanceType<typeof NativeFlow>['handleCallback'];
174
+ setMessage: (formId: string, widgetId: string, value: NativeFlowMessage) => void;
153
175
  };
154
- /**
155
- * Represents a combined context for Popup-based flows, containing both the Popup SDK and the session state.
156
- */
157
- export type PopupContext = PopupSDK & Session;
158
- /**
159
- * Represents a combined context for Redirect-based flows, containing both the Redirect SDK and the session state.
160
- */
161
- export type RedirectContext = RedirectSDK & Session;
162
- /**
163
- * Represents a combined context for Native-based flows, containing both the Native SDK and the session state.
164
- */
165
- export type NativeContext = NativeSDK & Session;
166
- export type NativeFlowContextValue = {
167
- loading: boolean;
168
- forms: Record<string, Record<string, unknown>>;
169
- messages: Record<string, Record<string, LoginFlowMessage>>;
170
- state: Partial<LoginFlowState>;
171
- submitForm: (formId: string) => Promise<void>;
172
- triggerFallback: (hostedUrl?: string) => void;
173
- triggerClose: () => void;
174
- setFormValue: (formId: string, widgetId: string, value: unknown) => void;
175
- setMessage: (formId: string, widgetId: string, value: LoginFlowMessage) => void;
176
+ export type UseNativeLoginOptions = {
177
+ /**
178
+ * Optional parameters to be passed to the login session request.
179
+ */
180
+ params?: NativeParams;
181
+ /**
182
+ * Called when the user successfully completes the login flow.
183
+ */
184
+ onLogin?: (session: SessionData) => void | Promise<void>;
185
+ /**
186
+ * Called when the fallback flow is triggered.
187
+ */
188
+ onFallback?: (error: FallbackError) => void | Promise<void>;
189
+ /**
190
+ * Called when the login flow is closed by the user.
191
+ */
192
+ onClose?: () => void | Promise<void>;
193
+ /**
194
+ * Called when an error occurs during login flow initialization or submission.
195
+ */
196
+ onError?: (error: Error) => void | Promise<void>;
197
+ /**
198
+ * Called when a global message is received during the login flow.
199
+ */
200
+ onGlobalMessage?: (message: NativeFlowMessage) => void | Promise<void>;
201
+ };
202
+ export type WithAuthGuardOptions = {
203
+ /**
204
+ * The URL to redirect the user to if they are not authenticated.
205
+ *
206
+ * @default '/login'
207
+ */
208
+ loginUri?: string;
209
+ /**
210
+ * A component to render while the authentication state is being determined. Defaults to `null`.
211
+ */
212
+ onLoading?: () => React.ReactNode;
176
213
  };
package/dist/types.mjs CHANGED
@@ -1,2 +1 @@
1
-
2
- //# sourceMappingURL=types.mjs.map
1
+ import"./assets/rolldown-runtime.mjs";export*from"@strivacity/sdk-core/types";
package/dist/utils.cjs ADDED
@@ -0,0 +1 @@
1
+ require("./assets/rolldown-runtime.cjs");var e=require("@strivacity/sdk-core/utils");Object.keys(e).forEach(function(t){t!=="default"&&!Object.prototype.hasOwnProperty.call(exports,t)&&Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})});
@@ -0,0 +1 @@
1
+ export * from '@strivacity/sdk-core/utils';
package/dist/utils.mjs ADDED
@@ -0,0 +1 @@
1
+ import"./assets/rolldown-runtime.mjs";export*from"@strivacity/sdk-core/utils";
package/package.json CHANGED
@@ -1,20 +1,64 @@
1
1
  {
2
2
  "name": "@strivacity/sdk-react",
3
- "version": "3.0.3",
4
- "license": "MIT",
3
+ "version": "4.0.0-beta.0",
5
4
  "description": "Strivacity React SDK client",
5
+ "license": "MIT",
6
6
  "author": "strivacity <opensource@strivacity.com>",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "https://github.com/Strivacity/sdk-js"
10
10
  },
11
+ "sideEffects": false,
12
+ "main": "./dist/index.cjs",
13
+ "types": "./dist/types.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.mjs",
18
+ "require": "./dist/index.cjs",
19
+ "default": "./dist/index.mjs"
20
+ },
21
+ "./components": {
22
+ "types": "./dist/components.d.ts",
23
+ "import": "./dist/components.mjs",
24
+ "require": "./dist/components.cjs",
25
+ "default": "./dist/components.mjs"
26
+ },
27
+ "./errors": {
28
+ "types": "./dist/errors.d.ts",
29
+ "import": "./dist/errors.mjs",
30
+ "require": "./dist/errors.cjs",
31
+ "default": "./dist/errors.mjs"
32
+ },
33
+ "./hooks": {
34
+ "types": "./dist/hooks.d.ts",
35
+ "import": "./dist/hooks.mjs",
36
+ "require": "./dist/hooks.cjs",
37
+ "default": "./dist/hooks.mjs"
38
+ },
39
+ "./storages": {
40
+ "types": "./dist/storages.d.ts",
41
+ "import": "./dist/storages.mjs",
42
+ "require": "./dist/storages.cjs",
43
+ "default": "./dist/storages.mjs"
44
+ },
45
+ "./types": {
46
+ "types": "./dist/types.d.ts",
47
+ "import": "./dist/types.mjs",
48
+ "require": "./dist/types.cjs",
49
+ "default": "./dist/types.mjs"
50
+ },
51
+ "./utils": {
52
+ "types": "./dist/utils.d.ts",
53
+ "import": "./dist/utils.mjs",
54
+ "require": "./dist/utils.cjs",
55
+ "default": "./dist/utils.mjs"
56
+ }
57
+ },
11
58
  "dependencies": {
12
- "@strivacity/sdk-core": "3.0.3"
59
+ "@strivacity/sdk-core": "4.0.0-beta.0"
13
60
  },
14
61
  "peerDependencies": {
15
62
  "react": ">=18"
16
- },
17
- "main": "./dist/index.cjs",
18
- "module": "./dist/index.mjs",
19
- "types": "./dist/index.d.ts"
63
+ }
20
64
  }
@@ -0,0 +1,146 @@
1
+ import type { SDKContext, RedirectFlow, SessionData } from '../../src/types';
2
+ import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
3
+ import { initFlow } from '@strivacity/sdk-core';
4
+ import { StyAuthProvider } from '../../src/components';
5
+ import { useStrivacity } from '../../src/hooks';
6
+ import { createMockFlow } from '@strivacity/testing/mocks/sdk';
7
+ import { mount, cleanupMounts, flush, act } from '../utils/common';
8
+
9
+ vi.mock('@strivacity/sdk-core', async (importOriginal) => ({
10
+ ...(await importOriginal<typeof import('@strivacity/sdk-core')>()),
11
+ initFlow: vi.fn(),
12
+ }));
13
+
14
+ afterEach(() => {
15
+ cleanupMounts();
16
+ });
17
+
18
+ function install(flow: RedirectFlow, session: SessionData | null = null) {
19
+ vi.mocked(initFlow).mockReturnValue(flow as never);
20
+
21
+ let context: SDKContext<RedirectFlow> | undefined;
22
+
23
+ function Consumer() {
24
+ context = useStrivacity<RedirectFlow>();
25
+ return null;
26
+ }
27
+
28
+ const options = { mode: 'redirect' as const, issuer: 'https://brandtegrity.io', clientId: 'client-id', redirectUri: 'https://brandtegrity.io/callback' };
29
+
30
+ mount(
31
+ <StyAuthProvider options={options} session={session ?? undefined}>
32
+ <Consumer />
33
+ </StyAuthProvider>,
34
+ );
35
+
36
+ return { context: () => context!, options };
37
+ }
38
+
39
+ describe('StyAuthProvider', () => {
40
+ beforeEach(() => {
41
+ vi.mocked(initFlow).mockReset();
42
+ });
43
+
44
+ test('initializes the flow through core sdk initFlow with the given options', async () => {
45
+ const flow = createMockFlow<RedirectFlow>();
46
+ const { options } = install(flow);
47
+ await flush();
48
+
49
+ expect(initFlow).toHaveBeenCalledTimes(1);
50
+ expect(initFlow).toHaveBeenCalledWith(options);
51
+ });
52
+
53
+ test('subscribes to all sdk events on mount and disposes on unmount', async () => {
54
+ const dispose = vi.fn();
55
+ const flow = createMockFlow<RedirectFlow>({ subscribeToAllEvents: vi.fn().mockReturnValue({ dispose }) });
56
+ install(flow);
57
+ await flush();
58
+
59
+ expect(flow.subscribeToAllEvents).toHaveBeenCalledTimes(1);
60
+ expect(flow.subscribeToAllEvents).toHaveBeenCalledWith(expect.any(Function));
61
+
62
+ cleanupMounts();
63
+
64
+ expect(dispose).toHaveBeenCalledTimes(1);
65
+ });
66
+
67
+ test('seeds the flow session when a session prop is given', async () => {
68
+ const flow = createMockFlow<RedirectFlow>();
69
+ const session = { access_token: 'access-token' } as unknown as SessionData;
70
+
71
+ install(flow, session);
72
+ await flush();
73
+
74
+ expect(flow.session).toBe(session);
75
+ });
76
+
77
+ test.each([
78
+ ['init', [], undefined],
79
+ ['checkAuthentication', [{ autoRefresh: false }], true],
80
+ ['tokenExchange', [{ code: 'abc' }], undefined],
81
+ ['handleCallback', ['https://brandtegrity.io/callback?code=abc'], undefined],
82
+ ['refresh', [], undefined],
83
+ ['revoke', [], undefined],
84
+ ['logout', [{ postLogoutRedirectUri: 'https://brandtegrity.io' }], undefined],
85
+ ['login', [{ scopes: ['openid'] }], undefined],
86
+ ['register', [{ scopes: ['openid'] }], undefined],
87
+ ['entry', ['https://brandtegrity.io/entry'], undefined],
88
+ ] as const)('%s calls through to the flow instance returned by core sdk', async (method, args, resolvedValue) => {
89
+ const flow = createMockFlow<RedirectFlow>({ [method]: vi.fn().mockReturnValue(resolvedValue) });
90
+ const { context } = install(flow);
91
+ await flush();
92
+
93
+ const result = await (context()[method] as (...a: Array<unknown>) => unknown)(...args);
94
+
95
+ expect(flow[method as keyof RedirectFlow]).toHaveBeenCalledWith(...args);
96
+ expect(result).toEqual(resolvedValue);
97
+ });
98
+
99
+ test('exposes reactive state seeded from the flow instance after the initial session update settles', async () => {
100
+ const flow = createMockFlow<RedirectFlow>({
101
+ isAuthenticated: Promise.resolve(true),
102
+ language: 'hu-HU',
103
+ idTokenClaims: { sub: 'user-1' },
104
+ accessToken: 'access-token',
105
+ refreshToken: 'refresh-token',
106
+ accessTokenExpired: false,
107
+ accessTokenExpirationDate: 1234,
108
+ });
109
+ const { context } = install(flow);
110
+
111
+ expect(context().loading).toBe(true);
112
+
113
+ await flush();
114
+
115
+ expect(context().loading).toBe(false);
116
+ expect(context().isAuthenticated).toBe(true);
117
+ expect(context().language).toBe('hu-HU');
118
+ expect(context().idTokenClaims).toEqual({ sub: 'user-1' });
119
+ expect(context().accessToken).toBe('access-token');
120
+ expect(context().refreshToken).toBe('refresh-token');
121
+ expect(context().accessTokenExpired).toBe(false);
122
+ expect(context().accessTokenExpirationDate).toBe(1234);
123
+ });
124
+
125
+ test('reactive state updates when the flow emits an event', async () => {
126
+ const subscribeToAllEvents = vi.fn().mockReturnValue({ dispose: vi.fn() });
127
+ const flow = createMockFlow<RedirectFlow>({ subscribeToAllEvents });
128
+ const { context } = install(flow);
129
+ await flush();
130
+
131
+ const onEvent = subscribeToAllEvents.mock.calls[0][0];
132
+ const mutableFlow = flow as unknown as { language: string; accessToken: string | null; isAuthenticated: Promise<boolean> };
133
+
134
+ mutableFlow.language = 'de-DE';
135
+ mutableFlow.accessToken = 'new-token';
136
+ mutableFlow.isAuthenticated = Promise.resolve(true);
137
+
138
+ await act(async () => {
139
+ await onEvent();
140
+ });
141
+
142
+ expect(context().language).toBe('de-DE');
143
+ expect(context().accessToken).toBe('new-token');
144
+ expect(context().isAuthenticated).toBe(true);
145
+ });
146
+ });
@@ -0,0 +1,10 @@
1
+ import { test, expect } from 'vitest';
2
+ import * as reactErrors from '../../src/errors';
3
+ import * as coreErrors from '@strivacity/sdk-core/utils/errors';
4
+
5
+ test('re-exports error classes from the core sdk', () => {
6
+ const coreKeys = Object.keys(coreErrors);
7
+
8
+ expect(coreKeys.length).toBeGreaterThan(0);
9
+ expect(Object.keys(reactErrors).sort()).toEqual(coreKeys.sort());
10
+ });