@salesforce/webapp-experimental 1.79.0 → 1.79.1

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.
@@ -5,5 +5,4 @@
5
5
  */
6
6
  export type { ProxyOptions, ProxyHandler } from "./handler.js";
7
7
  export { createProxyHandler, injectLivePreviewScript, WEBAPP_HEALTH_CHECK_PARAM, WEBAPP_PROXY_HEADER, } from "./handler.js";
8
- export { getErrorPageTemplate } from "./error-page.js";
9
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/proxy/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EACN,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/proxy/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EACN,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,GACnB,MAAM,cAAc,CAAC"}
@@ -4,4 +4,3 @@
4
4
  * For full license text, see the LICENSE.txt file
5
5
  */
6
6
  export { createProxyHandler, injectLivePreviewScript, WEBAPP_HEALTH_CHECK_PARAM, WEBAPP_PROXY_HEADER, } from "./handler.js";
7
- export { getErrorPageTemplate } from "./error-page.js";
@@ -13,7 +13,6 @@
13
13
  * - Runtime / compile / HMR error listeners
14
14
  * - Error deduplication
15
15
  * - postMessage bridge to the VS Code webview (when running inside an iframe)
16
- * - Standalone browser error overlay (when NOT inside an iframe)
17
16
  * - Copy / paste / right-click bridge for VS Code webview
18
17
  */
