@salesforce/webapp-template-feature-react-authentication-experimental 1.109.5 → 1.109.7

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/dist/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.109.7](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.109.6...v1.109.7) (2026-03-19)
7
+
8
+ **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
9
+
10
+
11
+
12
+
13
+
14
+ ## [1.109.6](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.109.5...v1.109.6) (2026-03-19)
15
+
16
+ **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
17
+
18
+
19
+
20
+
21
+
6
22
  ## [1.109.5](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.109.4...v1.109.5) (2026-03-19)
7
23
 
8
24
  **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
@@ -15,8 +15,8 @@
15
15
  "graphql:schema": "node scripts/get-graphql-schema.mjs"
16
16
  },
17
17
  "dependencies": {
18
- "@salesforce/sdk-data": "^1.109.5",
19
- "@salesforce/webapp-experimental": "^1.109.5",
18
+ "@salesforce/sdk-data": "^1.109.7",
19
+ "@salesforce/webapp-experimental": "^1.109.7",
20
20
  "@tailwindcss/vite": "^4.1.17",
21
21
  "class-variance-authority": "^0.7.1",
22
22
  "clsx": "^2.1.1",
@@ -28,6 +28,7 @@
28
28
  "react-dom": "^19.2.0",
29
29
  "react-router": "^7.10.1",
30
30
  "shadcn": "^3.8.5",
31
+ "sonner": "^1.7.0",
31
32
  "tailwind-merge": "^3.5.0",
32
33
  "tailwindcss": "^4.1.17",
33
34
  "tw-animate-css": "^1.4.0",
@@ -42,7 +43,7 @@
42
43
  "@graphql-eslint/eslint-plugin": "^4.1.0",
43
44
  "@graphql-tools/utils": "^11.0.0",
44
45
  "@playwright/test": "^1.49.0",
45
- "@salesforce/vite-plugin-webapp-experimental": "^1.109.5",
46
+ "@salesforce/vite-plugin-webapp-experimental": "^1.109.7",
46
47
  "@testing-library/jest-dom": "^6.6.3",
47
48
  "@testing-library/react": "^16.1.0",
48
49
  "@testing-library/user-event": "^14.5.2",
@@ -0,0 +1,20 @@
1
+ import { Toaster as Sonner } from 'sonner';
2
+
3
+ /**
4
+ * Renders the toast container. Use with `toast()` from this module for messages
5
+ * with title, description, actions, and variants (success, error, warning).
6
+ *
7
+ * @example
8
+ * toast("Event has been created", {
9
+ * description: "Sunday, December 03, 2023 at 9:00 AM",
10
+ * action: { label: "Undo", onClick: () => {} },
11
+ * });
12
+ * toast.success("Saved!");
13
+ * toast.error("Something went wrong");
14
+ * toast.warning("Please review");
15
+ */
16
+ export function Toaster() {
17
+ return <Sonner position="top-right" richColors />;
18
+ }
19
+
20
+ export { toast } from 'sonner';
@@ -253,19 +253,14 @@ function useSessionTimeout(config: SessionTimeoutConfig): SessionTimeoutResult {
253
253
  isPollingRef.current = true;
254
254
  setIsPolling(true);
255
255
 
256
- try {
257
- const response = await pollSessionTimeServlet(basePath);
256
+ const response = await pollSessionTimeServlet(basePath);
257
+ isPollingRef.current = false;
258
+ setIsPolling(false);
258
259
 
259
- // Success - reset retry state and process response
260
- isPollingRef.current = false;
261
- setIsPolling(false);
260
+ if (response) {
262
261
  retry.resetRetry();
263
262
  processTimeoutResponse(response.sr);
264
- } catch (error) {
265
- console.error("[useSessionTimeout] Poll failed:", error);
266
- // Reset polling flags before retry so handleRetryWithBackoff doesn't skip
267
- isPollingRef.current = false;
268
- setIsPolling(false);
263
+ } else {
269
264
  handleRetryWithBackoff(() => checkSessionRef.current?.());
270
265
  }
271
266
  }, [basePath, retry, processTimeoutResponse, handleRetryWithBackoff]);
@@ -274,15 +269,12 @@ function useSessionTimeout(config: SessionTimeoutConfig): SessionTimeoutResult {
274
269
  * Extend the session (called when user clicks "Continue Working")
275
270
  */
276
271
  const extendSession = useCallback(async () => {
277
- try {
278
- const response = await extendSessionTime(basePath);
272
+ const response = await extendSessionTime(basePath);
279
273
 
280
- // Reset retry state and process the new session time
274
+ if (response) {
281
275
  retry.resetRetry();
282
276
  processTimeoutResponse(response.sr);
283
- } catch (error) {
284
- console.error("[useSessionTimeout] Failed to extend session:", error);
285
- // On failure, retry extending session (not checkSession)
277
+ } else {
286
278
  handleRetryWithBackoff(() => extendSessionRef.current?.());
287
279
  }
288
280
  }, [basePath, retry, processTimeoutResponse, handleRetryWithBackoff]);
@@ -20,10 +20,9 @@ export interface SessionResponse {
20
20
  * Handles CSRF protection prefix
21
21
  *
22
22
  * @param text - Raw response text from servlet
23
- * @returns Parsed session response
24
- * @throws Error if response cannot be parsed
23
+ * @returns Parsed session response, or undefined if parsing fails
25
24
  */
26
- function parseResponseResult(text: string): SessionResponse {
25
+ function parseResponseResult(text: string): SessionResponse | undefined {
27
26
  let cleanedText = text;
28
27
 
29
28
  // Strip CSRF protection prefix if present
@@ -45,25 +44,23 @@ function parseResponseResult(text: string): SessionResponse {
45
44
  return parsed;
46
45
  } catch (error) {
47
46
  console.error("[sessionTimeService] Failed to parse response:", error, "Text:", cleanedText);
48
- throw new Error(
49
- `Failed to parse session response: ${error instanceof Error ? error.message : "Unknown error"}`,
50
- );
51
47
  }
52
48
  }
53
49
 
54
50
  /**
55
51
  * Call SessionTimeServlet API
56
- * Internal function used by both poll and extend functions
52
+ * Internal function used by both poll and extend functions.
53
+ * Returns undefined on any failure so the session timeout feature
54
+ * never crashes the running application.
57
55
  *
58
56
  * @param basePath - Community base path (e.g., "/sfsites/c/")
59
57
  * @param extend - Whether to extend the session (updateTimedOutSession param)
60
- * @returns Session response with remaining time
61
- * @throws Error if API call fails or security checks fail
58
+ * @returns Session response with remaining time, or undefined on failure
62
59
  */
63
60
  async function callSessionTimeServlet(
64
61
  basePath: string,
65
62
  extend: boolean = false,
66
- ): Promise<SessionResponse> {
63
+ ): Promise<SessionResponse | undefined> {
67
64
  // Build URL with cache-busting timestamp
68
65
  const timestamp = Date.now();
69
66
  let url = `${basePath}${SESSION_CONFIG.SERVLET_URL}?buster=${timestamp}`;
@@ -75,47 +72,37 @@ async function callSessionTimeServlet(
75
72
  try {
76
73
  const response = await fetch(url, {
77
74
  method: "GET",
78
- credentials: "same-origin", // Include cookies for session
75
+ credentials: "same-origin",
79
76
  cache: "no-cache",
80
- // Security headers
81
77
  headers: {
82
- "X-Requested-With": "XMLHttpRequest", // Helps identify XHR requests
78
+ "X-Requested-With": "XMLHttpRequest",
83
79
  },
84
80
  });
85
81
 
86
82
  if (!response.ok) {
87
- // Provide more context for common error codes
88
- if (response.status === 401) {
89
- throw new Error("Session expired or unauthorized");
90
- } else if (response.status === 403) {
91
- throw new Error("Access forbidden");
92
- } else if (response.status === 404) {
93
- throw new Error("Session endpoint not found");
94
- } else {
95
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
96
- }
83
+ console.error(`[sessionTimeService] HTTP ${response.status}: ${response.statusText}`);
84
+ return undefined;
97
85
  }
98
86
 
99
- // Security: Validate content type (should be text or JSON)
100
87
  const contentType = response.headers.get("content-type");
101
88
  if (contentType && !contentType.includes("text") && !contentType.includes("json")) {
102
- throw new Error(`Unexpected content type: ${contentType}`);
89
+ console.error(`[sessionTimeService] Unexpected content type: ${contentType}`);
90
+ return undefined;
103
91
  }
104
92
 
105
93
  const text = await response.text();
106
94
  const parsed = parseResponseResult(text);
107
-
108
- // Apply latency buffer to account for network delay
109
- const adjustedSecondsRemaining = Math.max(0, parsed.sr - SESSION_CONFIG.LATENCY_BUFFER_SECONDS);
95
+ if (!parsed) {
96
+ return undefined;
97
+ }
110
98
 
111
99
  return {
112
100
  sp: parsed.sp,
113
- sr: adjustedSecondsRemaining,
101
+ sr: Math.max(0, parsed.sr - SESSION_CONFIG.LATENCY_BUFFER_SECONDS),
114
102
  };
115
103
  } catch (error) {
116
- // Don't log the full URL in production to avoid leaking sensitive info
117
104
  console.error("[sessionTimeService] API call failed:", error);
118
- throw error;
105
+ return undefined;
119
106
  }
120
107
  }
121
108
 
@@ -124,17 +111,17 @@ async function callSessionTimeServlet(
124
111
  * Called periodically to monitor session status
125
112
  *
126
113
  * @param basePath - Community base path (e.g., "/sfsites/c/")
127
- * @returns Session response with remaining time (after latency buffer adjustment)
128
- * @throws Error if API call fails
114
+ * @returns Session response with remaining time, or undefined on failure
129
115
  *
130
116
  * @example
131
- * const { sr, sp } = await pollSessionTimeServlet('/sfsites/c/');
132
- * if (sr <= 300) {
133
- * // Less than 5 minutes remaining
117
+ * const response = await pollSessionTimeServlet('/sfsites/c/');
118
+ * if (response && response.sr <= 300) {
134
119
  * showWarning();
135
120
  * }
136
121
  */
137
- export async function pollSessionTimeServlet(basePath: string): Promise<SessionResponse> {
122
+ export async function pollSessionTimeServlet(
123
+ basePath: string,
124
+ ): Promise<SessionResponse | undefined> {
138
125
  return callSessionTimeServlet(basePath, false);
139
126
  }
140
127
 
@@ -143,14 +130,15 @@ export async function pollSessionTimeServlet(basePath: string): Promise<SessionR
143
130
  * Called when user clicks "Continue Working" in warning modal
144
131
  *
145
132
  * @param basePath - Community base path (e.g., "/sfsites/c/")
146
- * @returns Session response with new remaining time
147
- * @throws Error if API call fails
133
+ * @returns Session response with new remaining time, or undefined on failure
148
134
  *
149
135
  * @example
150
- * const { sr } = await extendSessionTime('/sfsites/c/');
151
- * console.log(`Session extended. ${sr} seconds remaining.`);
136
+ * const response = await extendSessionTime('/sfsites/c/');
137
+ * if (response) {
138
+ * console.log(`Session extended. ${response.sr} seconds remaining.`);
139
+ * }
152
140
  */
153
- export async function extendSessionTime(basePath: string): Promise<SessionResponse> {
141
+ export async function extendSessionTime(basePath: string): Promise<SessionResponse | undefined> {
154
142
  return callSessionTimeServlet(basePath, true);
155
143
  }
156
144
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-base-sfdx-project-experimental",
3
- "version": "1.109.5",
3
+ "version": "1.109.7",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@salesforce/webapp-template-base-sfdx-project-experimental",
9
- "version": "1.109.5",
9
+ "version": "1.109.7",
10
10
  "license": "SEE LICENSE IN LICENSE.txt",
11
11
  "devDependencies": {
12
12
  "@lwc/eslint-plugin-lwc": "^3.3.0",
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-base-sfdx-project-experimental",
3
- "version": "1.109.5",
3
+ "version": "1.109.7",
4
4
  "description": "Base SFDX project template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "publishConfig": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-feature-react-authentication-experimental",
3
- "version": "1.109.5",
3
+ "version": "1.109.7",
4
4
  "description": "Authentication feature for web applications",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "author": "",
@@ -16,7 +16,7 @@
16
16
  "clean": "rm -rf dist"
17
17
  },
18
18
  "devDependencies": {
19
- "@salesforce/webapp-experimental": "^1.109.5",
19
+ "@salesforce/webapp-experimental": "^1.109.7",
20
20
  "@tanstack/react-form": "^1.27.7",
21
21
  "@types/react": "^19.2.7",
22
22
  "@types/react-dom": "^19.2.3",