@salesforce/ui-bundle 1.117.2

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 (68) hide show
  1. package/LICENSE.txt +82 -0
  2. package/README.md +3 -0
  3. package/dist/api/clients.d.ts +22 -0
  4. package/dist/api/clients.d.ts.map +1 -0
  5. package/dist/api/clients.js +84 -0
  6. package/dist/api/graphql-operations-types.d.ts +225 -0
  7. package/dist/api/graphql-operations-types.d.ts.map +1 -0
  8. package/dist/api/index.d.ts +10 -0
  9. package/dist/api/index.d.ts.map +1 -0
  10. package/dist/api/index.js +13 -0
  11. package/dist/api/utils/accounts.d.ts +33 -0
  12. package/dist/api/utils/accounts.d.ts.map +1 -0
  13. package/dist/api/utils/accounts.js +47 -0
  14. package/dist/api/utils/records.d.ts +16 -0
  15. package/dist/api/utils/records.d.ts.map +1 -0
  16. package/dist/api/utils/records.js +26 -0
  17. package/dist/api/utils/user.d.ts +17 -0
  18. package/dist/api/utils/user.d.ts.map +1 -0
  19. package/dist/api/utils/user.js +25 -0
  20. package/dist/app/index.d.ts +10 -0
  21. package/dist/app/index.d.ts.map +1 -0
  22. package/dist/app/index.js +7 -0
  23. package/dist/app/manifest.d.ts +34 -0
  24. package/dist/app/manifest.d.ts.map +1 -0
  25. package/dist/app/manifest.js +28 -0
  26. package/dist/app/org.d.ts +28 -0
  27. package/dist/app/org.d.ts.map +1 -0
  28. package/dist/app/org.js +67 -0
  29. package/dist/design/design-mode-interactions.js +761 -0
  30. package/dist/design/index.d.ts +12 -0
  31. package/dist/design/index.d.ts.map +1 -0
  32. package/dist/design/index.js +14 -0
  33. package/dist/design/interactions/communicationManager.d.ts +25 -0
  34. package/dist/design/interactions/communicationManager.d.ts.map +1 -0
  35. package/dist/design/interactions/componentMatcher.d.ts +43 -0
  36. package/dist/design/interactions/componentMatcher.d.ts.map +1 -0
  37. package/dist/design/interactions/editableManager.d.ts +25 -0
  38. package/dist/design/interactions/editableManager.d.ts.map +1 -0
  39. package/dist/design/interactions/eventHandlers.d.ts +40 -0
  40. package/dist/design/interactions/eventHandlers.d.ts.map +1 -0
  41. package/dist/design/interactions/index.d.ts +7 -0
  42. package/dist/design/interactions/index.d.ts.map +1 -0
  43. package/dist/design/interactions/interactionsController.d.ts +36 -0
  44. package/dist/design/interactions/interactionsController.d.ts.map +1 -0
  45. package/dist/design/interactions/styleManager.d.ts +49 -0
  46. package/dist/design/interactions/styleManager.d.ts.map +1 -0
  47. package/dist/design/interactions/utils/cssUtils.d.ts +54 -0
  48. package/dist/design/interactions/utils/cssUtils.d.ts.map +1 -0
  49. package/dist/design/interactions/utils/sourceUtils.d.ts +36 -0
  50. package/dist/design/interactions/utils/sourceUtils.d.ts.map +1 -0
  51. package/dist/index.d.ts +10 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +25 -0
  54. package/dist/package.json.js +4 -0
  55. package/dist/proxy/handler.d.ts +38 -0
  56. package/dist/proxy/handler.d.ts.map +1 -0
  57. package/dist/proxy/handler.js +530 -0
  58. package/dist/proxy/index.d.ts +8 -0
  59. package/dist/proxy/index.d.ts.map +1 -0
  60. package/dist/proxy/index.js +7 -0
  61. package/dist/proxy/livePreviewScript.d.ts +21 -0
  62. package/dist/proxy/livePreviewScript.d.ts.map +1 -0
  63. package/dist/proxy/livePreviewScript.js +16 -0
  64. package/dist/proxy/routing.d.ts +35 -0
  65. package/dist/proxy/routing.d.ts.map +1 -0
  66. package/dist/proxy/routing.js +83 -0
  67. package/dist/proxy/templates/livePreviewScript.js +553 -0
  68. package/package.json +65 -0