19
18
  export declare function getLivePreviewScriptContent(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"livePreviewScript.d.ts","sourceRoot":"","sources":["../../src/proxy/livePreviewScript.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAKpD;AAED,oEAAoE;AACpE,eAAO,MAAM,0BAA0B,sBAAsB,CAAC"}
1
+ {"version":3,"file":"livePreviewScript.d.ts","sourceRoot":"","sources":["../../src/proxy/livePreviewScript.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAKpD;AAED,oEAAoE;AACpE,eAAO,MAAM,0BAA0B,sBAAsB,CAAC"}
@@ -18,7 +18,6 @@ let cached = null;
18
18
  * - Runtime / compile / HMR error listeners
19
19
  * - Error deduplication
20
20
  * - postMessage bridge to the VS Code webview (when running inside an iframe)
21
- * - Standalone browser error overlay (when NOT inside an iframe)
22
21
  * - Copy / paste / right-click bridge for VS Code webview
23
22
  */
24
23
  export function getLivePreviewScriptContent() {
@@ -28,11 +28,6 @@ describe("getLivePreviewScriptContent", () => {
28
28
  const content = getLivePreviewScriptContent();
29
29
  expect(content).toContain("sendErrorToParent");
30
30
  });
31
- it("should contain the error overlay function", () => {
32
- const content = getLivePreviewScriptContent();
33
- expect(content).toContain("showErrorOverlayInPage");
34
- expect(content).toContain("vscode-error-panel");
35
- });
36
31
  it("should contain the copy/paste bridge", () => {
37
32
  const content = getLivePreviewScriptContent();
38
33
  expect(content).toContain("sendCopyMessage");
@@ -154,189 +154,6 @@
154
154
  );
155
155
  }
156
156
 
157
- function escapeHtml(text) {
158
- if (!text) return "";
159
- const div = document.createElement("div");
160
- div.textContent = text;
161
- return div.innerHTML;
162
- }
163
-
164
- // Extracts filename from error data. Mirrors logic in errorPathUtils.ts (extension)
165
- // and uiPreviewWebview.js (webview) — keep patterns in sync across all three.
166
- function extractFileName(errorData) {
167
- const combined =
168
- (errorData.message || "") + " " + (errorData.source || "") + " " + (errorData.stack || "");
169
- const patterns = [
170
- /Failed to reload\s+\/?@?fs?\/?([^\s:?)]+\.[jt]sx?)/,
171
- /@fs\/([^\s:?)]+\.[jt]sx?)/,
172
- /\/(src\/[^\s:?)]+\.[jt]sx?)/,
173
- /\/([^\s\/:?)]+\.[jt]sx?)[\s:?]/,
174
- /at\s+\w+\s+\([^)]*\/([^\s\/:?)]+\.[jt]sx?)/,
175
- ];
176
- for (let i = 0; i < patterns.length; i++) {
177
- const m = combined.match(patterns[i]);
178
- if (m && m[1]) {
179
- const parts = m[1].split("/");
180
- return parts[parts.length - 1];
181
- }
182
- }
183
- return null;
184
- }
185
-
186
- function getFileIconLabel(name) {
187
- if (!name) return "JS";
188
- const ext = name.split(".").pop().toLowerCase();
189
- if (ext === "ts" || ext === "tsx") return "TS";
190
- if (ext === "jsx") return "JSX";
191
- return "JS";
192
- }
193
-
194
- // SVG illustration for error overlay (Figma design asset)
195
- const ERROR_OVERLAY_SVG =
196
- '<svg width="300" height="192" viewBox="0 0 300 192" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M104.698 144.786C104.458 144.138 103.542 144.138 103.302 144.786L101.757 148.962C101.621 149.33 101.33 149.621 100.962 149.757L96.7861 151.302C96.1381 151.542 96.1381 152.458 96.7861 152.698L100.962 154.243C101.33 154.379 101.621 154.67 101.757 155.038L103.302 159.214C103.542 159.862 104.458 159.862 104.698 159.214L106.243 155.038C106.379 154.67 106.67 154.379 107.038 154.243L111.214 152.698C111.862 152.458 111.862 151.542 111.214 151.302L107.038 149.757C106.67 149.621 106.379 149.33 106.243 148.962L104.698 144.786Z" stroke="#757575" stroke-width="0.6"/><path d="M57.265 44.5117C57.5175 43.8294 58.4825 43.8294 58.735 44.5117L59.8937 47.6433C59.9731 47.8578 60.1422 48.0269 60.3567 48.1063L63.4883 49.265C64.1706 49.5175 64.1706 50.4825 63.4883 50.735L60.3567 51.8937C60.1422 51.9731 59.9731 52.1422 59.8937 52.3567L58.735 55.4883C58.4825 56.1706 57.5175 56.1706 57.265 55.4883L56.1063 52.3567C56.0269 52.1422 55.8578 51.9731 55.6433 51.8937L52.5117 50.735C51.8294 50.4825 51.8294 49.5175 52.5117 49.265L55.6433 48.1063C55.8578 48.0269 56.0269 47.8578 56.1063 47.6433L57.265 44.5117Z" fill="#AEAEAE"/><path d="M236.477 128.332C236.313 127.889 235.687 127.889 235.523 128.332L234.742 130.442C234.691 130.581 234.581 130.691 234.442 130.742L232.332 131.523C231.889 131.687 231.889 132.313 232.332 132.477L234.442 133.258C234.581 133.309 234.691 133.419 234.742 133.558L235.523 135.668C235.687 136.111 236.313 136.111 236.477 135.668L237.258 133.558C237.309 133.419 237.419 133.309 237.558 133.258L239.668 132.477C240.111 132.313 240.111 131.687 239.668 131.523L237.558 130.742C237.419 130.691 237.309 130.581 237.258 130.442L236.477 128.332Z" fill="#AEAEAE"/><rect x="160" y="106" width="2" height="16" transform="rotate(-90 160 106)" fill="#7CB1FE"/><rect x="160" y="88" width="2" height="16" transform="rotate(-90 160 88)" fill="#7CB1FE"/><path d="M56 96C56 89.3726 61.3726 84 68 84L75 84C75 90.6274 69.6274 96 63 96L56 96Z" fill="#066AFE"/><path d="M264 48C257.373 48 252 42.6274 252 36L252 29C258.627 29 264 34.3726 264 41L264 48Z" fill="#066AFE"/><path d="M56 96C56 91.5818 52.4183 88 48 88C48 92.4183 51.5817 96 56 96Z" fill="#7CB1FE"/><path d="M264 48C259.582 48 256 51.5817 256 56C260.418 56 264 52.4183 264 48Z" fill="#7CB1FE"/><path d="M40 152C53.2548 152 64 141.255 64 128C50.7452 128 40 138.745 40 152Z" fill="#066AFE"/><path d="M264 120C250.745 120 240 109.255 240 96C253.255 96 264 106.745 264 120Z" fill="#066AFE"/><path d="M88 96L60 96C48.9543 96 40 104.954 40 116L40 168" stroke="#7CB1FE"/><path d="M216 96H244C255.046 96 264 87.0457 264 76V24" stroke="#7CB1FE"/><path d="M128 64L128 128L112 128C98.7452 128 88 117.255 88 104L88 88C88 74.7452 98.7452 64 112 64L128 64Z" fill="#066AFE"/><path d="M176 128L176 64L192 64C205.255 64 216 74.7452 216 88L216 104C216 117.255 205.255 128 192 128L176 128Z" fill="url(#pg1)"/><defs><linearGradient id="pg1" x1="176" y1="67.5" x2="217.94" y2="86.14" gradientUnits="userSpaceOnUse"><stop stop-color="#7CB1FE"/><stop offset="1" stop-color="#066AFE"/></linearGradient></defs></svg>';
197
-
198
- const COPY_FEEDBACK_DELAY_MS = 1500;
199
-
200
- function injectOverlayStyles() {
201
- if (document.getElementById("vscode-error-overlay-styles")) return;
202
- const style = document.createElement("style");
203
- style.id = "vscode-error-overlay-styles";
204
- style.textContent = [
205
- '#vscode-error-panel{position:fixed;top:0;left:0;right:0;bottom:0;background:#181818;z-index:999999;overflow-y:auto;display:flex;flex-direction:column;justify-content:center;align-items:center;padding:0 0 30px;font-family:-apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",system-ui,sans-serif}',
206
- ".vep-content{display:flex;flex-direction:column;justify-content:center;align-items:center;gap:46px}",
207
- ".vep-top{display:flex;flex-direction:column;align-items:center;gap:16px}",
208
- ".vep-illustration{width:300px;height:192px}",
209
- ".vep-title{display:flex;align-items:center;gap:8px;padding-bottom:8px}",
210
- ".vep-title-icon{width:16px;height:16px;color:#FE8AA7;font-size:16px;display:flex;align-items:center;justify-content:center}",
211
- ".vep-title-text{font-weight:600;font-size:20px;line-height:26px;color:#FE8AA7}",
212
- ".vep-body{display:flex;flex-direction:column;align-items:center;gap:24px;width:416px}",
213
- ".vep-fields{display:flex;flex-direction:column;gap:16px;width:100%}",
214
- ".vep-field{display:flex;flex-direction:column;gap:8px;width:100%}",
215
- ".vep-label{font-weight:500;font-size:14px;line-height:17px;color:#FFFFFF}",
216
- ".vep-file-box{box-sizing:border-box;display:flex;align-items:center;padding:4px 8px;gap:4px;width:100%;background:#181818;border:1px solid #444444;border-radius:4px}",
217
- ".vep-file-icon{font-size:10px;font-weight:700;color:#F9E3B6;letter-spacing:0.3px}",
218
- ".vep-file-name{font-weight:500;font-size:13px;line-height:16px;color:#AEAEAE;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
219
- ".vep-error-box{box-sizing:border-box;display:flex;align-items:center;padding:8px;gap:4px;width:100%;background:#242424;border:1px solid #FE8AA7;border-radius:8px}",
220
- ".vep-error-icon{width:16px;height:16px;color:#FE8AA7;font-size:16px;flex-shrink:0;display:flex;align-items:center;justify-content:center}",
221
- ".vep-error-msg{font-weight:500;font-size:12px;line-height:14px;color:#FE8AA7;word-break:break-word}",
222
- ".vep-stack-box{box-sizing:border-box;padding:8px;width:100%;max-height:200px;overflow-y:auto;background:#181818;border:1px solid #444444;border-radius:4px;font-weight:500;font-size:12px;line-height:14px;color:#AEAEAE;white-space:pre-wrap;word-break:break-word;user-select:text}",
223
- ".vep-actions{display:flex;gap:8px;width:100%}",
224
- ".vep-btn{display:flex;align-items:center;gap:4px;padding:2px 4px;background:#066AFE;border-radius:4px;border:none;cursor:pointer;font-weight:500;font-size:11px;line-height:13px;color:#fff;white-space:nowrap}",
225
- ".vep-btn:hover{background:#0559d4}",
226
- ].join("\n");
227
- document.head.appendChild(style);
228
- }
229
-
230
- function showErrorOverlayInPage(errorData) {
231
- try {
232
- const existing = document.getElementById("vscode-error-panel");
233
- if (existing) existing.remove();
234
-
235
- injectOverlayStyles();
236
-
237
- const errorType = errorData.type || "runtime";
238
- const titleMap = {
239
- network: "Network Error",
240
- compile: "Compilation Error",
241
- hmr: "HMR Error",
242
- component: "Component Error",
243
- };
244
- const titleText = titleMap[errorType] || "Runtime Error";
245
- const fileName = extractFileName(errorData);
246
- const fileIcon = getFileIconLabel(fileName);
247
-
248
- const panel = document.createElement("div");
249
- panel.id = "vscode-error-panel";
250
-
251
- const fileHtml = fileName
252
- ? '<div class="vep-field"><div class="vep-label">File</div>' +
253
- '<div class="vep-file-box"><span class="vep-file-icon">' +
254
- escapeHtml(fileIcon) +
255
- "</span>" +
256
- '<span class="vep-file-name">' +
257
- escapeHtml(fileName) +
258
- "</span></div></div>"
259
- : "";
260
-
261
- const stackHtml =
262
- errorData.stack && errorData.stack.trim()
263
- ? '<div class="vep-field"><div class="vep-label">Stack Trace</div>' +
264
- '<div class="vep-stack-box">' +
265
- escapeHtml(errorData.stack) +
266
- "</div></div>"
267
- : "";
268
-
269
- panel.innerHTML =
270
- '<div class="vep-content">' +
271
- '<div class="vep-top">' +
272
- '<div class="vep-illustration">' +
273
- ERROR_OVERLAY_SVG +
274
- "</div>" +
275
- '<div class="vep-title"><span class="vep-title-icon">\u2297</span>' +
276
- '<span class="vep-title-text">' +
277
- escapeHtml(titleText) +
278
- "</span></div>" +
279
- "</div>" +
280
- '<div class="vep-body">' +
281
- '<div class="vep-fields">' +
282
- fileHtml +
283
- '<div class="vep-field"><div class="vep-label">Error</div>' +
284
- '<div class="vep-error-box"><span class="vep-error-icon">\u2297</span>' +
285
- '<span class="vep-error-msg">' +
286
- escapeHtml(errorData.message || "Unknown error") +
287
- "</span></div></div>" +
288
- stackHtml +
289
- "</div>" +
290
- '<div class="vep-actions">' +
291
- '<button class="vep-btn" id="vep-copy-btn">\u{1F4CB} Copy Error</button>' +
292
- '<button class="vep-btn" id="vep-retry-btn">\u21BB Retry</button>' +
293
- "</div>" +
294
- "</div>" +
295
- "</div>";
296
-
297
- document.body.appendChild(panel);
298
-
299
- document.getElementById("vep-copy-btn").onclick = function () {
300
- const parts = [titleText, ""];
301
- if (fileName) parts.push("File: " + fileName);
302
- parts.push("Error: " + (errorData.message || "Unknown error"));
303
- if (errorData.stack && errorData.stack.trim()) {
304
- parts.push("");
305
- parts.push("Stack Trace:");
306
- parts.push(errorData.stack);
307
- }
308
- const text = parts.join("\n");
309
- const btn = this;
310
- if (navigator.clipboard && navigator.clipboard.writeText) {
311
- navigator.clipboard
312
- .writeText(text)
313
- .then(function () {
314
- btn.innerHTML = "\u2714 Copied!";
315
- setTimeout(function () {
316
- btn.innerHTML = "\u{1F4CB} Copy Error";
317
- }, COPY_FEEDBACK_DELAY_MS);
318
- })
319
- .catch(function () {
320
- btn.innerHTML = "Copy failed";
321
- });
322
- }
323
- };
324
- document.getElementById("vep-retry-btn").onclick = function () {
325
- panel.remove();
326
- window.location.reload();
327
- };
328
-
329
- document.addEventListener("keydown", function onEsc(e) {
330
- if (e.key === "Escape") {
331
- panel.remove();
332
- document.removeEventListener("keydown", onEsc);
333
- }
334
- });
335
- } catch (e) {
336
- console.error("[webapps-proxy] showErrorOverlayInPage failed:", e);
337
- }
338
- }
339
-
340
157
  function sendErrorToParent(errorData) {
341
158
  const errorHash = getErrorHash(errorData);
342
159
  const now = Date.now();
@@ -380,8 +197,6 @@
380
197
  } catch (err) {
381
198
  console.error("[webapps-proxy] Failed to send error to parent:", err);
382
199
  }
