rystem.authentication.social.client 0.5.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 +1903 -0
- package/lib/buttons/CreateSocialButton.d.ts +15 -0
- package/lib/buttons/CreateSocialButton.js +158 -0
- package/lib/buttons/SocialLoginButtons.d.ts +2 -0
- package/lib/buttons/SocialLoginButtons.js +22 -0
- package/lib/buttons/SocialLogoutButton.d.ts +3 -0
- package/lib/buttons/SocialLogoutButton.js +11 -0
- package/lib/buttons/graphics/AmazonLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/AmazonLoginButton.js +10 -0
- package/lib/buttons/graphics/AppleLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/AppleLoginButton.js +17 -0
- package/lib/buttons/graphics/BufferLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/BufferLoginButton.js +17 -0
- package/lib/buttons/graphics/DiscordLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/DiscordLoginButton.js +17 -0
- package/lib/buttons/graphics/FacebookLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/FacebookLoginButton.js +10 -0
- package/lib/buttons/graphics/GithubLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/GithubLoginButton.js +10 -0
- package/lib/buttons/graphics/GoogleLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/GoogleLoginButton.js +10 -0
- package/lib/buttons/graphics/InstagramLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/InstagramLoginButton.js +10 -0
- package/lib/buttons/graphics/LinkedInLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/LinkedInLoginButton.js +10 -0
- package/lib/buttons/graphics/MetamaskLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/MetamaskLoginButton.js +25 -0
- package/lib/buttons/graphics/MicrosoftLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/MicrosoftLoginButton.js +10 -0
- package/lib/buttons/graphics/OktaLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/OktaLoginButton.js +17 -0
- package/lib/buttons/graphics/PinterestLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/PinterestLoginButton.js +10 -0
- package/lib/buttons/graphics/SlackLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/SlackLoginButton.js +17 -0
- package/lib/buttons/graphics/SocialButtonStyle.d.ts +8 -0
- package/lib/buttons/graphics/SocialButtonStyle.js +2 -0
- package/lib/buttons/graphics/SocialLoginButton.d.ts +2 -0
- package/lib/buttons/graphics/SocialLoginButton.js +46 -0
- package/lib/buttons/graphics/TelegramLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/TelegramLoginButton.js +17 -0
- package/lib/buttons/graphics/TikTokLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/TikTokLoginButton.js +10 -0
- package/lib/buttons/graphics/XLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/XLoginButton.js +10 -0
- package/lib/buttons/graphics/YahooLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/YahooLoginButton.js +17 -0
- package/lib/buttons/graphics/ZaloLoginButton.d.ts +1 -0
- package/lib/buttons/graphics/ZaloLoginButton.js +17 -0
- package/lib/buttons/singles/AmazonButton.d.ts +2 -0
- package/lib/buttons/singles/AmazonButton.js +33 -0
- package/lib/buttons/singles/FacebookButton.d.ts +2 -0
- package/lib/buttons/singles/FacebookButton.js +55 -0
- package/lib/buttons/singles/GitHubButton.d.ts +2 -0
- package/lib/buttons/singles/GitHubButton.js +19 -0
- package/lib/buttons/singles/GoogleButton.d.ts +2 -0
- package/lib/buttons/singles/GoogleButton.js +44 -0
- package/lib/buttons/singles/InstagramButton.d.ts +2 -0
- package/lib/buttons/singles/InstagramButton.js +21 -0
- package/lib/buttons/singles/LinkedinButton.d.ts +2 -0
- package/lib/buttons/singles/LinkedinButton.js +23 -0
- package/lib/buttons/singles/MicrosoftButton.d.ts +2 -0
- package/lib/buttons/singles/MicrosoftButton.js +68 -0
- package/lib/buttons/singles/PinterestButton.d.ts +2 -0
- package/lib/buttons/singles/PinterestButton.js +22 -0
- package/lib/buttons/singles/TikTokButton.d.ts +2 -0
- package/lib/buttons/singles/TikTokButton.js +18 -0
- package/lib/buttons/singles/XButton.d.ts +2 -0
- package/lib/buttons/singles/XButton.js +24 -0
- package/lib/components/BrandIcons.d.ts +64 -0
- package/lib/components/BrandIcons.js +76 -0
- package/lib/components/ModernSocialButton.d.ts +21 -0
- package/lib/components/ModernSocialButton.js +23 -0
- package/lib/context/SocialLoginContext.d.ts +4 -0
- package/lib/context/SocialLoginContext.js +10 -0
- package/lib/context/SocialLoginWrapper.d.ts +3 -0
- package/lib/context/SocialLoginWrapper.js +137 -0
- package/lib/hooks/removeSocialLogin.d.ts +1 -0
- package/lib/hooks/removeSocialLogin.js +14 -0
- package/lib/hooks/useSocialToken.d.ts +2 -0
- package/lib/hooks/useSocialToken.js +21 -0
- package/lib/hooks/useSocialUser.d.ts +2 -0
- package/lib/hooks/useSocialUser.js +30 -0
- package/lib/index.d.ts +47 -0
- package/lib/index.js +90 -0
- package/lib/models/SocialButtonProps.d.ts +3 -0
- package/lib/models/SocialButtonProps.js +2 -0
- package/lib/models/SocialButtonsProps.d.ts +4 -0
- package/lib/models/SocialButtonsProps.js +2 -0
- package/lib/models/SocialToken.d.ts +6 -0
- package/lib/models/SocialToken.js +2 -0
- package/lib/models/SocialUser.d.ts +5 -0
- package/lib/models/SocialUser.js +2 -0
- package/lib/models/Token.d.ts +6 -0
- package/lib/models/Token.js +2 -0
- package/lib/models/setup/LoginMode.d.ts +13 -0
- package/lib/models/setup/LoginMode.js +17 -0
- package/lib/models/setup/PlatformConfig.d.ts +45 -0
- package/lib/models/setup/PlatformConfig.js +19 -0
- package/lib/models/setup/PlatformType.d.ts +21 -0
- package/lib/models/setup/PlatformType.js +25 -0
- package/lib/models/setup/ProviderType.d.ts +13 -0
- package/lib/models/setup/ProviderType.js +17 -0
- package/lib/models/setup/SocialLoginErrorResponse.d.ts +6 -0
- package/lib/models/setup/SocialLoginErrorResponse.js +2 -0
- package/lib/models/setup/SocialLoginSettings.d.ts +66 -0
- package/lib/models/setup/SocialLoginSettings.js +2 -0
- package/lib/models/setup/SocialParameter.d.ts +3 -0
- package/lib/models/setup/SocialParameter.js +2 -0
- package/lib/services/IRoutingService.d.ts +123 -0
- package/lib/services/IRoutingService.js +2 -0
- package/lib/services/IStorageService.d.ts +33 -0
- package/lib/services/IStorageService.js +2 -0
- package/lib/services/LocalStorageService.d.ts +32 -0
- package/lib/services/LocalStorageService.js +93 -0
- package/lib/services/MockRoutingService.example.d.ts +96 -0
- package/lib/services/MockRoutingService.example.js +153 -0
- package/lib/services/NextAppRouterRoutingService.example.d.ts +75 -0
- package/lib/services/NextAppRouterRoutingService.example.js +128 -0
- package/lib/services/PkceStorageService.d.ts +55 -0
- package/lib/services/PkceStorageService.js +103 -0
- package/lib/services/ReactRouterRoutingService.example.d.ts +69 -0
- package/lib/services/ReactRouterRoutingService.example.js +121 -0
- package/lib/services/TokenStorageService.d.ts +34 -0
- package/lib/services/TokenStorageService.js +90 -0
- package/lib/services/UserStorageService.d.ts +29 -0
- package/lib/services/UserStorageService.js +56 -0
- package/lib/services/WindowRoutingService.d.ts +74 -0
- package/lib/services/WindowRoutingService.js +118 -0
- package/lib/setup/SocialLoginManager.d.ts +12 -0
- package/lib/setup/SocialLoginManager.js +106 -0
- package/lib/setup/getSocialLoginSettings.d.ts +2 -0
- package/lib/setup/getSocialLoginSettings.js +8 -0
- package/lib/setup/setupSocialLogin.d.ts +2 -0
- package/lib/setup/setupSocialLogin.js +62 -0
- package/lib/styles/SocialButton.css +365 -0
- package/lib/utils/pkce.d.ts +18 -0
- package/lib/utils/pkce.js +44 -0
- package/lib/utils/platform.d.ts +30 -0
- package/lib/utils/platform.js +103 -0
- package/package.json +45 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { SocialLoginErrorResponse, SocialParameter } from "../..";
|
|
2
|
+
import { PlatformConfig } from "./PlatformConfig";
|
|
3
|
+
import { IStorageService } from "../../services/IStorageService";
|
|
4
|
+
import { IRoutingService } from "../../services/IRoutingService";
|
|
5
|
+
export interface SocialLoginSettings {
|
|
6
|
+
apiUri: string;
|
|
7
|
+
automaticRefresh: boolean;
|
|
8
|
+
identityTransformer?: IIdentityTransformer<any>;
|
|
9
|
+
onLoginFailure: (data: SocialLoginErrorResponse) => void;
|
|
10
|
+
title: string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Storage service for persisting tokens, PKCE verifiers, etc.
|
|
13
|
+
* Default: LocalStorageService (browser localStorage)
|
|
14
|
+
*
|
|
15
|
+
* @example Custom secure storage for mobile
|
|
16
|
+
* storageService: new SecureStorageService()
|
|
17
|
+
*/
|
|
18
|
+
storageService: IStorageService;
|
|
19
|
+
/**
|
|
20
|
+
* Routing service for URL parameter reading and navigation
|
|
21
|
+
* Default: WindowRoutingService (uses window.location and window.history)
|
|
22
|
+
*
|
|
23
|
+
* IMPORTANT: Required for React Router, Next.js App Router, and other client-side routing frameworks
|
|
24
|
+
* to properly handle OAuth callbacks, redirects, and return URLs.
|
|
25
|
+
*
|
|
26
|
+
* @example React Router with useSearchParams, useNavigate, and useLocation hooks
|
|
27
|
+
* routingService: new ReactRouterRoutingService()
|
|
28
|
+
*
|
|
29
|
+
* @example Next.js App Router with useRouter, usePathname, and useSearchParams
|
|
30
|
+
* routingService: new NextAppRouterRoutingService()
|
|
31
|
+
*/
|
|
32
|
+
routingService: IRoutingService;
|
|
33
|
+
/**
|
|
34
|
+
* Platform configuration (Web, iOS, Android)
|
|
35
|
+
*
|
|
36
|
+
* @example Web configuration (auto-detect domain)
|
|
37
|
+
* platform: {
|
|
38
|
+
* type: PlatformType.Web,
|
|
39
|
+
* redirectPath: "/account/login"
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* @example React Native iOS configuration
|
|
43
|
+
* platform: {
|
|
44
|
+
* type: PlatformType.iOS,
|
|
45
|
+
* redirectPath: "myapp://oauth/callback" // Complete deep link
|
|
46
|
+
* }
|
|
47
|
+
*
|
|
48
|
+
* @default { type: 'auto', redirectPath: '/account/login', loginMode: 'popup' }
|
|
49
|
+
*/
|
|
50
|
+
platform?: PlatformConfig;
|
|
51
|
+
google: SocialParameter;
|
|
52
|
+
microsoft: SocialParameter;
|
|
53
|
+
facebook: SocialParameter;
|
|
54
|
+
github: SocialParameter;
|
|
55
|
+
amazon: SocialParameter;
|
|
56
|
+
linkedin: SocialParameter;
|
|
57
|
+
x: SocialParameter;
|
|
58
|
+
instagram: SocialParameter;
|
|
59
|
+
pinterest: SocialParameter;
|
|
60
|
+
tiktok: SocialParameter;
|
|
61
|
+
}
|
|
62
|
+
export interface IIdentityTransformer<TIdentity> {
|
|
63
|
+
toPlain: (input: TIdentity | any) => any;
|
|
64
|
+
fromPlain: (input: any) => TIdentity;
|
|
65
|
+
retrieveUsername: (input: TIdentity) => string;
|
|
66
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified routing service for URL parameter reading and navigation
|
|
3
|
+
* Handles both OAuth callback detection and client-side routing operations
|
|
4
|
+
*
|
|
5
|
+
* This service solves compatibility issues with client-side routing frameworks
|
|
6
|
+
* (React Router, Next.js, Remix) where native browser APIs bypass the router.
|
|
7
|
+
*
|
|
8
|
+
* @example Default (Vanilla React, no routing library)
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const routingService = new WindowRoutingService();
|
|
11
|
+
* setupSocialLogin(x => {
|
|
12
|
+
* x.routingService = routingService; // Uses window.location and window.history
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @example React Router
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const routingService = new ReactRouterRoutingService();
|
|
19
|
+
* setupSocialLogin(x => {
|
|
20
|
+
* x.routingService = routingService;
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // In component:
|
|
24
|
+
* const [searchParams] = useSearchParams();
|
|
25
|
+
* const navigate = useNavigate();
|
|
26
|
+
* const location = useLocation();
|
|
27
|
+
* routingService.initialize(() => searchParams, navigate, location);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export interface IRoutingService {
|
|
31
|
+
/**
|
|
32
|
+
* Get a single URL parameter value
|
|
33
|
+
* Used for reading OAuth callback parameters (code, state)
|
|
34
|
+
*
|
|
35
|
+
* @param key - Parameter name (e.g., 'code', 'state')
|
|
36
|
+
* @returns Parameter value or null if not found
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const code = routingService.getSearchParam('code');
|
|
41
|
+
* const state = routingService.getSearchParam('state');
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
getSearchParam(key: string): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* Get all URL parameters
|
|
47
|
+
* Used for debugging or advanced OAuth scenarios
|
|
48
|
+
*
|
|
49
|
+
* @returns URLSearchParams object with all query parameters
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const allParams = routingService.getAllSearchParams();
|
|
54
|
+
* console.log('OAuth callback params:', allParams.toString());
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
getAllSearchParams(): URLSearchParams;
|
|
58
|
+
/**
|
|
59
|
+
* Get current path including search parameters
|
|
60
|
+
* Used for saving return URL before OAuth redirect
|
|
61
|
+
*
|
|
62
|
+
* @returns Current route path with query string (e.g., "/dashboard?tab=settings")
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* // Save current location before OAuth
|
|
67
|
+
* const returnUrl = routingService.getCurrentPath();
|
|
68
|
+
* storageService.set('return_url', returnUrl);
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
getCurrentPath(): string;
|
|
72
|
+
/**
|
|
73
|
+
* Navigate to a URL (for OAuth redirects and internal navigation)
|
|
74
|
+
*
|
|
75
|
+
* @param url - Full URL (https://...) for external OAuth, or path (/login) for internal
|
|
76
|
+
*
|
|
77
|
+
* @example OAuth redirect
|
|
78
|
+
* ```typescript
|
|
79
|
+
* routingService.navigateTo('https://login.microsoftonline.com/oauth2/authorize?...');
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @example Internal navigation (React Router)
|
|
83
|
+
* ```typescript
|
|
84
|
+
* routingService.navigateTo('/dashboard'); // Calls navigate() internally
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
navigateTo(url: string): void;
|
|
88
|
+
/**
|
|
89
|
+
* Replace current URL without full navigation (cleanup after OAuth callback)
|
|
90
|
+
* Updates URL bar without triggering page reload or adding to browser history
|
|
91
|
+
*
|
|
92
|
+
* @param path - Path to replace current URL with
|
|
93
|
+
*
|
|
94
|
+
* @example Clean URL after OAuth
|
|
95
|
+
* ```typescript
|
|
96
|
+
* // Before: /account/login?code=ABC123&state=microsoft
|
|
97
|
+
* routingService.navigateReplace('/account/login');
|
|
98
|
+
* // After: /account/login (query params removed)
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
navigateReplace(path: string): void;
|
|
102
|
+
/**
|
|
103
|
+
* Open URL in popup window (for popup mode OAuth)
|
|
104
|
+
*
|
|
105
|
+
* @param url - URL to open in popup
|
|
106
|
+
* @param name - Window name/target
|
|
107
|
+
* @param features - Window features (size, position, menubar, etc.)
|
|
108
|
+
* @returns Window reference or null if blocked by popup blocker
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const popup = routingService.openPopup(
|
|
113
|
+
* 'https://oauth.provider.com/authorize',
|
|
114
|
+
* 'oauth_popup',
|
|
115
|
+
* 'width=450,height=730,top=100,left=100'
|
|
116
|
+
* );
|
|
117
|
+
* if (!popup) {
|
|
118
|
+
* alert('Popup blocked! Please allow popups for this site.');
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
openPopup(url: string, name: string, features: string): Window | null;
|
|
123
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic storage service interface (key-value storage)
|
|
3
|
+
* Allows different infrastructure implementations (localStorage, secure storage, Redis, etc.)
|
|
4
|
+
*/
|
|
5
|
+
export interface IStorageService {
|
|
6
|
+
/**
|
|
7
|
+
* Get value by key
|
|
8
|
+
* @param key Storage key
|
|
9
|
+
* @returns Value or null if not found
|
|
10
|
+
*/
|
|
11
|
+
get(key: string): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Set value for key
|
|
14
|
+
* @param key Storage key
|
|
15
|
+
* @param value Value to store
|
|
16
|
+
*/
|
|
17
|
+
set(key: string, value: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Remove value by key
|
|
20
|
+
* @param key Storage key
|
|
21
|
+
*/
|
|
22
|
+
remove(key: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Check if key exists
|
|
25
|
+
* @param key Storage key
|
|
26
|
+
* @returns True if key exists
|
|
27
|
+
*/
|
|
28
|
+
has(key: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Clear all stored values (optional)
|
|
31
|
+
*/
|
|
32
|
+
clear?(): void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { IStorageService } from "./IStorageService";
|
|
2
|
+
/**
|
|
3
|
+
* Default storage implementation using browser localStorage
|
|
4
|
+
* Works for web applications (browser environment)
|
|
5
|
+
* For React Native, provide a custom IStorageService implementation
|
|
6
|
+
*/
|
|
7
|
+
export declare class LocalStorageService implements IStorageService {
|
|
8
|
+
/**
|
|
9
|
+
* Check if localStorage is available (browser environment)
|
|
10
|
+
*/
|
|
11
|
+
private isAvailable;
|
|
12
|
+
/**
|
|
13
|
+
* Get value from localStorage
|
|
14
|
+
*/
|
|
15
|
+
get(key: string): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Set value in localStorage
|
|
18
|
+
*/
|
|
19
|
+
set(key: string, value: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Remove value from localStorage
|
|
22
|
+
*/
|
|
23
|
+
remove(key: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Check if key exists in localStorage
|
|
26
|
+
*/
|
|
27
|
+
has(key: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Clear all localStorage
|
|
30
|
+
*/
|
|
31
|
+
clear(): void;
|
|
32
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalStorageService = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Default storage implementation using browser localStorage
|
|
6
|
+
* Works for web applications (browser environment)
|
|
7
|
+
* For React Native, provide a custom IStorageService implementation
|
|
8
|
+
*/
|
|
9
|
+
class LocalStorageService {
|
|
10
|
+
/**
|
|
11
|
+
* Check if localStorage is available (browser environment)
|
|
12
|
+
*/
|
|
13
|
+
isAvailable() {
|
|
14
|
+
return typeof window !== 'undefined' && typeof localStorage !== 'undefined';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get value from localStorage
|
|
18
|
+
*/
|
|
19
|
+
get(key) {
|
|
20
|
+
if (!this.isAvailable()) {
|
|
21
|
+
console.warn('LocalStorageService: localStorage not available (React Native or SSR?)');
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
return localStorage.getItem(key);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error(`LocalStorageService: Error getting key "${key}"`, error);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Set value in localStorage
|
|
34
|
+
*/
|
|
35
|
+
set(key, value) {
|
|
36
|
+
if (!this.isAvailable()) {
|
|
37
|
+
console.warn('LocalStorageService: localStorage not available (React Native or SSR?)');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
localStorage.setItem(key, value);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error(`LocalStorageService: Error setting key "${key}"`, error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Remove value from localStorage
|
|
49
|
+
*/
|
|
50
|
+
remove(key) {
|
|
51
|
+
if (!this.isAvailable()) {
|
|
52
|
+
console.warn('LocalStorageService: localStorage not available (React Native or SSR?)');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
localStorage.removeItem(key);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error(`LocalStorageService: Error removing key "${key}"`, error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if key exists in localStorage
|
|
64
|
+
*/
|
|
65
|
+
has(key) {
|
|
66
|
+
if (!this.isAvailable()) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return localStorage.getItem(key) !== null;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error(`LocalStorageService: Error checking key "${key}"`, error);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Clear all localStorage
|
|
79
|
+
*/
|
|
80
|
+
clear() {
|
|
81
|
+
if (!this.isAvailable()) {
|
|
82
|
+
console.warn('LocalStorageService: localStorage not available (React Native or SSR?)');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
localStorage.clear();
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error('LocalStorageService: Error clearing storage', error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.LocalStorageService = LocalStorageService;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Routing Service for unit testing
|
|
3
|
+
*
|
|
4
|
+
* This is a simple in-memory implementation for testing purposes.
|
|
5
|
+
* Allows setting URL parameters and controlling navigation programmatically.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { MockRoutingService } from './MockRoutingService';
|
|
10
|
+
*
|
|
11
|
+
* const mockRouting = new MockRoutingService();
|
|
12
|
+
*
|
|
13
|
+
* // Setup test data
|
|
14
|
+
* mockRouting.setSearchParam('code', 'test-auth-code-12345');
|
|
15
|
+
* mockRouting.setSearchParam('state', 'microsoft');
|
|
16
|
+
* mockRouting.setCurrentPath('/account/login?tab=oauth');
|
|
17
|
+
*
|
|
18
|
+
* setupSocialLogin(x => {
|
|
19
|
+
* x.routingService = mockRouting;
|
|
20
|
+
* // ... rest of test config
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // In your test
|
|
24
|
+
* expect(mockRouting.getSearchParam('code')).toBe('test-auth-code-12345');
|
|
25
|
+
* expect(mockRouting.wasNavigateToCalledWith('https://oauth.provider.com')).toBe(true);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
import type { IRoutingService } from './IRoutingService';
|
|
29
|
+
/**
|
|
30
|
+
* Mock Routing Service for testing
|
|
31
|
+
* Stores state in-memory and tracks navigation calls
|
|
32
|
+
*/
|
|
33
|
+
export declare class MockRoutingService implements IRoutingService {
|
|
34
|
+
private params;
|
|
35
|
+
private currentPath;
|
|
36
|
+
private navigationHistory;
|
|
37
|
+
private replaceHistory;
|
|
38
|
+
private popupCalls;
|
|
39
|
+
/**
|
|
40
|
+
* Set a URL parameter (for testing OAuth callbacks)
|
|
41
|
+
* @param key - Parameter name
|
|
42
|
+
* @param value - Parameter value
|
|
43
|
+
*/
|
|
44
|
+
setSearchParam(key: string, value: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Set multiple parameters at once
|
|
47
|
+
* @param params - Object with key-value pairs
|
|
48
|
+
*/
|
|
49
|
+
setSearchParams(params: Record<string, string>): void;
|
|
50
|
+
/**
|
|
51
|
+
* Set the current path
|
|
52
|
+
* @param path - Path with query string (e.g., "/dashboard?tab=settings")
|
|
53
|
+
*/
|
|
54
|
+
setCurrentPath(path: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Clear all state (for test cleanup)
|
|
57
|
+
*/
|
|
58
|
+
reset(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Check if navigateTo was called with specific URL
|
|
61
|
+
* @param url - URL to check
|
|
62
|
+
* @returns True if navigateTo was called with this URL
|
|
63
|
+
*/
|
|
64
|
+
wasNavigateToCalledWith(url: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Get all navigateTo calls
|
|
67
|
+
* @returns Array of URLs passed to navigateTo
|
|
68
|
+
*/
|
|
69
|
+
getNavigationHistory(): string[];
|
|
70
|
+
/**
|
|
71
|
+
* Check if navigateReplace was called with specific path
|
|
72
|
+
* @param path - Path to check
|
|
73
|
+
* @returns True if navigateReplace was called with this path
|
|
74
|
+
*/
|
|
75
|
+
wasReplaceCalledWith(path: string): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Get all navigateReplace calls
|
|
78
|
+
* @returns Array of paths passed to navigateReplace
|
|
79
|
+
*/
|
|
80
|
+
getReplaceHistory(): string[];
|
|
81
|
+
/**
|
|
82
|
+
* Get all openPopup calls
|
|
83
|
+
* @returns Array of popup call parameters
|
|
84
|
+
*/
|
|
85
|
+
getPopupCalls(): Array<{
|
|
86
|
+
url: string;
|
|
87
|
+
name: string;
|
|
88
|
+
features: string;
|
|
89
|
+
}>;
|
|
90
|
+
getSearchParam(key: string): string | null;
|
|
91
|
+
getAllSearchParams(): URLSearchParams;
|
|
92
|
+
getCurrentPath(): string;
|
|
93
|
+
navigateTo(url: string): void;
|
|
94
|
+
navigateReplace(path: string): void;
|
|
95
|
+
openPopup(url: string, name: string, features: string): Window | null;
|
|
96
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Mock Routing Service for unit testing
|
|
4
|
+
*
|
|
5
|
+
* This is a simple in-memory implementation for testing purposes.
|
|
6
|
+
* Allows setting URL parameters and controlling navigation programmatically.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { MockRoutingService } from './MockRoutingService';
|
|
11
|
+
*
|
|
12
|
+
* const mockRouting = new MockRoutingService();
|
|
13
|
+
*
|
|
14
|
+
* // Setup test data
|
|
15
|
+
* mockRouting.setSearchParam('code', 'test-auth-code-12345');
|
|
16
|
+
* mockRouting.setSearchParam('state', 'microsoft');
|
|
17
|
+
* mockRouting.setCurrentPath('/account/login?tab=oauth');
|
|
18
|
+
*
|
|
19
|
+
* setupSocialLogin(x => {
|
|
20
|
+
* x.routingService = mockRouting;
|
|
21
|
+
* // ... rest of test config
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // In your test
|
|
25
|
+
* expect(mockRouting.getSearchParam('code')).toBe('test-auth-code-12345');
|
|
26
|
+
* expect(mockRouting.wasNavigateToCalledWith('https://oauth.provider.com')).toBe(true);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.MockRoutingService = void 0;
|
|
31
|
+
/**
|
|
32
|
+
* Mock Routing Service for testing
|
|
33
|
+
* Stores state in-memory and tracks navigation calls
|
|
34
|
+
*/
|
|
35
|
+
class MockRoutingService {
|
|
36
|
+
params = new Map();
|
|
37
|
+
currentPath = '/';
|
|
38
|
+
navigationHistory = [];
|
|
39
|
+
replaceHistory = [];
|
|
40
|
+
popupCalls = [];
|
|
41
|
+
// ===== Setup Methods (for tests) =====
|
|
42
|
+
/**
|
|
43
|
+
* Set a URL parameter (for testing OAuth callbacks)
|
|
44
|
+
* @param key - Parameter name
|
|
45
|
+
* @param value - Parameter value
|
|
46
|
+
*/
|
|
47
|
+
setSearchParam(key, value) {
|
|
48
|
+
this.params.set(key, value);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Set multiple parameters at once
|
|
52
|
+
* @param params - Object with key-value pairs
|
|
53
|
+
*/
|
|
54
|
+
setSearchParams(params) {
|
|
55
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
56
|
+
this.params.set(key, value);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Set the current path
|
|
61
|
+
* @param path - Path with query string (e.g., "/dashboard?tab=settings")
|
|
62
|
+
*/
|
|
63
|
+
setCurrentPath(path) {
|
|
64
|
+
this.currentPath = path;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Clear all state (for test cleanup)
|
|
68
|
+
*/
|
|
69
|
+
reset() {
|
|
70
|
+
this.params.clear();
|
|
71
|
+
this.currentPath = '/';
|
|
72
|
+
this.navigationHistory = [];
|
|
73
|
+
this.replaceHistory = [];
|
|
74
|
+
this.popupCalls = [];
|
|
75
|
+
}
|
|
76
|
+
// ===== Verification Methods (for test assertions) =====
|
|
77
|
+
/**
|
|
78
|
+
* Check if navigateTo was called with specific URL
|
|
79
|
+
* @param url - URL to check
|
|
80
|
+
* @returns True if navigateTo was called with this URL
|
|
81
|
+
*/
|
|
82
|
+
wasNavigateToCalledWith(url) {
|
|
83
|
+
return this.navigationHistory.includes(url);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get all navigateTo calls
|
|
87
|
+
* @returns Array of URLs passed to navigateTo
|
|
88
|
+
*/
|
|
89
|
+
getNavigationHistory() {
|
|
90
|
+
return [...this.navigationHistory];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if navigateReplace was called with specific path
|
|
94
|
+
* @param path - Path to check
|
|
95
|
+
* @returns True if navigateReplace was called with this path
|
|
96
|
+
*/
|
|
97
|
+
wasReplaceCalledWith(path) {
|
|
98
|
+
return this.replaceHistory.includes(path);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get all navigateReplace calls
|
|
102
|
+
* @returns Array of paths passed to navigateReplace
|
|
103
|
+
*/
|
|
104
|
+
getReplaceHistory() {
|
|
105
|
+
return [...this.replaceHistory];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get all openPopup calls
|
|
109
|
+
* @returns Array of popup call parameters
|
|
110
|
+
*/
|
|
111
|
+
getPopupCalls() {
|
|
112
|
+
return [...this.popupCalls];
|
|
113
|
+
}
|
|
114
|
+
// ===== IRoutingService Implementation =====
|
|
115
|
+
getSearchParam(key) {
|
|
116
|
+
return this.params.get(key) || null;
|
|
117
|
+
}
|
|
118
|
+
getAllSearchParams() {
|
|
119
|
+
const urlParams = new URLSearchParams();
|
|
120
|
+
this.params.forEach((value, key) => {
|
|
121
|
+
urlParams.set(key, value);
|
|
122
|
+
});
|
|
123
|
+
return urlParams;
|
|
124
|
+
}
|
|
125
|
+
getCurrentPath() {
|
|
126
|
+
return this.currentPath;
|
|
127
|
+
}
|
|
128
|
+
navigateTo(url) {
|
|
129
|
+
this.navigationHistory.push(url);
|
|
130
|
+
// In real scenario, external URLs would do full page navigation
|
|
131
|
+
// In tests, we just track the call
|
|
132
|
+
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
133
|
+
console.log('[MockRouting] External navigation (tracked):', url);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.log('[MockRouting] Internal navigation (tracked):', url);
|
|
137
|
+
this.currentPath = url;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
navigateReplace(path) {
|
|
141
|
+
this.replaceHistory.push(path);
|
|
142
|
+
this.currentPath = path;
|
|
143
|
+
console.log('[MockRouting] Replace URL (tracked):', path);
|
|
144
|
+
}
|
|
145
|
+
openPopup(url, name, features) {
|
|
146
|
+
this.popupCalls.push({ url, name, features });
|
|
147
|
+
console.log('[MockRouting] Popup open (tracked):', { url, name });
|
|
148
|
+
// In tests, we don't actually open popups
|
|
149
|
+
// Return a mock Window object if needed for testing
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
exports.MockRoutingService = MockRoutingService;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Routing Service for Next.js App Router (v13+)
|
|
3
|
+
*
|
|
4
|
+
* This service handles both URL parameter reading and navigation for Next.js with App Router.
|
|
5
|
+
* Copy this file to your project and remove the `.example` extension.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This must be used in Client Components ('use client')
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* 'use client';
|
|
12
|
+
*
|
|
13
|
+
* import { NextAppRouterRoutingService } from './NextAppRouterRoutingService';
|
|
14
|
+
* import { useRouter, usePathname, useSearchParams } from 'next/navigation';
|
|
15
|
+
*
|
|
16
|
+
* const routingService = new NextAppRouterRoutingService();
|
|
17
|
+
*
|
|
18
|
+
* setupSocialLogin(x => {
|
|
19
|
+
* x.routingService = routingService;
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* export default function LoginPage() {
|
|
23
|
+
* const router = useRouter();
|
|
24
|
+
* const pathname = usePathname();
|
|
25
|
+
* const searchParams = useSearchParams();
|
|
26
|
+
*
|
|
27
|
+
* useEffect(() => {
|
|
28
|
+
* // Single initialization with all hooks
|
|
29
|
+
* routingService.initialize(router, pathname, searchParams);
|
|
30
|
+
* }, [router, pathname, searchParams]);
|
|
31
|
+
*
|
|
32
|
+
* return <div>Your login page</div>;
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
import type { IRoutingService } from './IRoutingService';
|
|
37
|
+
/**
|
|
38
|
+
* Routing Service for Next.js App Router (v13+)
|
|
39
|
+
* Uses useRouter, usePathname, and useSearchParams from next/navigation
|
|
40
|
+
*
|
|
41
|
+
* NOTE: Only works in Client Components ('use client' directive required)
|
|
42
|
+
*/
|
|
43
|
+
export declare class NextAppRouterRoutingService implements IRoutingService {
|
|
44
|
+
private router;
|
|
45
|
+
private pathname;
|
|
46
|
+
private searchParams;
|
|
47
|
+
/**
|
|
48
|
+
* Initialize with Next.js navigation hooks
|
|
49
|
+
* MUST be called inside a Client Component ('use client')
|
|
50
|
+
*
|
|
51
|
+
* @param router - The router from useRouter() hook
|
|
52
|
+
* @param pathname - The pathname from usePathname() hook
|
|
53
|
+
* @param searchParams - The searchParams from useSearchParams() hook
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* 'use client';
|
|
58
|
+
*
|
|
59
|
+
* const router = useRouter();
|
|
60
|
+
* const pathname = usePathname();
|
|
61
|
+
* const searchParams = useSearchParams();
|
|
62
|
+
*
|
|
63
|
+
* useEffect(() => {
|
|
64
|
+
* routingService.initialize(router, pathname, searchParams);
|
|
65
|
+
* }, [router, pathname, searchParams]);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
initialize(router: any, pathname: string, searchParams: URLSearchParams | null): void;
|
|
69
|
+
getSearchParam(key: string): string | null;
|
|
70
|
+
getAllSearchParams(): URLSearchParams;
|
|
71
|
+
getCurrentPath(): string;
|
|
72
|
+
navigateTo(url: string): void;
|
|
73
|
+
navigateReplace(path: string): void;
|
|
74
|
+
openPopup(url: string, name: string, features: string): Window | null;
|
|
75
|
+
}
|