@@ -0,0 +1,530 @@
1
+ import { LIVE_PREVIEW_SCRIPT_MARKER, getLivePreviewScriptContent } from "./livePreviewScript.js";
2
+ import { applyTrailingSlash, matchRoute } from "./routing.js";
3
+ import { version } from "../package.json.js";
4
+ import { refreshOrgAuth } from "../app/org.js";
5
+ import "node:fs/promises";
6
+ const UIBUNDLE_HEALTH_CHECK_PARAM = "sfProxyHealthCheck";
7
+ const UIBUNDLE_PROXY_HEADER = "X-Salesforce-UIBundle-Proxy";
8
+ const LIGHTNING_OUT_SINGLE_ACCESS_PATH = "/services/oauth2/singleaccess";
9
+ const SALESFORCE_API_PREFIXES = ["/services/", "/lwr/apex/"];
10
+ const SALESFORCE_FILE_UPLOAD_PREFIX = "/chatter/handlers/file/body";
11
+ const AUTH_FAILED_RESPONSE = {
12
+ error: "AUTHENTICATION_FAILED",
13
+ message: "Authentication failed. Please re-authenticate to your Salesforce org.",
14
+ status: 401
15
+ };
16
+ class AuthenticationError extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = "AuthenticationError";
20
+ }
21
+ }
22
+ class UIBundleProxyHandler {
23
+ constructor(manifest, orgInfo, target, basePath, options) {
24
+ this.manifest = manifest;
25
+ this.orgInfo = orgInfo;
26
+ this.target = target;
27
+ this.basePath = basePath;
28
+ this.options = options;
29
+ }
30
+ startTime = Date.now();
31
+ async handle(req, res, next) {
32
+ const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
33
+ if (url.searchParams.get(UIBUNDLE_HEALTH_CHECK_PARAM) === "true") {
34
+ this.handleHealthCheck(res);
35
+ return;
36
+ }
37
+ let pathname = url.pathname;
38
+ if (pathname === "/__lo/frontdoor") {
39
+ await this.handleLightningOutFrontdoor(req, res, url);
40
+ return;
41
+ }
42
+ if (this.options?.debug) {
43
+ console.log(`[ui-bundle-proxy] ${req.method} ${pathname}`);
44
+ }
45
+ pathname = applyTrailingSlash(pathname, this.manifest.routing?.trailingSlash);
46
+ const match = matchRoute(
47
+ pathname,
48
+ this.basePath,
49
+ this.manifest.routing?.rewrites,
50
+ this.manifest.routing?.redirects
51
+ );
52
+ if (match) {
53
+ if (match.type === "api") {
54
+ await this.handleSalesforceApi(req, res);
55
+ return;
56
+ }
57
+ if (match.type === "gql") {
58
+ await this.handleGraphQL(req, res);
59
+ return;
60
+ }
61
+ if (match.type === "redirect" && match.target && match.statusCode) {
62
+ this.handleRedirect(res, match.target, match.statusCode);
63
+ return;
64
+ }
65
+ if (match.type === "rewrite" && match.target) {
66
+ url.pathname = `/${match.target}`.replace(/\/+/g, "/");
67
+ req.url = url.pathname + url.search;
68
+ if (this.options?.debug) {
69
+ console.log(`[ui-bundle-proxy] Rewrite to ${req.url}`);
70
+ }
71
+ if (next) {
72
+ next();
73
+ return;
74
+ }
75
+ await this.forwardToDevServer(req, res);
76
+ return;
77
+ }
78
+ if (match.type === "file-upload") {
79
+ if (this.options?.debug) {
80
+ console.log("[ui-bundle-proxy] file-upload match found → handleFileUpload");
81
+ }
82
+ await this.handleFileUpload(req, res);
83
+ return;
84
+ }
85
+ }
86
+ if (pathname.startsWith(SALESFORCE_FILE_UPLOAD_PREFIX)) {
87
+ await this.handleFileUpload(req, res);
88
+ return;
89
+ } else if (SALESFORCE_API_PREFIXES.some((prefix) => pathname.startsWith(prefix))) {
90
+ await this.handleSalesforceApi(req, res);
91
+ return;
92
+ }
93
+ if (next) {
94
+ next();
95
+ } else {
96
+ await this.forwardToDevServer(req, res);
97
+ }
98
+ }
99
+ async forwardToDevServer(req, res) {
100
+ try {
101
+ const baseUrl = this.target ?? `http://${req.headers.host ?? "localhost"}`;
102
+ const url = new URL(req.url ?? "/", baseUrl);
103
+ if (this.options?.debug) {
104
+ console.log(`[ui-bundle-proxy] Forwarding to dev server: ${url.href}`);
105
+ }
106
+ const body = req.method !== "GET" && req.method !== "HEAD" ? await getBody(req) : void 0;
107
+ const response = await fetch(url.href, {
108
+ method: req.method,
109
+ headers: getFilteredHeaders(req.headers),
110
+ body
111
+ });
112
+ await this.sendResponse(res, response);
113
+ } catch (error) {
114
+ console.error("[ui-bundle-proxy] Dev server request failed:", error);
115
+ this.sendAuthOrGatewayError(res, error, "Failed to forward request to dev server");
116
+ }
117
+ }
118
+ handleRedirect(res, location, statusCode) {
119
+ res.writeHead(statusCode, { Location: location });
120
+ res.end();
121
+ }
122
+ handleHealthCheck(res) {
123
+ const response = {
124
+ proxyName: "@salesforce/ui-bundle",
125
+ proxyVersion: version,
126
+ port: this.target ? new URL(this.target).port : void 0,
127
+ org: this.orgInfo?.orgAlias ?? this.orgInfo?.username,
128
+ apiVersion: this.orgInfo?.apiVersion,
129
+ uiBundleName: this.manifest.name,
130
+ uiBundleFolderPath: this.basePath,
131
+ uptime: Date.now() - this.startTime,
132
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
133
+ debugMode: this.options?.debug ?? false
134
+ };
135
+ res.writeHead(200, {
136
+ "Content-Type": "application/json",
137
+ [UIBUNDLE_PROXY_HEADER]: "true"
138
+ });
139
+ res.end(JSON.stringify(response, null, 2));
140
+ }
141
+ sendNoOrgError(res) {
142
+ res.writeHead(401, { "Content-Type": "application/json" });
143
+ res.end(
144
+ JSON.stringify({
145
+ error: "NO_ORG_FOUND",
146
+ message: "No default Salesforce org found. Run 'sf org login web --set-default' to authenticate."
147
+ })
148
+ );
149
+ }
150
+ sendJson(res, statusCode, body) {
151
+ res.writeHead(statusCode, { "Content-Type": "application/json" });
152
+ res.end(JSON.stringify(body));
153
+ }
154
+ sendAuthOrGatewayError(res, error, gatewayMessage) {
155
+ if (error instanceof AuthenticationError) {
156
+ this.sendJson(res, 401, AUTH_FAILED_RESPONSE);
157
+ } else {
158
+ this.sendJson(res, 502, {
159
+ error: "GATEWAY_ERROR",
160
+ message: gatewayMessage
161
+ });
162
+ }
163
+ }
164
+ async handleLightningOutFrontdoor(req, res, _url) {
165
+ try {
166
+ if (!this.orgInfo) {
167
+ this.sendNoOrgError(res);
168
+ return;
169
+ }
170
+ if (req.method && req.method !== "GET") {
171
+ this.sendJson(res, 405, { error: "METHOD_NOT_ALLOWED", message: "Use GET" });
172
+ return;
173
+ }
174
+ const { rawInstanceUrl, accessToken, orgId } = this.orgInfo;
175
+ const frontdoorUrl = await this.fetchLightningOutFrontdoorUrl(rawInstanceUrl, accessToken);
176
+ if (!frontdoorUrl) {
177
+ this.sendJson(res, 502, {
178
+ error: "FRONTDOOR_FAILED",
179
+ message: "No frontdoor URL returned from Salesforce"
180
+ });
181
+ return;
182
+ }
183
+ this.sendJson(res, 200, {
184
+ frontdoorUrl,
185
+ instanceUrl: rawInstanceUrl,
186
+ orgId
187
+ });
188
+ } catch (error) {
189
+ console.error("[ui-bundle-proxy] Frontdoor request failed:", error);
190
+ this.sendJson(res, 502, {
191
+ error: "GATEWAY_ERROR",
192
+ message: "Failed to generate frontdoor URL"
193
+ });
194
+ }
195
+ }
196
+ async fetchLightningOutFrontdoorUrl(rawInstanceUrl, accessToken) {
197
+ let baseUrl = rawInstanceUrl.replace(/\/$/, "");
198
+ let response = await fetch(`${baseUrl}${LIGHTNING_OUT_SINGLE_ACCESS_PATH}`, {
199
+ method: "POST",
200
+ headers: {
201
+ Authorization: `Bearer ${accessToken}`,
202
+ "Content-Type": "application/x-www-form-urlencoded"
203
+ }
204
+ });
205
+ if (response.status === 401 || response.status === 403) {
206
+ const updatedOrgInfo = await this.refreshToken();
207
+ if (!updatedOrgInfo) {
208
+ throw new AuthenticationError("Failed to refresh token");
209
+ }
210
+ baseUrl = updatedOrgInfo.rawInstanceUrl.replace(/\/$/, "");
211
+ response = await fetch(`${baseUrl}${LIGHTNING_OUT_SINGLE_ACCESS_PATH}`, {
212
+ method: "POST",
213
+ headers: {
214
+ Authorization: `Bearer ${updatedOrgInfo.accessToken}`,
215
+ "Content-Type": "application/x-www-form-urlencoded"
216
+ }
217
+ });
218
+ }
219
+ if (!response.ok) {
220
+ const errorBody = await response.text();
221
+ throw new Error(`Frontdoor exchange failed: ${response.status} ${errorBody}`);
222
+ }
223
+ const data = await response.json();
224
+ return data.frontdoor_uri ?? null;
225
+ }
226
+ async handleSalesforceApi(req, res) {
227
+ try {
228
+ if (!this.orgInfo) {
229
+ this.sendNoOrgError(res);
230
+ return;
231
+ }
232
+ const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
233
+ let pathIndex = url.pathname.indexOf("/lwr/apex/v");
234
+ if (pathIndex === -1) {
235
+ pathIndex = url.pathname.indexOf("/services/data/v");
236
+ }
237
+ const apiPath = url.pathname.substring(pathIndex);
238
+ let targetUrl = `${this.orgInfo.instanceUrl}${apiPath}${url.search}`;
239
+ if (this.options?.debug) {
240
+ console.log(`[ui-bundle-proxy] Forwarding to Salesforce: ${targetUrl}`);
241
+ }
242
+ const body = req.method !== "GET" && req.method !== "HEAD" ? await getBody(req) : void 0;
243
+ let response = await fetch(targetUrl, {
244
+ method: req.method,
245
+ headers: {
246
+ ...getFilteredHeaders(req.headers),
247
+ Cookie: `sid=${this.orgInfo.accessToken}`,
248
+ Accept: req.headers.accept ?? "application/json",
249
+ // necessary for Apex requests, for which SessionUtil.validateSessionUsage won't accept OAuth token as `sid` cookie
250
+ Authorization: `Bearer ${this.orgInfo.accessToken}`
251
+ },
252
+ body
253
+ });
254
+ if (response.status === 401 || response.status === 403) {
255
+ console.warn(`[ui-bundle-proxy] Received ${response.status}, refreshing token...`);
256
+ const updatedOrgInfo = await this.refreshToken();
257
+ if (updatedOrgInfo === void 0) {
258
+ console.error("[ui-bundle-proxy] Failed to refresh token - authentication error");
259
+ this.sendJson(res, 401, AUTH_FAILED_RESPONSE);
260
+ return;
261
+ }
262
+ if (this.options?.debug) {
263
+ console.log("[ui-bundle-proxy] Token refreshed, retrying request");
264
+ }
265
+ targetUrl = `${updatedOrgInfo.instanceUrl}${url.pathname}${url.search}`;
266
+ response = await fetch(targetUrl, {
267
+ method: req.method,
268
+ headers: {
269
+ ...getFilteredHeaders(req.headers),
270
+ Cookie: `sid=${updatedOrgInfo.accessToken}`,
271
+ Accept: req.headers.accept ?? "application/json"
272
+ },
273
+ body
274
+ });
275
+ }
276
+ await this.sendResponse(res, response);
277
+ } catch (error) {
278
+ console.error("[ui-bundle-proxy] Salesforce API request failed:", error);
279
+ this.sendAuthOrGatewayError(res, error, "Failed to forward request to Salesforce");
280
+ }
281
+ }
282
+ async refreshToken() {
283
+ if (!this.orgInfo) {
284
+ return void 0;
285
+ }
286
+ const refreshIdentifier = this.orgInfo.orgAlias || this.orgInfo.username;
287
+ if (!refreshIdentifier) {
288
+ throw new AuthenticationError("Cannot refresh token: no org alias or username available");
289
+ }
290
+ const updatedOrgInfo = await refreshOrgAuth(refreshIdentifier);
291
+ if (!updatedOrgInfo) {
292
+ return void 0;
293
+ }
294
+ this.orgInfo = updatedOrgInfo;
295
+ if (this.options?.onTokenRefresh) {
296
+ this.options.onTokenRefresh(updatedOrgInfo);
297
+ }
298
+ return updatedOrgInfo;
299
+ }
300
+ async handleGraphQL(req, res) {
301
+ try {
302
+ if (!this.orgInfo) {
303
+ this.sendNoOrgError(res);
304
+ return;
305
+ }
306
+ const { rawInstanceUrl, apiVersion, accessToken } = this.orgInfo;
307
+ let targetUrl = `${rawInstanceUrl}/services/data/v${apiVersion}/graphql`;
308
+ if (this.options?.debug) {
309
+ console.log(`[ui-bundle-proxy] Forwarding GraphQL to Salesforce: ${targetUrl}`);
310
+ }
311
+ const body = await getBody(req);
312
+ const headers = {
313
+ "Content-Type": "application/json",
314
+ Accept: "application/json",
315
+ Authorization: `Bearer ${accessToken}`,
316
+ "X-Chatter-Entity-Encoding": "false"
317
+ };
318
+ let response = await fetch(targetUrl, {
319
+ method: "POST",
320
+ headers,
321
+ body
322
+ });
323
+ if (response.status === 401 || response.status === 403) {
324
+ console.warn(`[ui-bundle-proxy] Received ${response.status}, refreshing token...`);
325
+ const updatedOrgInfo = await this.refreshToken();
326
+ if (updatedOrgInfo !== void 0) {
327
+ if (this.options?.debug) {
328
+ console.log("[ui-bundle-proxy] Token refreshed, retrying request");
329
+ }
330
+ const { rawInstanceUrl: rawInstanceUrl2, apiVersion: apiVersion2, accessToken: accessToken2 } = updatedOrgInfo;
331
+ targetUrl = `${rawInstanceUrl2}/services/data/v${apiVersion2}/graphql`;
332
+ headers.Authorization = `Bearer ${accessToken2}`;
333
+ response = await fetch(targetUrl, {
334
+ method: "POST",
335
+ headers,
336
+ body
337
+ });
338
+ } else {
339
+ console.error(
340
+ "[ui-bundle-proxy] Failed to refresh token for GraphQL - authentication error"
341
+ );
342
+ this.sendJson(res, 401, AUTH_FAILED_RESPONSE);
343
+ return;
344
+ }
345
+ }
346
+ await this.sendResponse(res, response);
347
+ } catch (error) {
348
+ console.error("[ui-bundle-proxy] GraphQL request failed:", error);
349
+ this.sendAuthOrGatewayError(res, error, "Failed to forward GraphQL request to Salesforce");
350
+ }
351
+ }
352
+ async sendResponse(res, response) {
353
+ const headers = {};
354
+ const skipHeaders = /* @__PURE__ */ new Set(["content-encoding", "content-length", "transfer-encoding"]);
355
+ response.headers.forEach((value, key) => {
356
+ if (!skipHeaders.has(key.toLowerCase())) {
357
+ headers[key] = value;
358
+ }
359
+ });
360
+ const contentType = response.headers.get("content-type") || "";
361
+ const isHtml = contentType.includes("text/html");
362
+ if (isHtml && response.body) {
363
+ const chunks = [];
364
+ const reader = response.body.getReader();
365
+ while (true) {
366
+ const { done, value } = await reader.read();
367
+ if (done) break;
368
+ chunks.push(value);
369
+ }
370
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
371
+ const buffer = new Uint8Array(totalLength);
372
+ let offset = 0;
373
+ for (const chunk of chunks) {
374
+ buffer.set(chunk, offset);
375
+ offset += chunk.length;
376
+ }
377
+ const html = new TextDecoder().decode(buffer);
378
+ const modifiedHtml = UIBundleProxyHandler.injectLivePreviewScript(html);
379
+ const modifiedBuffer = new TextEncoder().encode(modifiedHtml);
380
+ headers["content-length"] = modifiedBuffer.length.toString();
381
+ res.writeHead(response.status, headers);
382
+ res.write(modifiedBuffer);
383
+ } else {
384
+ res.writeHead(response.status, headers);
385
+ if (response.body) {
386
+ const reader = response.body.getReader();
387
+ while (true) {
388
+ const { done, value } = await reader.read();
389
+ if (done) break;
390
+ res.write(value);
391
+ }
392
+ }
393
+ }
394
+ res.end();
395
+ }
396
+ static injectLivePreviewScript(html) {
397
+ if (html.includes(LIVE_PREVIEW_SCRIPT_MARKER)) {
398
+ return html;
399
+ }
400
+ const scriptContent = getLivePreviewScriptContent();
401
+ const scriptTag = `<script ${LIVE_PREVIEW_SCRIPT_MARKER}>` + scriptContent + "<\/script>";
402
+ if (html.includes("</body>")) {
403
+ const lastBodyIndex = html.lastIndexOf("</body>");
404
+ return html.substring(0, lastBodyIndex) + scriptTag + html.substring(lastBodyIndex);
405
+ }
406
+ if (html.includes("</html>")) {
407
+ const lastHtmlIndex = html.lastIndexOf("</html>");
408
+ return html.substring(0, lastHtmlIndex) + scriptTag + html.substring(lastHtmlIndex);
409
+ }
410
+ return html + scriptTag;
411
+ }
412
+ /**
413
+ * Proxy POST /chatter/handlers/file/body (XHR/file upload) to Salesforce.
414
+ * Uses rawInstanceUrl for Chatter API. Preserves multipart/form-data from XHR.
415
+ */
416
+ async handleFileUpload(req, res) {
417
+ try {
418
+ if (!this.orgInfo) {
419
+ this.sendNoOrgError(res);
420
+ return;
421
+ }
422
+ const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
423
+ const pathIndex = url.pathname.indexOf("/chatter/handlers/file/body");
424
+ const apiPath = url.pathname.substring(pathIndex);
425
+ const uploadUrl = `${this.orgInfo.rawInstanceUrl}${apiPath}${url.search}`;
426
+ if (this.options?.debug) {
427
+ console.log(`[ui-bundle-proxy] Forwarding file upload to Salesforce: ${uploadUrl}`);
428
+ }
429
+ const body = await getBody(req);
430
+ if (!body?.length) {
431
+ res.writeHead(400, { "Content-Type": "application/json" });
432
+ res.end(
433
+ JSON.stringify({
434
+ error: "BAD_REQUEST",
435
+ message: "Request body is empty"
436
+ })
437
+ );
438
+ return;
439
+ }
440
+ const contentType = req.headers["content-type"];
441
+ if (!contentType?.includes("multipart/form-data")) {
442
+ res.writeHead(400, { "Content-Type": "application/json" });
443
+ res.end(
444
+ JSON.stringify({
445
+ error: "BAD_REQUEST",
446
+ message: "Content-Type must be multipart/form-data"
447
+ })
448
+ );
449
+ return;
450
+ }
451
+ const headers = {
452
+ "Content-Type": contentType,
453
+ "Content-Length": String(body.length),
454
+ Cookie: `sid=${this.orgInfo.accessToken}`,
455
+ Authorization: `Bearer ${this.orgInfo.accessToken}`
456
+ };
457
+ const response = await fetch(uploadUrl, {
458
+ method: "POST",
459
+ headers,
460
+ body: new Uint8Array(body)
461
+ });
462
+ const resHeaders = {};
463
+ const skipHeaders = /* @__PURE__ */ new Set(["content-encoding", "transfer-encoding"]);
464
+ response.headers.forEach((value, key) => {
465
+ if (!skipHeaders.has(key.toLowerCase())) {
466
+ resHeaders[key] = value;
467
+ }
468
+ });
469
+ res.writeHead(response.status, resHeaders);
470
+ if (response.body) {
471
+ const reader = response.body.getReader();
472
+ while (true) {
473
+ const { done, value } = await reader.read();
474
+ if (done) break;
475
+ res.write(value);
476
+ }
477
+ }
478
+ res.end();
479
+ } catch (error) {
480
+ console.error("[ui-bundle-proxy] File upload proxy failed:", error);
481
+ res.writeHead(502, { "Content-Type": "application/json" });
482
+ res.end(
483
+ JSON.stringify({
484
+ error: "GATEWAY_ERROR",
485
+ message: "Failed to forward file upload"
486
+ })
487
+ );
488
+ }
489
+ }
490
+ }
491
+ function createProxyHandler(manifest, orgInfo, target, basePath, options) {
492
+ const handler = new UIBundleProxyHandler(manifest, orgInfo, target, basePath, options);
493
+ return (req, res, next) => handler.handle(req, res, next);
494
+ }
495
+ function injectLivePreviewScript(html) {
496
+ return UIBundleProxyHandler.injectLivePreviewScript(html);
497
+ }
498
+ function getFilteredHeaders(headers) {
499
+ const filtered = {};
500
+ const hopByHopHeaders = /* @__PURE__ */ new Set([
501
+ "connection",
502
+ "keep-alive",
503
+ "proxy-authenticate",
504
+ "proxy-authorization",
505
+ "te",
506
+ "trailer",
507
+ "transfer-encoding",
508
+ "upgrade"
509
+ ]);
510
+ for (const [key, value] of Object.entries(headers)) {
511
+ if (!hopByHopHeaders.has(key.toLowerCase()) && value) {
512
+ filtered[key] = Array.isArray(value) ? value.join(", ") : typeof value === "string" ? value : String(value);
513
+ }
514
+ }
515
+ return filtered;
516
+ }
517
+ function getBody(req) {
518
+ return new Promise((resolve, reject) => {
519
+ const chunks = [];
520
+ req.on("data", (chunk) => chunks.push(chunk));
521
+ req.on("end", () => resolve(Buffer.concat(chunks)));
522
+ req.on("error", reject);
523
+ });
524
+ }
525
+ export {
526
+ UIBUNDLE_HEALTH_CHECK_PARAM,
527
+ UIBUNDLE_PROXY_HEADER,
528
+ createProxyHandler,
529
+ injectLivePreviewScript
530
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ export type { ProxyOptions, ProxyHandler } from './handler';
7
+ export { createProxyHandler, injectLivePreviewScript, UIBUNDLE_HEALTH_CHECK_PARAM, UIBUNDLE_PROXY_HEADER, } from './handler';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,WAAW,CAAC;AAC5D,OAAO,EACN,kBAAkB,EAClB,uBAAuB,EACvB,2BAA2B,EAC3B,qBAAqB,GACrB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { UIBUNDLE_HEALTH_CHECK_PARAM, UIBUNDLE_PROXY_HEADER, createProxyHandler, injectLivePreviewScript } from "./handler.js";
2
+ export {
3
+ UIBUNDLE_HEALTH_CHECK_PARAM,
4
+ UIBUNDLE_PROXY_HEADER,
5
+ createProxyHandler,
6
+ injectLivePreviewScript
7
+ };
@@ -0,0 +1,21 @@
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 JavaScript source that gets injected into previewed UI Bundles.
8
+ * Reads from templates/livePreviewScript.js at runtime (copied to dist/ by postbuild).
9
+ * Cached after first read.
10
+ *
11
+ * Responsibilities:
12
+ * - Fetch interceptor for network-error detection (runs synchronously on load)
13
+ * - Runtime / compile / HMR error listeners
14
+ * - Error deduplication
15
+ * - postMessage bridge to the VS Code webview (when running inside an iframe)
16
+ * - Copy / paste / right-click bridge for VS Code webview
17
+ */
18
+ export declare function getLivePreviewScriptContent(): string;
19
+ /** Data attribute used to detect (and prevent) double injection. */
20
+ export declare const LIVE_PREVIEW_SCRIPT_MARKER = "data-live-preview";
21
+ //# sourceMappingURL=livePreviewScript.d.ts.map
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,16 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const __dirname$1 = dirname(fileURLToPath(import.meta.url));
5
+ let cached = null;
6
+ function getLivePreviewScriptContent() {
7
+ if (cached) return cached;
8
+ const scriptPath = join(__dirname$1, "templates", "livePreviewScript.js");
9
+ cached = readFileSync(scriptPath, "utf-8");
10
+ return cached;
11
+ }
12
+ const LIVE_PREVIEW_SCRIPT_MARKER = "data-live-preview";
13
+ export {
14
+ LIVE_PREVIEW_SCRIPT_MARKER,
15
+ getLivePreviewScriptContent
16
+ };
@@ -0,0 +1,35 @@
1
+ import { RedirectRule, RewriteRule } from '../app/index';
2
+ export interface RouteMatch {
3
+ type: "rewrite" | "redirect" | "api" | "gql" | "file-upload";
4
+ target?: string;
5
+ statusCode?: number;
6
+ params?: Record<string, string>;
7
+ }
8
+ /**
9
+ * Match URL path against routing rules
10
+ *
11
+ * @param pathname - The URL pathname to match
12
+ * @param basePath - Optional base path of Salesforce server
13
+ * @param rewrites - Optional array of rewrite rules
14
+ * @param redirects - Optional array of redirect rules
15
+ * @returns Route match result indicating the type and target, or null if no match
16
+ */
17
+ export declare function matchRoute(pathname: string, basePath?: string, rewrites?: RewriteRule[], redirects?: RedirectRule[]): RouteMatch | null;
18
+ /**
19
+ * Check if a path matches any of the given glob patterns
20
+ * Supports glob wildcards: * (matches anything except /), ** (matches anything including /), ? (single character)
21
+ *
22
+ * @param path - The path to test
23
+ * @param patterns - Array of glob patterns to match against
24
+ * @returns True if the path matches any pattern, false otherwise
25
+ */
26
+ export declare function matchesPattern(path: string, patterns: string[] | undefined): boolean;
27
+ /**
28
+ * Apply trailing slash rules to pathname
29
+ *
30
+ * @param pathname - The URL pathname
31
+ * @param trailingSlash - Trailing slash handling strategy
32
+ * @returns Modified pathname with trailing slash applied according to rules
33
+ */
34
+ export declare function applyTrailingSlash(pathname: string, trailingSlash?: "always" | "never" | "auto"): string;
35
+ //# sourceMappingURL=routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/proxy/routing.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,aAAa,CAAC;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AASD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,WAAW,EAAE,EACxB,SAAS,CAAC,EAAE,YAAY,EAAE,GACxB,UAAU,GAAG,IAAI,CAqEnB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,OAAO,CAMpF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GACzC,MAAM,CAiBR"}