383
- } else {
384
- showErrorOverlayInPage(errorData);
385
200
  }
386
201
  }
387
202
  // Expose for fetch interceptor: once init runs, network errors use sendErrorToParent (dedup)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-experimental",
3
3
  "description": "[experimental] Core package for Salesforce Web Applications",
4
- "version": "1.79.0",
4
+ "version": "1.79.1",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@salesforce/core": "^8.23.4",
48
- "@salesforce/sdk-data": "^1.79.0",
48
+ "@salesforce/sdk-data": "^1.79.1",
49
49
  "axios": "^1.7.7",
50
50
  "micromatch": "^4.0.8",
51
51
  "path-to-regexp": "^8.3.0"
@@ -1,11 +0,0 @@
1
- /**
2
- * Copyright (c) 2026, Salesforce, Inc.,
3
- * All rights reserved.
4
- * For full license text, see the LICENSE.txt file
5
- */
6
- /**
7
- * Returns the error page HTML template. Reads from dist/proxy/templates/ at runtime (after postbuild copy).
8
- * Cached after first read.
9
- */
10
- export declare function getErrorPageTemplate(): string;
11
- //# sourceMappingURL=error-page.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-page.d.ts","sourceRoot":"","sources":["../../src/proxy/error-page.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAK7C"}
@@ -1,21 +0,0 @@
1
- /**
2
- * Copyright (c) 2026, Salesforce, Inc.,
3
- * All rights reserved.
4
- * For full license text, see the LICENSE.txt file
5
- */
6
- import { readFileSync } from "node:fs";
7
- import { dirname, join } from "node:path";
8
- import { fileURLToPath } from "node:url";
9
- const __dirname = dirname(fileURLToPath(import.meta.url));
10
- let cached = null;
11
- /**
12
- * Returns the error page HTML template. Reads from dist/proxy/templates/ at runtime (after postbuild copy).
13
- * Cached after first read.
14
- */
15
- export function getErrorPageTemplate() {
16
- if (cached)
17
- return cached;
18
- const templatePath = join(__dirname, "templates", "error-page.html");
19
- cached = readFileSync(templatePath, "utf-8");
20
- return cached;
21
- }
@@ -1,748 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>{{PAGE_TITLE}} - Salesforce Local Dev Proxy</title>
7
- {{META_REFRESH}}
8
- <style>
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- body {
16
- font-family:
17
- -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
18
- background: linear-gradient(135deg, #1e2a47 0%, #2d3e5f 100%);
19
- color: #333;
20
- min-height: 100vh;
21
- padding: 20px;
22
- }
23
-
24
- .main-container {
25
- max-width: 1400px;
26
- margin: 0 auto;
27
- background: #e8e8e8;
28
- border-radius: 16px;
29
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
30
- overflow: hidden;
31
- }
32
-
33
- /* ============================================
34
- TOP HEADER
35
- ============================================ */
36
- .top-header {
37
- background: #e8e8e8;
38
- padding: 30px 40px;
39
- display: flex;
40
- justify-content: space-between;
41
- align-items: flex-start;
42
- border-bottom: 1px solid #ccc;
43
- }
44
-
45
- .header-left h1 {
46
- color: #1a1a1a;
47
- font-size: 2.2em;
48
- font-weight: 600;
49
- margin-bottom: 8px;
50
- }
51
-
52
- .header-left .subtitle {
53
- color: #666;
54
- font-size: 1.05em;
55
- }
56
-
57
- .status-badge {
58
- padding: 12px 24px;
59
- border-radius: 8px;
60
- font-weight: 600;
61
- font-size: 0.95em;
62
- }
63
-
64
- .status-badge.critical {
65
- background: #ffcdd2;
66
- color: #b71c1c;
67
- }
68
-
69
- .status-badge.error {
70
- background: #ffebee;
71
- color: #c62828;
72
- }
73
-
74
- .status-badge.warning {
75
- background: #fff3e0;
76
- color: #e65100;
77
- }
78
-
79
- .status-badge.success {
80
- background: #e8f5e9;
81
- color: #2e7d32;
82
- }
83
-
84
- /* ============================================
85
- TWO-COLUMN LAYOUT
86
- ============================================ */
87
- .content-wrapper {
88
- display: grid;
89
- grid-template-columns: 1fr 308px;
90
- gap: 0;
91
- background: #e8e8e8;
92
- }
93
-
94
- .main-content {
95
- padding: 40px;
96
- background: white;
97
- border-right: 1px solid #ccc;
98
- }
99
-
100
- .diagnostics-panel {
101
- padding: 40px 20px;
102
- background: #f5f5f5;
103
- }
104
-
105
- /* ============================================
106
- MAIN CONTENT SECTIONS
107
- ============================================ */
108
- .content-section {
109
- margin-bottom: 30px;
110
- }
111
-
112
- .content-section h2 {
113
- color: #1a1a1a;
114
- font-size: 1.4em;
115
- margin-bottom: 15px;
116
- font-weight: 600;
117
- }
118
-
119
- .content-section h3 {
120
- color: #1a1a1a;
121
- font-size: 1.1em;
122
- margin-bottom: 12px;
123
- font-weight: 600;
124
- }
125
-
126
- .content-section p {
127
- color: #555;
128
- line-height: 1.6;
129
- margin-bottom: 15px;
130
- }
131
-
132
- .content-section ul {
133
- list-style: disc;
134
- padding-left: 25px;
135
- color: #555;
136
- line-height: 1.8;
137
- }
138
-
139
- /* Message box for errors */
140
- .message-box {
141
- background: #fff3e0;
142
- border-left: 4px solid #ff9800;
143
- padding: 20px;
144
- border-radius: 6px;
145
- margin-bottom: 25px;
146
- }
147
-
148
- .message-box p {
149
- color: #555;
150
- margin: 0;
151
- }
152
-
153
- /* Stack trace and code outputs */
154
- .code-output {
155
- background: #1e1e1e;
156
- border-radius: 8px;
157
- padding: 20px;
158
- max-height: 400px;
159
- overflow-y: auto;
160
- font-family: "Courier New", Consolas, Monaco, monospace;
161
- font-size: 0.9em;
162
- line-height: 1.5;
163
- margin: 20px 0;
164
- }
165
-
166
- .code-output pre {
167
- color: #ff6b6b;
168
- margin: 0;
169
- white-space: pre-wrap;
170
- word-wrap: break-word;
171
- }
172
-
173
- .stack-trace-container {
174
- background: #263238;
175
- border-radius: 8px;
176
- padding: 20px;
177
- max-height: 350px;
178
- overflow-y: auto;
179
- font-family: "Courier New", Consolas, Monaco, monospace;
180
- font-size: 0.9em;
181
- line-height: 1.5;
182
- }
183
-
184
- /* Suggestions box */
185
- .suggestions-box {
186
- background: #e3f2fd;
187
- border-left: 4px solid #2196f3;
188
- padding: 20px;
189
- border-radius: 6px;
190
- margin: 20px 0;
191
- }
192
-
193
- .suggestions-box h3 {
194
- color: #1a1a1a;
195
- font-size: 1.1em;
196
- margin-bottom: 15px;
197
- }
198
-
199
- .suggestions-box ul {
200
- list-style: none;
201
- padding: 0;
202
- }
203
-
204
- .suggestions-box li {
205
- padding: 8px 0;
206
- color: #555;
207
- line-height: 1.6;
208
- }
209
-
210
- .suggestions-box li::before {
211
- content: "💡";
212
- margin-right: 10px;
213
- }
214
-
215
- /* ============================================
216
- DIAGNOSTICS PANEL
217
- ============================================ */
218
- .diagnostics-panel h2 {
219
- color: #1a1a1a;
220
- font-size: 1.15em;
221
- margin-bottom: 18px;
222
- font-weight: 600;
223
- }
224
-
225
- .diagnostics-list {
226
- list-style: none;
227
- padding: 0;
228
- margin-bottom: 20px;
229
- }
230
-
231
- .diagnostics-list li {
232
- margin-bottom: 12px;
233
- color: #555;
234
- line-height: 1.4;
235
- font-size: 0.9em;
236
- }
237
-
238
- .diagnostics-list .label {
239
- display: block;
240
- font-weight: 600;
241
- color: #1a1a1a;
242
- margin-bottom: 3px;
243
- font-size: 0.85em;
244
- }
245
-
246
- .diagnostics-list .value {
247
- font-family: "Courier New", monospace;
248
- font-size: 0.85em;
249
- color: #666;
250
- word-break: break-word;
251
- }
252
-
253
- .diagnostics-list .value code {
254
- background: #263238;
255
- color: #aed581;
256
- padding: 2px 5px;
257
- border-radius: 3px;
258
- font-size: 0.8em;
259
- }
260
-
261
- /* Live logs section */
262
- .live-logs {
263
- background: #1e1e1e;
264
- border-radius: 4px;
265
- padding: 12px;
266
- margin-top: 18px;
267
- font-family: "Courier New", monospace;
268
- font-size: 0.75em;
269
- color: #90a4ae;
270
- max-height: 200px;
271
- overflow-y: auto;
272
- line-height: 1.6;
273
- }
274
-
275
- .live-logs .log-line {
276
- margin-bottom: 3px;
277
- white-space: nowrap;
278
- overflow: hidden;
279
- text-overflow: ellipsis;
280
- }
281
-
282
- .live-logs .timestamp {
283
- color: #78909c;
284
- font-size: 0.9em;
285
- }
286
-
287
- .live-logs .log-content {
288
- color: #b0bec5;
289
- }
290
-
291
- /* Emergency exit commands */
292
- .emergency-commands {
293
- background: #fff3cd;
294
- border: 1px solid #ffc107;
295
- border-radius: 4px;
296
- padding: 15px;
297
- margin-top: 20px;
298
- }
299
-
300
- .emergency-commands h3 {
301
- color: #856404;
302
- font-size: 0.8em;
303
- margin: 0 0 8px 0;
304
- font-weight: 600;
305
- }
306
-
307
- .emergency-commands p {
308
- color: #856404;
309
- font-size: 0.75em;
310
- margin: 0 0 8px 0;
311
- }
312
-
313
- .command-item {
314
- margin-bottom: 10px;
315
- }
316
-
317
- .command-item:last-child {
318
- margin-bottom: 0;
319
- }
320
-
321
- .command-comment {
322
- color: #856404;
323
- font-size: 0.7em;
324
- margin-bottom: 3px;
325
- font-style: italic;
326
- }
327
-
328
- .command-wrapper {
329
- position: relative;
330
- }
331
-
332
- .command-box {
333
- background: #263238;
334
- color: #aed581;
335
- padding: 10px 44px 10px 10px;
336
- border-radius: 4px;
337
- font-family: "Courier New", monospace;
338
- font-size: 0.72em;
339
- word-break: break-all;
340
- position: relative;
341
- min-height: 36px;
342
- display: flex;
343
- align-items: center;
344
- }
345
-
346
- .copy-button {
347
- position: absolute;
348
- right: 8px;
349
- top: 50%;
350
- transform: translateY(-50%);
351
- background: #4caf50;
352
- color: white;
353
- border: none;
354
- border-radius: 3px;
355
- padding: 6px;
356
- width: 28px;
357
- height: 28px;
358
- cursor: pointer;
359
- transition: all 0.2s;
360
- display: flex;
361
- align-items: center;
362
- justify-content: center;
363
- flex-shrink: 0;
364
- }
365
-
366
- .copy-button:hover {
367
- background: #45a049;
368
- transform: translateY(-50%) scale(1.1);
369
- }
370
-
371
- .copy-button.copied {
372
- background: #2196f3;
373
- }
374
-
375
- .copy-button svg {
376
- width: 16px;
377
- height: 16px;
378
- fill: white;
379
- }
380
-
381
- /* ============================================
382
- BOTTOM FOOTER
383
- ============================================ */
384
- .footer-section {
385
- background: #e8e8e8;
386
- padding: 20px 40px;
387
- border-top: 1px solid #ccc;
388
- text-align: center;
389
- }
390
-
391
- .help-text {
392
- color: #666;
393
- font-size: 0.9em;
394
- line-height: 1.5;
395
- }
396
-
397
- .help-text strong {
398
- color: #333;
399
- }
400
-
401
- /* ============================================
402
- UTILITY CLASSES
403
- ============================================ */
404
- .hidden {
405
- display: none;
406
- }
407
-
408
- .code {
409
- background: #263238;
410
- color: #aed581;
411
- padding: 3px 8px;
412
- border-radius: 4px;
413
- font-family: "Courier New", Consolas, Monaco, monospace;
414
- font-size: 0.9em;
415
- }
416
-
417
- .auto-refresh-indicator {
418
- background: #e8f5e9;
419
- border-left: 4px solid #4caf50;
420
- padding: 12px 16px;
421
- border-radius: 6px;
422
- margin: 20px 0;
423
- color: #2e7d32;
424
- font-size: 0.9em;
425
- }
426
-
427
- .auto-refresh-indicator::before {
428
- content: "🔄";
429
- margin-right: 8px;
430
- }
431
-
432
- /* Error metadata cards */
433
- .metadata-grid {
434
- display: grid;
435
- grid-template-columns: repeat(2, 1fr);
436
- gap: 15px;
437
- margin: 20px 0;
438
- }
439
-
440
- .metadata-card {
441
- background: #f9f9f9;
442
- padding: 15px;
443
- border-radius: 6px;
444
- border-left: 3px solid #2196f3;
445
- }
446
-
447
- .metadata-card .label {
448
- font-weight: 600;
449
- color: #1a1a1a;
450
- font-size: 0.85em;
451
- margin-bottom: 6px;
452
- }
453
-
454
- .metadata-card .value {
455
- color: #666;
456
- font-family: monospace;
457
- font-size: 0.9em;
458
- }
459
-
460
- /* ============================================
461
- SCROLLBARS
462
- ============================================ */
463
- .code-output::-webkit-scrollbar,
464
- .stack-trace-container::-webkit-scrollbar,
465
- .live-logs::-webkit-scrollbar {
466
- width: 8px;
467
- }
468
-
469
- .code-output::-webkit-scrollbar-track,
470
- .stack-trace-container::-webkit-scrollbar-track,
471
- .live-logs::-webkit-scrollbar-track {
472
- background: #1a1f2e;
473
- border-radius: 4px;
474
- }
475
-
476
- .code-output::-webkit-scrollbar-thumb,
477
- .stack-trace-container::-webkit-scrollbar-thumb,
478
- .live-logs::-webkit-scrollbar-thumb {
479
- background: #4a5568;
480
- border-radius: 4px;
481
- }
482
-
483
- /* ============================================
484
- RESPONSIVE DESIGN
485
- ============================================ */
486
- @media (max-width: 900px) {
487
- .content-wrapper {
488
- grid-template-columns: 1fr;
489
- }
490
-
491
- .main-content {
492
- border-right: none;
493
- border-bottom: 1px solid #ccc;
494
- }
495
-
496
- .diagnostics-panel {
497
- padding: 30px 20px;
498
- }
499
-
500
- .metadata-grid {
501
- grid-template-columns: 1fr;
502
- }
503
- }
504
-
505
- @media (max-width: 768px) {
506
- .top-header {
507
- flex-direction: column;
508
- gap: 15px;
509
- padding: 20px;
510
- }
511
-
512
- .main-content {
513
- padding: 25px;
514
- }
515
- }
516
- </style>
517
- </head>
518
-
519
- <body>
520
- <div class="main-container">
521
- <!-- Top Header -->
522
- <div class="top-header">
523
- <div class="header-left">
524
- <h1>Local Dev Proxy</h1>
525
- <p class="subtitle">Salesforce preview → Proxy → Your dev server</p>
526
- </div>
527
- <div class="status-badge {{STATUS_CLASS}}">{{ERROR_STATUS}}</div>
528
- </div>
529
-
530
- <!-- Two-Column Content -->
531
- <div class="content-wrapper">
532
- <!-- Main Content (Left) -->
533
- <div class="main-content">
534
- <!-- SECTION 1: Simple Error (Dev Server Down) -->
535
- <div id="simple-error-section" class="{{SIMPLE_SECTION_CLASS}}">
536
- <div class="content-section">
537
- <h2>{{ERROR_TITLE}}</h2>
538
- {{MESSAGE_CONTENT}}
539
- </div>
540
-
541
- <div class="auto-refresh-indicator {{AUTO_REFRESH_CLASS}}">{{AUTO_REFRESH_TEXT}}</div>
542
-
543
- <div class="suggestions-box">
544
- <h3>What to do next</h3>
545
- <ul>
546
- <li>
547
- Start your dev server using: <code>npm run start</code> or <code>npm start</code>
548
- </li>
549
- <li>Verify your dev server is running on the correct port</li>
550
- <li>Check webapplication.json for the correct dev server URL</li>
551
- <li>This page will auto-refresh when the server is detected</li>
552
- </ul>
553
- </div>
554
- </div>
555
-
556
- <!-- SECTION 2: Runtime Error -->
557
- <div id="runtime-error-section" class="{{RUNTIME_SECTION_CLASS}}">
558
- <div class="content-section">
559
- <h2>⚠️ Runtime Error: {{ERROR_TYPE}}</h2>
560
- <div class="message-box">
561
- <p>{{ERROR_MESSAGE_TEXT}}</p>
562
- </div>
563
- </div>
564
-
565
- <div class="content-section">
566
- <h3>Stack Trace</h3>
567
- <div class="stack-trace-container">{{FORMATTED_STACK_HTML}}</div>
568
- </div>
569
-
570
- <div class="metadata-grid">
571
- <div class="metadata-card">
572
- <div class="label">Error Type</div>
573
- <div class="value">{{ERROR_TYPE}}{{ERROR_CODE_BADGE}}</div>
574
- </div>
575
- <div class="metadata-card">
576
- <div class="label">Severity</div>
577
- <div class="value">{{SEVERITY_LABEL}}</div>
578
- </div>
579
- <div class="metadata-card">
580
- <div class="label">Timestamp</div>
581
- <div class="value">{{TIMESTAMP_FORMATTED}}</div>
582
- </div>
583
- <div class="metadata-card">
584
- <div class="label">Process ID</div>
585
- <div class="value">{{PID}}</div>
586
- </div>
587
- </div>
588
-
589
- <div class="suggestions-box {{SUGGESTIONS_SECTION_CLASS}}">
590
- <h3>{{SUGGESTIONS_TITLE}}</h3>
591
- <ul>
592
- {{SUGGESTIONS_LIST}}
593
- </ul>
594
- </div>
595
- </div>
596
-
597
- <!-- SECTION 3: Dev Server Error -->
598
- <div id="dev-server-error-section" class="{{DEV_SERVER_SECTION_CLASS}}">
599
- <div class="content-section">
600
- <h2>⚠️ {{ERROR_TITLE}}</h2>
601
- <div class="message-box">
602
- <p>{{ERROR_MESSAGE_TEXT}}</p>
603
- </div>
604
- </div>
605
-
606
- <div class="content-section">
607
- <h3>Error Output</h3>
608
- <div class="code-output">
609
- <pre>{{STDERR_OUTPUT}}</pre>
610
- </div>
611
- </div>
612
-
613
- <div class="auto-refresh-indicator {{AUTO_REFRESH_CLASS}}">{{AUTO_REFRESH_TEXT}}</div>
614
-
615
- <div class="suggestions-box {{SUGGESTIONS_SECTION_CLASS}}">
616
- <h3>{{SUGGESTIONS_TITLE}}</h3>
617
- <ul>
618
- {{SUGGESTIONS_LIST}}
619
- </ul>
620
- </div>
621
- </div>
622
- </div>
623
-
624
- <!-- Diagnostics Panel (Right) -->
625
- <div class="diagnostics-panel">
626
- <h2>Diagnostics</h2>
627
-
628
- <ul class="diagnostics-list">
629
- <li class="{{SIMPLE_SECTION_CLASS}}">
630
- <span class="label">Dev Server URL:</span>
631
- <span class="value"><code>{{DEV_SERVER_URL}}</code></span>
632
- </li>
633
- <li class="{{SIMPLE_SECTION_CLASS}}">
634
- <span class="label">Proxy URL:</span>
635
- <span class="value"><code>{{PROXY_URL}}</code></span>
636
- </li>
637
- <li class="{{SIMPLE_SECTION_CLASS}}">
638
- <span class="label">Workspace Script:</span>
639
- <span class="value"><code>{{WORKSPACE_SCRIPT}}</code></span>
640
- </li>
641
- <li class="{{SIMPLE_SECTION_CLASS}}">
642
- <span class="label">Target Org:</span>
643
- <span class="value">{{ORG_TARGET}}</span>
644
- </li>
645
- <li class="{{SIMPLE_SECTION_CLASS}}">
646
- <span class="label">Last Check:</span>
647
- <span class="value">{{LAST_CHECK_TIME}}</span>
648
- </li>
649
-
650
- <li class="{{RUNTIME_SECTION_CLASS}}">
651
- <span class="label">Node Version:</span>
652
- <span class="value">{{NODE_VERSION}}</span>
653
- </li>
654
- <li class="{{RUNTIME_SECTION_CLASS}}">
655
- <span class="label">Platform:</span>
656
- <span class="value">{{PLATFORM}}</span>
657
- </li>
658
- <li class="{{RUNTIME_SECTION_CLASS}}">
659
- <span class="label">Memory Usage:</span>
660
- <span class="value">{{HEAP_USED_MB}} MB / {{HEAP_TOTAL_MB}} MB heap</span>
661
- </li>
662
- <li class="{{RUNTIME_SECTION_CLASS}}">
663
- <span class="label">Process ID:</span>
664
- <span class="value">{{PID}}</span>
665
- </li>
666
- </ul>
667
-
668
- <div class="live-logs {{SIMPLE_SECTION_CLASS}}">
669
- <div class="log-line">
670
- <span class="timestamp">[{{LAST_CHECK_TIME}}]</span>
671
- <span class="log-content">proxy ▶ waiting for backend...</span>
672
- </div>
673
- <div class="log-line">
674
- <span class="timestamp">[{{LAST_CHECK_TIME}}]</span>
675
- <span class="log-content">check {{DEV_SERVER_URL}} ▶ unreachable</span>
676
- </div>
677
- <div class="log-line">
678
- <span class="timestamp">[{{LAST_CHECK_TIME}}]</span>
679
- <span class="log-content">hint ▶ try "{{WORKSPACE_SCRIPT}}"</span>
680
- </div>
681
- </div>
682
-
683
- <div class="live-logs {{RUNTIME_SECTION_CLASS}}">
684
- <div class="log-line">
685
- <span class="timestamp">[{{TIMESTAMP_FORMATTED}}]</span>
686
- <span class="log-content">error ▶ {{ERROR_TYPE}} detected</span>
687
- </div>
688
- <div class="log-line">
689
- <span class="log-content">severity ▶ {{SEVERITY_LABEL}}</span>
690
- </div>
691
- </div>
692
-
693
- <!-- Emergency exit commands -->
694
- <div class="emergency-commands">
695
- <h3>⚠️ If Ctrl+C doesn't work</h3>
696
- <p>Copy and run this command in a new terminal to force-stop the proxy:</p>
697
-
698
- <div class="command-item">
699
- <div class="command-comment">Kill all processes on port {{PROXY_PORT}}:</div>
700
- <div class="command-wrapper">
701
- <div class="command-box">lsof -ti:{{PROXY_PORT}} | xargs kill -9</div>
702
- <button
703
- class="copy-button"
704
- onclick="copyCommand(this, 'lsof -ti:{{PROXY_PORT}} | xargs kill -9')"
705
- >
706
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
707
- <path
708
- d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"
709
- />
710
- </svg>
711
- </button>
712
- </div>
713
- </div>
714
- </div>
715
- </div>
716
- </div>
717
-
718
- <script>
719
- function copyCommand(button, command) {
720
- const textarea = document.createElement("textarea");
721
- textarea.value = command;
722
- textarea.style.position = "fixed";
723
- textarea.style.opacity = "0";
724
- document.body.appendChild(textarea);
725
- textarea.select();
726
- document.execCommand("copy");
727
- document.body.removeChild(textarea);
728
- const originalHTML = button.innerHTML;
729
- button.innerHTML =
730
- '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>';
731
- button.classList.add("copied");
732
- setTimeout(() => {
733
- button.innerHTML = originalHTML;
734
- button.classList.remove("copied");
735
- }, 2000);
736
- }
737
- </script>
738
-
739
- <!-- Footer -->
740
- <div class="footer-section">
741
- <p class="help-text">
742
- Salesforce Web App Development Proxy &nbsp;•&nbsp; Press <strong>Ctrl+C</strong> in the
743
- terminal to stop the proxy
744
- </p>
745
- </div>
746
- </div>
747
- </body>
748
- </html>