remote-components 0.0.50 → 0.0.51

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 (71) hide show
  1. package/dist/html/host.cjs +330 -166
  2. package/dist/html/host.cjs.map +1 -1
  3. package/dist/html/host.js +330 -166
  4. package/dist/html/host.js.map +1 -1
  5. package/dist/html/remote.cjs +114 -20
  6. package/dist/html/remote.cjs.map +1 -1
  7. package/dist/html/remote.js +114 -20
  8. package/dist/html/remote.js.map +1 -1
  9. package/dist/internal/shared/{ssr → client}/fetch-with-protected-rc-fallback.cjs +9 -6
  10. package/dist/internal/shared/client/fetch-with-protected-rc-fallback.cjs.map +1 -0
  11. package/dist/internal/shared/{ssr → client}/fetch-with-protected-rc-fallback.d.ts +1 -2
  12. package/dist/internal/shared/{ssr → client}/fetch-with-protected-rc-fallback.js +10 -6
  13. package/dist/internal/shared/client/fetch-with-protected-rc-fallback.js.map +1 -0
  14. package/dist/internal/shared/client/protected-rc-fallback.cjs +32 -0
  15. package/dist/internal/shared/client/protected-rc-fallback.cjs.map +1 -0
  16. package/dist/internal/shared/client/protected-rc-fallback.d.ts +6 -0
  17. package/dist/internal/shared/client/protected-rc-fallback.js +8 -0
  18. package/dist/internal/shared/client/protected-rc-fallback.js.map +1 -0
  19. package/dist/internal/shared/client/remote-component.cjs +190 -100
  20. package/dist/internal/shared/client/remote-component.cjs.map +1 -1
  21. package/dist/internal/shared/client/remote-component.js +190 -100
  22. package/dist/internal/shared/client/remote-component.js.map +1 -1
  23. package/dist/internal/shared/constants.cjs +29 -0
  24. package/dist/internal/shared/constants.cjs.map +1 -0
  25. package/dist/internal/shared/constants.d.ts +3 -0
  26. package/dist/internal/shared/constants.js +5 -0
  27. package/dist/internal/shared/constants.js.map +1 -0
  28. package/dist/internal/shared/ssr/fetch-remote-component.cjs +1 -2
  29. package/dist/internal/shared/ssr/fetch-remote-component.cjs.map +1 -1
  30. package/dist/internal/shared/ssr/fetch-remote-component.js +1 -2
  31. package/dist/internal/shared/ssr/fetch-remote-component.js.map +1 -1
  32. package/dist/internal/shared/ssr/fetch-with-hooks.cjs +1 -1
  33. package/dist/internal/shared/ssr/fetch-with-hooks.cjs.map +1 -1
  34. package/dist/internal/shared/ssr/fetch-with-hooks.js +1 -1
  35. package/dist/internal/shared/ssr/fetch-with-hooks.js.map +1 -1
  36. package/dist/internal/shared/utils/logger.cjs.map +1 -1
  37. package/dist/internal/shared/utils/logger.d.ts +1 -1
  38. package/dist/internal/shared/utils/logger.js.map +1 -1
  39. package/dist/next/config.cjs.map +1 -1
  40. package/dist/next/config.js.map +1 -1
  41. package/dist/next/host/client/index.cjs +202 -107
  42. package/dist/next/host/client/index.cjs.map +1 -1
  43. package/dist/next/host/client/index.js +202 -107
  44. package/dist/next/host/client/index.js.map +1 -1
  45. package/dist/next/host/pages-router-server.cjs +1 -2
  46. package/dist/next/host/pages-router-server.cjs.map +1 -1
  47. package/dist/next/host/pages-router-server.d.ts +5 -0
  48. package/dist/next/host/pages-router-server.js +1 -2
  49. package/dist/next/host/pages-router-server.js.map +1 -1
  50. package/dist/next/proxy.cjs +36 -24
  51. package/dist/next/proxy.cjs.map +1 -1
  52. package/dist/next/proxy.d.ts +0 -1
  53. package/dist/next/proxy.js +36 -24
  54. package/dist/next/proxy.js.map +1 -1
  55. package/dist/react/index.cjs +202 -107
  56. package/dist/react/index.cjs.map +1 -1
  57. package/dist/react/index.js +202 -107
  58. package/dist/react/index.js.map +1 -1
  59. package/dist/shared/host/proxy.cjs +23 -20
  60. package/dist/shared/host/proxy.cjs.map +1 -1
  61. package/dist/shared/host/proxy.d.ts +0 -3
  62. package/dist/shared/host/proxy.js +23 -19
  63. package/dist/shared/host/proxy.js.map +1 -1
  64. package/dist/shared/remote/proxy.cjs +9 -1
  65. package/dist/shared/remote/proxy.cjs.map +1 -1
  66. package/dist/shared/remote/proxy.d.ts +7 -1
  67. package/dist/shared/remote/proxy.js +8 -1
  68. package/dist/shared/remote/proxy.js.map +1 -1
  69. package/package.json +1 -1
  70. package/dist/internal/shared/ssr/fetch-with-protected-rc-fallback.cjs.map +0 -1
  71. package/dist/internal/shared/ssr/fetch-with-protected-rc-fallback.js.map +0 -1
@@ -18,22 +18,36 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var proxy_exports = {};
20
20
  __export(proxy_exports, {
21
- RC_PROTECTED_REMOTE_FETCH_PATHNAME: () => import_fetch_with_protected_rc_fallback.RC_PROTECTED_REMOTE_FETCH_PATHNAME,
22
21
  handleProtectedRemoteFetchRequest: () => handleProtectedRemoteFetchRequest
23
22
  });
24
23
  module.exports = __toCommonJS(proxy_exports);
24
+ var import_constants = require("#internal/shared/constants");
25
25
  var import_fetch_headers = require("#internal/shared/ssr/fetch-headers");
26
- var import_fetch_with_protected_rc_fallback = require("#internal/shared/ssr/fetch-with-protected-rc-fallback");
27
- function isUrlAllowed(targetUrl, options) {
26
+ async function handleProtectedRemoteFetchRequest(requestUrl, options) {
27
+ const url = new URL(requestUrl, "https://fallback.com");
28
+ if (url.pathname !== import_constants.RC_PROTECTED_REMOTE_FETCH_PATHNAME) {
29
+ return null;
30
+ }
31
+ const targetUrl = url.searchParams.get("url");
32
+ if (!targetUrl) {
33
+ return new Response("Bad request, missing url query param", {
34
+ status: 400
35
+ });
36
+ }
28
37
  const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(
29
38
  ","
30
39
  ).map((p) => p.trim());
31
40
  const optionPatterns = options?.allowedProxyUrls;
32
41
  const allowedPatterns = [...optionPatterns || [], ...envPatterns || []];
33
42
  if (allowedPatterns.length === 0) {
34
- return false;
43
+ return new Response(
44
+ `Forbidden: no allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS provided to withRemoteComponentsHost.`,
45
+ {
46
+ status: 403
47
+ }
48
+ );
35
49
  }
36
- return allowedPatterns.some((pattern) => {
50
+ const isUrlAllowed = allowedPatterns.some((pattern) => {
37
51
  try {
38
52
  const regex = new RegExp(pattern);
39
53
  return regex.test(targetUrl);
@@ -45,19 +59,7 @@ function isUrlAllowed(targetUrl, options) {
45
59
  return false;
46
60
  }
47
61
  });
48
- }
49
- async function handleProtectedRemoteFetchRequest(requestUrl, options) {
50
- const url = new URL(requestUrl, "https://fallback.com");
51
- if (url.pathname !== import_fetch_with_protected_rc_fallback.RC_PROTECTED_REMOTE_FETCH_PATHNAME) {
52
- return null;
53
- }
54
- const targetUrl = url.searchParams.get("url");
55
- if (!targetUrl) {
56
- return new Response("Bad request, missing url query param", {
57
- status: 400
58
- });
59
- }
60
- if (!isUrlAllowed(targetUrl, options)) {
62
+ if (!isUrlAllowed) {
61
63
  return new Response(
62
64
  `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,
63
65
  {
@@ -66,14 +68,15 @@ async function handleProtectedRemoteFetchRequest(requestUrl, options) {
66
68
  );
67
69
  }
68
70
  const response = await fetch(targetUrl, { headers: (0, import_fetch_headers.remoteFetchHeaders)() });
71
+ const contentType = response.headers.get("content-type");
69
72
  return new Response(response.body, {
70
73
  status: response.status,
71
- statusText: response.statusText
74
+ statusText: response.statusText,
75
+ headers: contentType ? { "content-type": contentType } : void 0
72
76
  });
73
77
  }
74
78
  // Annotate the CommonJS export names for ESM import in node:
75
79
  0 && (module.exports = {
76
- RC_PROTECTED_REMOTE_FETCH_PATHNAME,
77
80
  handleProtectedRemoteFetchRequest
78
81
  });
79
82
  //# sourceMappingURL=proxy.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/shared/host/proxy.ts"],"sourcesContent":["/**\n * Proxy utilities for host applications that consume remote components.\n *\n * Hosts do NOT handle CORS - that's the remote's responsibility.\n * Hosts only handle protected fetch proxying.\n */\n\nimport { remoteFetchHeaders } from '#internal/shared/ssr/fetch-headers';\nimport { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from '#internal/shared/ssr/fetch-with-protected-rc-fallback';\n\nexport interface HostProxyOptions {\n /**\n * List of allowed URL patterns (as regex strings) that can be proxied.\n * These patterns are combined with REMOTE_COMPONENTS_ALLOWED_PROXY_URLS env var if both are set.\n * If neither is set, all URLs are blocked.\n */\n allowedProxyUrls?: string[];\n}\n\n/**\n * Validates if a URL is allowed to be proxied based on the allowed patterns.\n *\n * @param targetUrl - The URL to validate\n * @param options - Host proxy configuration options\n * @returns true if the URL is allowed, false otherwise\n */\nfunction isUrlAllowed(targetUrl: string, options?: HostProxyOptions): boolean {\n const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(\n ',',\n ).map((p) => p.trim());\n const optionPatterns = options?.allowedProxyUrls;\n\n // Combine both sources if both are specified\n const allowedPatterns = [...(optionPatterns || []), ...(envPatterns || [])];\n\n if (allowedPatterns.length === 0) {\n return false;\n }\n\n // Check if the URL matches any of the allowed patterns\n return allowedPatterns.some((pattern) => {\n try {\n const regex = new RegExp(pattern);\n return regex.test(targetUrl);\n } catch (error) {\n console.error(\n `Invalid regex pattern in allowedProxyUrls: ${pattern}`,\n error,\n );\n return false;\n }\n });\n}\n\n/**\n * Handles protected remote component fetch requests by proxying them with\n * authentication headers. This is needed for accessing Vercel-protected remote\n * component deployments from client-side code.\n *\n * @param requestUrl - The full request URL\n * @param options - Host proxy configuration options\n * @returns Response object if this is a protected fetch request, or null if not\n */\nexport async function handleProtectedRemoteFetchRequest(\n requestUrl: string,\n options?: HostProxyOptions,\n): Promise<Response | null> {\n const url = new URL(requestUrl, 'https://fallback.com');\n\n if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {\n return null;\n }\n\n const targetUrl = url.searchParams.get('url');\n if (!targetUrl) {\n return new Response('Bad request, missing url query param', {\n status: 400,\n });\n }\n\n // Validate URL against allowed patterns to prevent SSRF attacks\n if (!isUrlAllowed(targetUrl, options)) {\n return new Response(\n `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Fetch the remote resource\n const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });\n\n // Create new headers without content-encoding to avoid decoding errors\n // (Node.js fetch auto-decodes but keeps the header, causing browser issues)\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n });\n}\n\nexport { RC_PROTECTED_REMOTE_FETCH_PATHNAME };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,2BAAmC;AACnC,8CAAmD;AAkBnD,SAAS,aAAa,WAAmB,SAAqC;AAC5E,QAAM,cAAc,QAAQ,IAAI,sCAAsC;AAAA,IACpE;AAAA,EACF,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACrB,QAAM,iBAAiB,SAAS;AAGhC,QAAM,kBAAkB,CAAC,GAAI,kBAAkB,CAAC,GAAI,GAAI,eAAe,CAAC,CAAE;AAE1E,MAAI,gBAAgB,WAAW,GAAG;AAChC,WAAO;AAAA,EACT;AAGA,SAAO,gBAAgB,KAAK,CAAC,YAAY;AACvC,QAAI;AACF,YAAM,QAAQ,IAAI,OAAO,OAAO;AAChC,aAAO,MAAM,KAAK,SAAS;AAAA,IAC7B,SAAS,OAAP;AACA,cAAQ;AAAA,QACN,8CAA8C;AAAA,QAC9C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAWA,eAAsB,kCACpB,YACA,SAC0B;AAC1B,QAAM,MAAM,IAAI,IAAI,YAAY,sBAAsB;AAEtD,MAAI,IAAI,aAAa,4EAAoC;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,IAAI,aAAa,IAAI,KAAK;AAC5C,MAAI,CAAC,WAAW;AACd,WAAO,IAAI,SAAS,wCAAwC;AAAA,MAC1D,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,aAAa,WAAW,OAAO,GAAG;AACrC,WAAO,IAAI;AAAA,MACT,mCAAmC;AAAA,MACnC;AAAA,QACE,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAW,MAAM,MAAM,WAAW,EAAE,aAAS,yCAAmB,EAAE,CAAC;AAKzE,SAAO,IAAI,SAAS,SAAS,MAAM;AAAA,IACjC,QAAQ,SAAS;AAAA,IACjB,YAAY,SAAS;AAAA,EACvB,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../../src/shared/host/proxy.ts"],"sourcesContent":["/**\n * Proxy utilities for host applications that consume remote components.\n *\n * Hosts do NOT handle CORS - that's the remote's responsibility.\n * Hosts only handle protected fetch proxying.\n */\n\nimport { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from '#internal/shared/constants';\nimport { remoteFetchHeaders } from '#internal/shared/ssr/fetch-headers';\n\nexport interface HostProxyOptions {\n /**\n * List of allowed URL patterns (as regex strings) that can be proxied.\n * These patterns are combined with REMOTE_COMPONENTS_ALLOWED_PROXY_URLS env var if both are set.\n * If neither is set, all URLs are blocked.\n */\n allowedProxyUrls?: string[];\n}\n\n/**\n * Handles protected remote component fetch requests by proxying them with\n * authentication headers. This is needed for accessing Vercel-protected remote\n * component deployments from client-side code.\n *\n * @param requestUrl - The full request URL\n * @param options - Host proxy configuration options\n * @returns Response object if this is a protected fetch request, or null if not\n */\nexport async function handleProtectedRemoteFetchRequest(\n requestUrl: string,\n options?: HostProxyOptions,\n): Promise<Response | null> {\n const url = new URL(requestUrl, 'https://fallback.com');\n\n if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {\n return null;\n }\n\n const targetUrl = url.searchParams.get('url');\n if (!targetUrl) {\n return new Response('Bad request, missing url query param', {\n status: 400,\n });\n }\n\n const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(\n ',',\n ).map((p) => p.trim());\n const optionPatterns = options?.allowedProxyUrls;\n\n // Combine both sources if both are specified\n const allowedPatterns = [...(optionPatterns || []), ...(envPatterns || [])];\n\n if (allowedPatterns.length === 0) {\n return new Response(\n `Forbidden: no allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS provided to withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Validate URL against allowed patterns to prevent SSRF attacks\n const isUrlAllowed = allowedPatterns.some((pattern) => {\n try {\n const regex = new RegExp(pattern);\n return regex.test(targetUrl);\n } catch (error) {\n console.error(\n `Invalid regex pattern in allowedProxyUrls: ${pattern}`,\n error,\n );\n return false;\n }\n });\n\n if (!isUrlAllowed) {\n return new Response(\n `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Fetch the remote resource\n const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });\n\n const contentType = response.headers.get('content-type');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: contentType ? { 'content-type': contentType } : undefined,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,uBAAmD;AACnD,2BAAmC;AAoBnC,eAAsB,kCACpB,YACA,SAC0B;AAC1B,QAAM,MAAM,IAAI,IAAI,YAAY,sBAAsB;AAEtD,MAAI,IAAI,aAAa,qDAAoC;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,IAAI,aAAa,IAAI,KAAK;AAC5C,MAAI,CAAC,WAAW;AACd,WAAO,IAAI,SAAS,wCAAwC;AAAA,MAC1D,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,QAAM,cAAc,QAAQ,IAAI,sCAAsC;AAAA,IACpE;AAAA,EACF,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACrB,QAAM,iBAAiB,SAAS;AAGhC,QAAM,kBAAkB,CAAC,GAAI,kBAAkB,CAAC,GAAI,GAAI,eAAe,CAAC,CAAE;AAE1E,MAAI,gBAAgB,WAAW,GAAG;AAChC,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAGA,QAAM,eAAe,gBAAgB,KAAK,CAAC,YAAY;AACrD,QAAI;AACF,YAAM,QAAQ,IAAI,OAAO,OAAO;AAChC,aAAO,MAAM,KAAK,SAAS;AAAA,IAC7B,SAAS,OAAP;AACA,cAAQ;AAAA,QACN,8CAA8C;AAAA,QAC9C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAED,MAAI,CAAC,cAAc;AACjB,WAAO,IAAI;AAAA,MACT,mCAAmC;AAAA,MACnC;AAAA,QACE,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAW,MAAM,MAAM,WAAW,EAAE,aAAS,yCAAmB,EAAE,CAAC;AAEzE,QAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;AAEvD,SAAO,IAAI,SAAS,SAAS,MAAM;AAAA,IACjC,QAAQ,SAAS;AAAA,IACjB,YAAY,SAAS;AAAA,IACrB,SAAS,cAAc,EAAE,gBAAgB,YAAY,IAAI;AAAA,EAC3D,CAAC;AACH;","names":[]}
@@ -1,12 +1,9 @@
1
- export { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from '../../internal/shared/ssr/fetch-with-protected-rc-fallback.js';
2
-
3
1
  /**
4
2
  * Proxy utilities for host applications that consume remote components.
5
3
  *
6
4
  * Hosts do NOT handle CORS - that's the remote's responsibility.
7
5
  * Hosts only handle protected fetch proxying.
8
6
  */
9
-
10
7
  interface HostProxyOptions {
11
8
  /**
12
9
  * List of allowed URL patterns (as regex strings) that can be proxied.
@@ -1,15 +1,30 @@
1
+ import { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from "#internal/shared/constants";
1
2
  import { remoteFetchHeaders } from "#internal/shared/ssr/fetch-headers";
2
- import { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from "#internal/shared/ssr/fetch-with-protected-rc-fallback";
3
- function isUrlAllowed(targetUrl, options) {
3
+ async function handleProtectedRemoteFetchRequest(requestUrl, options) {
4
+ const url = new URL(requestUrl, "https://fallback.com");
5
+ if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {
6
+ return null;
7
+ }
8
+ const targetUrl = url.searchParams.get("url");
9
+ if (!targetUrl) {
10
+ return new Response("Bad request, missing url query param", {
11
+ status: 400
12
+ });
13
+ }
4
14
  const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(
5
15
  ","
6
16
  ).map((p) => p.trim());
7
17
  const optionPatterns = options?.allowedProxyUrls;
8
18
  const allowedPatterns = [...optionPatterns || [], ...envPatterns || []];
9
19
  if (allowedPatterns.length === 0) {
10
- return false;
20
+ return new Response(
21
+ `Forbidden: no allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS provided to withRemoteComponentsHost.`,
22
+ {
23
+ status: 403
24
+ }
25
+ );
11
26
  }
12
- return allowedPatterns.some((pattern) => {
27
+ const isUrlAllowed = allowedPatterns.some((pattern) => {
13
28
  try {
14
29
  const regex = new RegExp(pattern);
15
30
  return regex.test(targetUrl);
@@ -21,19 +36,7 @@ function isUrlAllowed(targetUrl, options) {
21
36
  return false;
22
37
  }
23
38
  });
24
- }
25
- async function handleProtectedRemoteFetchRequest(requestUrl, options) {
26
- const url = new URL(requestUrl, "https://fallback.com");
27
- if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {
28
- return null;
29
- }
30
- const targetUrl = url.searchParams.get("url");
31
- if (!targetUrl) {
32
- return new Response("Bad request, missing url query param", {
33
- status: 400
34
- });
35
- }
36
- if (!isUrlAllowed(targetUrl, options)) {
39
+ if (!isUrlAllowed) {
37
40
  return new Response(
38
41
  `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,
39
42
  {
@@ -42,13 +45,14 @@ async function handleProtectedRemoteFetchRequest(requestUrl, options) {
42
45
  );
43
46
  }
44
47
  const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });
48
+ const contentType = response.headers.get("content-type");
45
49
  return new Response(response.body, {
46
50
  status: response.status,
47
- statusText: response.statusText
51
+ statusText: response.statusText,
52
+ headers: contentType ? { "content-type": contentType } : void 0
48
53
  });
49
54
  }
50
55
  export {
51
- RC_PROTECTED_REMOTE_FETCH_PATHNAME,
52
56
  handleProtectedRemoteFetchRequest
53
57
  };
54
58
  //# sourceMappingURL=proxy.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/shared/host/proxy.ts"],"sourcesContent":["/**\n * Proxy utilities for host applications that consume remote components.\n *\n * Hosts do NOT handle CORS - that's the remote's responsibility.\n * Hosts only handle protected fetch proxying.\n */\n\nimport { remoteFetchHeaders } from '#internal/shared/ssr/fetch-headers';\nimport { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from '#internal/shared/ssr/fetch-with-protected-rc-fallback';\n\nexport interface HostProxyOptions {\n /**\n * List of allowed URL patterns (as regex strings) that can be proxied.\n * These patterns are combined with REMOTE_COMPONENTS_ALLOWED_PROXY_URLS env var if both are set.\n * If neither is set, all URLs are blocked.\n */\n allowedProxyUrls?: string[];\n}\n\n/**\n * Validates if a URL is allowed to be proxied based on the allowed patterns.\n *\n * @param targetUrl - The URL to validate\n * @param options - Host proxy configuration options\n * @returns true if the URL is allowed, false otherwise\n */\nfunction isUrlAllowed(targetUrl: string, options?: HostProxyOptions): boolean {\n const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(\n ',',\n ).map((p) => p.trim());\n const optionPatterns = options?.allowedProxyUrls;\n\n // Combine both sources if both are specified\n const allowedPatterns = [...(optionPatterns || []), ...(envPatterns || [])];\n\n if (allowedPatterns.length === 0) {\n return false;\n }\n\n // Check if the URL matches any of the allowed patterns\n return allowedPatterns.some((pattern) => {\n try {\n const regex = new RegExp(pattern);\n return regex.test(targetUrl);\n } catch (error) {\n console.error(\n `Invalid regex pattern in allowedProxyUrls: ${pattern}`,\n error,\n );\n return false;\n }\n });\n}\n\n/**\n * Handles protected remote component fetch requests by proxying them with\n * authentication headers. This is needed for accessing Vercel-protected remote\n * component deployments from client-side code.\n *\n * @param requestUrl - The full request URL\n * @param options - Host proxy configuration options\n * @returns Response object if this is a protected fetch request, or null if not\n */\nexport async function handleProtectedRemoteFetchRequest(\n requestUrl: string,\n options?: HostProxyOptions,\n): Promise<Response | null> {\n const url = new URL(requestUrl, 'https://fallback.com');\n\n if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {\n return null;\n }\n\n const targetUrl = url.searchParams.get('url');\n if (!targetUrl) {\n return new Response('Bad request, missing url query param', {\n status: 400,\n });\n }\n\n // Validate URL against allowed patterns to prevent SSRF attacks\n if (!isUrlAllowed(targetUrl, options)) {\n return new Response(\n `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Fetch the remote resource\n const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });\n\n // Create new headers without content-encoding to avoid decoding errors\n // (Node.js fetch auto-decodes but keeps the header, causing browser issues)\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n });\n}\n\nexport { RC_PROTECTED_REMOTE_FETCH_PATHNAME };\n"],"mappings":"AAOA,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AAkBnD,SAAS,aAAa,WAAmB,SAAqC;AAC5E,QAAM,cAAc,QAAQ,IAAI,sCAAsC;AAAA,IACpE;AAAA,EACF,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACrB,QAAM,iBAAiB,SAAS;AAGhC,QAAM,kBAAkB,CAAC,GAAI,kBAAkB,CAAC,GAAI,GAAI,eAAe,CAAC,CAAE;AAE1E,MAAI,gBAAgB,WAAW,GAAG;AAChC,WAAO;AAAA,EACT;AAGA,SAAO,gBAAgB,KAAK,CAAC,YAAY;AACvC,QAAI;AACF,YAAM,QAAQ,IAAI,OAAO,OAAO;AAChC,aAAO,MAAM,KAAK,SAAS;AAAA,IAC7B,SAAS,OAAP;AACA,cAAQ;AAAA,QACN,8CAA8C;AAAA,QAC9C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAWA,eAAsB,kCACpB,YACA,SAC0B;AAC1B,QAAM,MAAM,IAAI,IAAI,YAAY,sBAAsB;AAEtD,MAAI,IAAI,aAAa,oCAAoC;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,IAAI,aAAa,IAAI,KAAK;AAC5C,MAAI,CAAC,WAAW;AACd,WAAO,IAAI,SAAS,wCAAwC;AAAA,MAC1D,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,aAAa,WAAW,OAAO,GAAG;AACrC,WAAO,IAAI;AAAA,MACT,mCAAmC;AAAA,MACnC;AAAA,QACE,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAW,MAAM,MAAM,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;AAKzE,SAAO,IAAI,SAAS,SAAS,MAAM;AAAA,IACjC,QAAQ,SAAS;AAAA,IACjB,YAAY,SAAS;AAAA,EACvB,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../../src/shared/host/proxy.ts"],"sourcesContent":["/**\n * Proxy utilities for host applications that consume remote components.\n *\n * Hosts do NOT handle CORS - that's the remote's responsibility.\n * Hosts only handle protected fetch proxying.\n */\n\nimport { RC_PROTECTED_REMOTE_FETCH_PATHNAME } from '#internal/shared/constants';\nimport { remoteFetchHeaders } from '#internal/shared/ssr/fetch-headers';\n\nexport interface HostProxyOptions {\n /**\n * List of allowed URL patterns (as regex strings) that can be proxied.\n * These patterns are combined with REMOTE_COMPONENTS_ALLOWED_PROXY_URLS env var if both are set.\n * If neither is set, all URLs are blocked.\n */\n allowedProxyUrls?: string[];\n}\n\n/**\n * Handles protected remote component fetch requests by proxying them with\n * authentication headers. This is needed for accessing Vercel-protected remote\n * component deployments from client-side code.\n *\n * @param requestUrl - The full request URL\n * @param options - Host proxy configuration options\n * @returns Response object if this is a protected fetch request, or null if not\n */\nexport async function handleProtectedRemoteFetchRequest(\n requestUrl: string,\n options?: HostProxyOptions,\n): Promise<Response | null> {\n const url = new URL(requestUrl, 'https://fallback.com');\n\n if (url.pathname !== RC_PROTECTED_REMOTE_FETCH_PATHNAME) {\n return null;\n }\n\n const targetUrl = url.searchParams.get('url');\n if (!targetUrl) {\n return new Response('Bad request, missing url query param', {\n status: 400,\n });\n }\n\n const envPatterns = process.env.REMOTE_COMPONENTS_ALLOWED_PROXY_URLS?.split(\n ',',\n ).map((p) => p.trim());\n const optionPatterns = options?.allowedProxyUrls;\n\n // Combine both sources if both are specified\n const allowedPatterns = [...(optionPatterns || []), ...(envPatterns || [])];\n\n if (allowedPatterns.length === 0) {\n return new Response(\n `Forbidden: no allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS provided to withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Validate URL against allowed patterns to prevent SSRF attacks\n const isUrlAllowed = allowedPatterns.some((pattern) => {\n try {\n const regex = new RegExp(pattern);\n return regex.test(targetUrl);\n } catch (error) {\n console.error(\n `Invalid regex pattern in allowedProxyUrls: ${pattern}`,\n error,\n );\n return false;\n }\n });\n\n if (!isUrlAllowed) {\n return new Response(\n `Forbidden: remote component URL ${url} does not match any allowedProxyUrls or REMOTE_COMPONENTS_ALLOWED_PROXY_URLS in withRemoteComponentsHost.`,\n {\n status: 403,\n },\n );\n }\n\n // Fetch the remote resource\n const response = await fetch(targetUrl, { headers: remoteFetchHeaders() });\n\n const contentType = response.headers.get('content-type');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: contentType ? { 'content-type': contentType } : undefined,\n });\n}\n"],"mappings":"AAOA,SAAS,0CAA0C;AACnD,SAAS,0BAA0B;AAoBnC,eAAsB,kCACpB,YACA,SAC0B;AAC1B,QAAM,MAAM,IAAI,IAAI,YAAY,sBAAsB;AAEtD,MAAI,IAAI,aAAa,oCAAoC;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,IAAI,aAAa,IAAI,KAAK;AAC5C,MAAI,CAAC,WAAW;AACd,WAAO,IAAI,SAAS,wCAAwC;AAAA,MAC1D,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,QAAM,cAAc,QAAQ,IAAI,sCAAsC;AAAA,IACpE;AAAA,EACF,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACrB,QAAM,iBAAiB,SAAS;AAGhC,QAAM,kBAAkB,CAAC,GAAI,kBAAkB,CAAC,GAAI,GAAI,eAAe,CAAC,CAAE;AAE1E,MAAI,gBAAgB,WAAW,GAAG;AAChC,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAGA,QAAM,eAAe,gBAAgB,KAAK,CAAC,YAAY;AACrD,QAAI;AACF,YAAM,QAAQ,IAAI,OAAO,OAAO;AAChC,aAAO,MAAM,KAAK,SAAS;AAAA,IAC7B,SAAS,OAAP;AACA,cAAQ;AAAA,QACN,8CAA8C;AAAA,QAC9C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAED,MAAI,CAAC,cAAc;AACjB,WAAO,IAAI;AAAA,MACT,mCAAmC;AAAA,MACnC;AAAA,QACE,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAW,MAAM,MAAM,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;AAEzE,QAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;AAEvD,SAAO,IAAI,SAAS,SAAS,MAAM;AAAA,IACjC,QAAQ,SAAS;AAAA,IACjB,YAAY,SAAS;AAAA,IACrB,SAAS,cAAc,EAAE,gBAAgB,YAAY,IAAI;AAAA,EAC3D,CAAC;AACH;","names":[]}
@@ -20,6 +20,7 @@ var proxy_exports = {};
20
20
  __export(proxy_exports, {
21
21
  getCorsHeaders: () => getCorsHeaders,
22
22
  getHeader: () => getHeader,
23
+ getSecurityHeaders: () => getSecurityHeaders,
23
24
  handleCorsPreflightRequest: () => handleCorsPreflightRequest
24
25
  });
25
26
  module.exports = __toCommonJS(proxy_exports);
@@ -52,6 +53,12 @@ function getCorsHeaders(options, requestHeaders) {
52
53
  } : {};
53
54
  return CORS_HEADERS;
54
55
  }
56
+ function getSecurityHeaders() {
57
+ return {
58
+ "Content-Security-Policy": "frame-ancestors 'none'",
59
+ "X-Frame-Options": "DENY"
60
+ };
61
+ }
55
62
  function handleCorsPreflightRequest(method, headers, options) {
56
63
  if (method !== "OPTIONS" || options === false) {
57
64
  return null;
@@ -59,13 +66,14 @@ function handleCorsPreflightRequest(method, headers, options) {
59
66
  const corsHeaders = getCorsHeaders(options, headers);
60
67
  return new Response(void 0, {
61
68
  status: 200,
62
- headers: corsHeaders
69
+ headers: { ...corsHeaders, ...getSecurityHeaders() }
63
70
  });
64
71
  }
65
72
  // Annotate the CommonJS export names for ESM import in node:
66
73
  0 && (module.exports = {
67
74
  getCorsHeaders,
68
75
  getHeader,
76
+ getSecurityHeaders,
69
77
  handleCorsPreflightRequest
70
78
  });
71
79
  //# sourceMappingURL=proxy.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/shared/remote/proxy.ts"],"sourcesContent":["/**\n * Proxy utilities for remote applications that expose components to hosts.\n */\n\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport interface RemoteComponentProxyOptions {\n cors?:\n | {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n maxAge?: string;\n }\n | false;\n}\n\n/**\n * Gets a header value from either a Headers object or a plain object.\n */\nexport function getHeader(\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n const value = headers[name];\n // IncomingHttpHeaders can have string | string[] | undefined\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * Computes CORS headers based on the provided options and request headers.\n *\n * @param options - CORS configuration options\n * @param requestHeaders - Headers from the incoming request (can be a Headers object or a plain object)\n * @returns Object containing CORS headers to be added to the response\n */\nexport function getCorsHeaders(\n options: RemoteComponentProxyOptions['cors'],\n requestHeaders: Record<string, string> | Headers | IncomingHttpHeaders,\n): Record<string, string> {\n if (options === false) {\n return {};\n }\n\n const originHeader = getHeader(requestHeaders, 'origin');\n const refererHeader = getHeader(requestHeaders, 'referer');\n const origin =\n originHeader ?? (refererHeader ? new URL(refererHeader).origin : '*');\n\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((allowedOrigin) => allowedOrigin.trim());\n\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n const allowedHeaders =\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n getHeader(requestHeaders, 'access-control-request-headers');\n\n const CORS_HEADERS = (\n isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n ...(allowedHeaders\n ? { 'Access-Control-Allow-Headers': allowedHeaders }\n : {}),\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS ||\n options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n 'Access-Control-Max-Age': options?.maxAge || '600',\n Vary: 'Origin',\n }\n : {}\n ) as Record<string, string>;\n\n return CORS_HEADERS;\n}\n\n/**\n * Handles CORS preflight OPTIONS requests.\n *\n * @param method - The HTTP method of the incoming request\n * @param headers - Headers from the incoming request (can be a Headers object or a plain object)\n * @param options - CORS configuration options\n * @returns Response object for the preflight request, or null if not a preflight\n */\nexport function handleCorsPreflightRequest(\n method: string,\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n options?: RemoteComponentProxyOptions['cors'],\n): Response | null {\n if (method !== 'OPTIONS' || options === false) {\n return null;\n }\n\n const corsHeaders = getCorsHeaders(options, headers);\n\n return new Response(undefined, {\n status: 200,\n headers: corsHeaders,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBO,SAAS,UACd,SACA,MACoB;AACpB,MAAI,mBAAmB,SAAS;AAC9B,WAAO,QAAQ,IAAI,IAAI,KAAK;AAAA,EAC9B;AACA,QAAM,QAAQ,QAAQ,IAAI;AAE1B,SAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC3C;AASO,SAAS,eACd,SACA,gBACwB;AACxB,MAAI,YAAY,OAAO;AACrB,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,eAAe,UAAU,gBAAgB,QAAQ;AACvD,QAAM,gBAAgB,UAAU,gBAAgB,SAAS;AACzD,QAAM,SACJ,iBAAiB,gBAAgB,IAAI,IAAI,aAAa,EAAE,SAAS;AAEnE,QAAM,mBACJ,QAAQ,IAAI,sCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,KAAK,GAAG,IACvB,SAAS,WACb,KAEC,MAAM,GAAG,EACT,IAAI,CAAC,kBAAkB,cAAc,KAAK,CAAC;AAE9C,QAAM,YACJ,gBAAgB,SAAS,GAAG,KAAK,gBAAgB,SAAS,MAAM;AAElE,QAAM,iBACJ,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,OAAO,IAC3B,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC7C,SAAS,YACb,UAAU,gBAAgB,gCAAgC;AAE5D,QAAM,eACJ,YACI;AAAA,IACE,+BAA+B;AAAA,IAC/B,gCACE,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC5C,SAAS,WACb;AAAA,IACF,GAAI,iBACA,EAAE,gCAAgC,eAAe,IACjD,CAAC;AAAA,IACL,GAAI,QAAQ,IAAI,uCAChB,SAAS,cACL;AAAA,MACE,oCACE,QAAQ,IAAI,uCAAuC;AAAA,IACvD,IACA,CAAC;AAAA,IACL,0BAA0B,SAAS,UAAU;AAAA,IAC7C,MAAM;AAAA,EACR,IACA,CAAC;AAGP,SAAO;AACT;AAUO,SAAS,2BACd,QACA,SACA,SACiB;AACjB,MAAI,WAAW,aAAa,YAAY,OAAO;AAC7C,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,eAAe,SAAS,OAAO;AAEnD,SAAO,IAAI,SAAS,QAAW;AAAA,IAC7B,QAAQ;AAAA,IACR,SAAS;AAAA,EACX,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../../src/shared/remote/proxy.ts"],"sourcesContent":["/**\n * Proxy utilities for remote applications that expose components to hosts.\n */\n\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport interface RemoteComponentProxyOptions {\n cors?:\n | {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n maxAge?: string;\n }\n | false;\n}\n\n/**\n * Gets a header value from either a Headers object or a plain object.\n */\nexport function getHeader(\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n const value = headers[name];\n // IncomingHttpHeaders can have string | string[] | undefined\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * Computes CORS headers based on the provided options and request headers.\n *\n * @param options - CORS configuration options\n * @param requestHeaders - Headers from the incoming request (can be a Headers object or a plain object)\n * @returns Object containing CORS headers to be added to the response\n */\nexport function getCorsHeaders(\n options: RemoteComponentProxyOptions['cors'],\n requestHeaders: Record<string, string> | Headers | IncomingHttpHeaders,\n): Record<string, string> {\n if (options === false) {\n return {};\n }\n\n const originHeader = getHeader(requestHeaders, 'origin');\n const refererHeader = getHeader(requestHeaders, 'referer');\n const origin =\n originHeader ?? (refererHeader ? new URL(refererHeader).origin : '*');\n\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((allowedOrigin) => allowedOrigin.trim());\n\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n const allowedHeaders =\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n getHeader(requestHeaders, 'access-control-request-headers');\n\n const CORS_HEADERS = (\n isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n ...(allowedHeaders\n ? { 'Access-Control-Allow-Headers': allowedHeaders }\n : {}),\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS ||\n options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n 'Access-Control-Max-Age': options?.maxAge || '600',\n Vary: 'Origin',\n }\n : {}\n ) as Record<string, string>;\n\n return CORS_HEADERS;\n}\n\n/**\n * Returns security headers that prevent the remote component pages from being\n * embedded in frames. Remote components are fetched via HTTP and rendered\n * directly into the host's DOM — they should never be loaded in an iframe.\n */\nexport function getSecurityHeaders(): Record<string, string> {\n return {\n 'Content-Security-Policy': \"frame-ancestors 'none'\",\n 'X-Frame-Options': 'DENY',\n };\n}\n\n/**\n * Handles CORS preflight OPTIONS requests.\n *\n * @param method - The HTTP method of the incoming request\n * @param headers - Headers from the incoming request (can be a Headers object or a plain object)\n * @param options - CORS configuration options\n * @returns Response object for the preflight request, or null if not a preflight\n */\nexport function handleCorsPreflightRequest(\n method: string,\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n options?: RemoteComponentProxyOptions['cors'],\n): Response | null {\n if (method !== 'OPTIONS' || options === false) {\n return null;\n }\n\n const corsHeaders = getCorsHeaders(options, headers);\n\n return new Response(undefined, {\n status: 200,\n headers: { ...corsHeaders, ...getSecurityHeaders() },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBO,SAAS,UACd,SACA,MACoB;AACpB,MAAI,mBAAmB,SAAS;AAC9B,WAAO,QAAQ,IAAI,IAAI,KAAK;AAAA,EAC9B;AACA,QAAM,QAAQ,QAAQ,IAAI;AAE1B,SAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC3C;AASO,SAAS,eACd,SACA,gBACwB;AACxB,MAAI,YAAY,OAAO;AACrB,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,eAAe,UAAU,gBAAgB,QAAQ;AACvD,QAAM,gBAAgB,UAAU,gBAAgB,SAAS;AACzD,QAAM,SACJ,iBAAiB,gBAAgB,IAAI,IAAI,aAAa,EAAE,SAAS;AAEnE,QAAM,mBACJ,QAAQ,IAAI,sCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,KAAK,GAAG,IACvB,SAAS,WACb,KAEC,MAAM,GAAG,EACT,IAAI,CAAC,kBAAkB,cAAc,KAAK,CAAC;AAE9C,QAAM,YACJ,gBAAgB,SAAS,GAAG,KAAK,gBAAgB,SAAS,MAAM;AAElE,QAAM,iBACJ,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,OAAO,IAC3B,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC7C,SAAS,YACb,UAAU,gBAAgB,gCAAgC;AAE5D,QAAM,eACJ,YACI;AAAA,IACE,+BAA+B;AAAA,IAC/B,gCACE,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC5C,SAAS,WACb;AAAA,IACF,GAAI,iBACA,EAAE,gCAAgC,eAAe,IACjD,CAAC;AAAA,IACL,GAAI,QAAQ,IAAI,uCAChB,SAAS,cACL;AAAA,MACE,oCACE,QAAQ,IAAI,uCAAuC;AAAA,IACvD,IACA,CAAC;AAAA,IACL,0BAA0B,SAAS,UAAU;AAAA,IAC7C,MAAM;AAAA,EACR,IACA,CAAC;AAGP,SAAO;AACT;AAOO,SAAS,qBAA6C;AAC3D,SAAO;AAAA,IACL,2BAA2B;AAAA,IAC3B,mBAAmB;AAAA,EACrB;AACF;AAUO,SAAS,2BACd,QACA,SACA,SACiB;AACjB,MAAI,WAAW,aAAa,YAAY,OAAO;AAC7C,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,eAAe,SAAS,OAAO;AAEnD,SAAO,IAAI,SAAS,QAAW;AAAA,IAC7B,QAAQ;AAAA,IACR,SAAS,EAAE,GAAG,aAAa,GAAG,mBAAmB,EAAE;AAAA,EACrD,CAAC;AACH;","names":[]}
@@ -25,6 +25,12 @@ declare function getHeader(headers: Record<string, string> | Headers | IncomingH
25
25
  * @returns Object containing CORS headers to be added to the response
26
26
  */
27
27
  declare function getCorsHeaders(options: RemoteComponentProxyOptions['cors'], requestHeaders: Record<string, string> | Headers | IncomingHttpHeaders): Record<string, string>;
28
+ /**
29
+ * Returns security headers that prevent the remote component pages from being
30
+ * embedded in frames. Remote components are fetched via HTTP and rendered
31
+ * directly into the host's DOM — they should never be loaded in an iframe.
32
+ */
33
+ declare function getSecurityHeaders(): Record<string, string>;
28
34
  /**
29
35
  * Handles CORS preflight OPTIONS requests.
30
36
  *
@@ -35,4 +41,4 @@ declare function getCorsHeaders(options: RemoteComponentProxyOptions['cors'], re
35
41
  */
36
42
  declare function handleCorsPreflightRequest(method: string, headers: Record<string, string> | Headers | IncomingHttpHeaders, options?: RemoteComponentProxyOptions['cors']): Response | null;
37
43
 
38
- export { RemoteComponentProxyOptions, getCorsHeaders, getHeader, handleCorsPreflightRequest };
44
+ export { RemoteComponentProxyOptions, getCorsHeaders, getHeader, getSecurityHeaders, handleCorsPreflightRequest };
@@ -27,6 +27,12 @@ function getCorsHeaders(options, requestHeaders) {
27
27
  } : {};
28
28
  return CORS_HEADERS;
29
29
  }
30
+ function getSecurityHeaders() {
31
+ return {
32
+ "Content-Security-Policy": "frame-ancestors 'none'",
33
+ "X-Frame-Options": "DENY"
34
+ };
35
+ }
30
36
  function handleCorsPreflightRequest(method, headers, options) {
31
37
  if (method !== "OPTIONS" || options === false) {
32
38
  return null;
@@ -34,12 +40,13 @@ function handleCorsPreflightRequest(method, headers, options) {
34
40
  const corsHeaders = getCorsHeaders(options, headers);
35
41
  return new Response(void 0, {
36
42
  status: 200,
37
- headers: corsHeaders
43
+ headers: { ...corsHeaders, ...getSecurityHeaders() }
38
44
  });
39
45
  }
40
46
  export {
41
47
  getCorsHeaders,
42
48
  getHeader,
49
+ getSecurityHeaders,
43
50
  handleCorsPreflightRequest
44
51
  };
45
52
  //# sourceMappingURL=proxy.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/shared/remote/proxy.ts"],"sourcesContent":["/**\n * Proxy utilities for remote applications that expose components to hosts.\n */\n\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport interface RemoteComponentProxyOptions {\n cors?:\n | {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n maxAge?: string;\n }\n | false;\n}\n\n/**\n * Gets a header value from either a Headers object or a plain object.\n */\nexport function getHeader(\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n const value = headers[name];\n // IncomingHttpHeaders can have string | string[] | undefined\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * Computes CORS headers based on the provided options and request headers.\n *\n * @param options - CORS configuration options\n * @param requestHeaders - Headers from the incoming request (can be a Headers object or a plain object)\n * @returns Object containing CORS headers to be added to the response\n */\nexport function getCorsHeaders(\n options: RemoteComponentProxyOptions['cors'],\n requestHeaders: Record<string, string> | Headers | IncomingHttpHeaders,\n): Record<string, string> {\n if (options === false) {\n return {};\n }\n\n const originHeader = getHeader(requestHeaders, 'origin');\n const refererHeader = getHeader(requestHeaders, 'referer');\n const origin =\n originHeader ?? (refererHeader ? new URL(refererHeader).origin : '*');\n\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((allowedOrigin) => allowedOrigin.trim());\n\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n const allowedHeaders =\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n getHeader(requestHeaders, 'access-control-request-headers');\n\n const CORS_HEADERS = (\n isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n ...(allowedHeaders\n ? { 'Access-Control-Allow-Headers': allowedHeaders }\n : {}),\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS ||\n options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n 'Access-Control-Max-Age': options?.maxAge || '600',\n Vary: 'Origin',\n }\n : {}\n ) as Record<string, string>;\n\n return CORS_HEADERS;\n}\n\n/**\n * Handles CORS preflight OPTIONS requests.\n *\n * @param method - The HTTP method of the incoming request\n * @param headers - Headers from the incoming request (can be a Headers object or a plain object)\n * @param options - CORS configuration options\n * @returns Response object for the preflight request, or null if not a preflight\n */\nexport function handleCorsPreflightRequest(\n method: string,\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n options?: RemoteComponentProxyOptions['cors'],\n): Response | null {\n if (method !== 'OPTIONS' || options === false) {\n return null;\n }\n\n const corsHeaders = getCorsHeaders(options, headers);\n\n return new Response(undefined, {\n status: 200,\n headers: corsHeaders,\n });\n}\n"],"mappings":"AAqBO,SAAS,UACd,SACA,MACoB;AACpB,MAAI,mBAAmB,SAAS;AAC9B,WAAO,QAAQ,IAAI,IAAI,KAAK;AAAA,EAC9B;AACA,QAAM,QAAQ,QAAQ,IAAI;AAE1B,SAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC3C;AASO,SAAS,eACd,SACA,gBACwB;AACxB,MAAI,YAAY,OAAO;AACrB,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,eAAe,UAAU,gBAAgB,QAAQ;AACvD,QAAM,gBAAgB,UAAU,gBAAgB,SAAS;AACzD,QAAM,SACJ,iBAAiB,gBAAgB,IAAI,IAAI,aAAa,EAAE,SAAS;AAEnE,QAAM,mBACJ,QAAQ,IAAI,sCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,KAAK,GAAG,IACvB,SAAS,WACb,KAEC,MAAM,GAAG,EACT,IAAI,CAAC,kBAAkB,cAAc,KAAK,CAAC;AAE9C,QAAM,YACJ,gBAAgB,SAAS,GAAG,KAAK,gBAAgB,SAAS,MAAM;AAElE,QAAM,iBACJ,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,OAAO,IAC3B,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC7C,SAAS,YACb,UAAU,gBAAgB,gCAAgC;AAE5D,QAAM,eACJ,YACI;AAAA,IACE,+BAA+B;AAAA,IAC/B,gCACE,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC5C,SAAS,WACb;AAAA,IACF,GAAI,iBACA,EAAE,gCAAgC,eAAe,IACjD,CAAC;AAAA,IACL,GAAI,QAAQ,IAAI,uCAChB,SAAS,cACL;AAAA,MACE,oCACE,QAAQ,IAAI,uCAAuC;AAAA,IACvD,IACA,CAAC;AAAA,IACL,0BAA0B,SAAS,UAAU;AAAA,IAC7C,MAAM;AAAA,EACR,IACA,CAAC;AAGP,SAAO;AACT;AAUO,SAAS,2BACd,QACA,SACA,SACiB;AACjB,MAAI,WAAW,aAAa,YAAY,OAAO;AAC7C,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,eAAe,SAAS,OAAO;AAEnD,SAAO,IAAI,SAAS,QAAW;AAAA,IAC7B,QAAQ;AAAA,IACR,SAAS;AAAA,EACX,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../../src/shared/remote/proxy.ts"],"sourcesContent":["/**\n * Proxy utilities for remote applications that expose components to hosts.\n */\n\nimport type { IncomingHttpHeaders } from 'node:http';\n\nexport interface RemoteComponentProxyOptions {\n cors?:\n | {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n maxAge?: string;\n }\n | false;\n}\n\n/**\n * Gets a header value from either a Headers object or a plain object.\n */\nexport function getHeader(\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n name: string,\n): string | undefined {\n if (headers instanceof Headers) {\n return headers.get(name) ?? undefined;\n }\n const value = headers[name];\n // IncomingHttpHeaders can have string | string[] | undefined\n return Array.isArray(value) ? value[0] : value;\n}\n\n/**\n * Computes CORS headers based on the provided options and request headers.\n *\n * @param options - CORS configuration options\n * @param requestHeaders - Headers from the incoming request (can be a Headers object or a plain object)\n * @returns Object containing CORS headers to be added to the response\n */\nexport function getCorsHeaders(\n options: RemoteComponentProxyOptions['cors'],\n requestHeaders: Record<string, string> | Headers | IncomingHttpHeaders,\n): Record<string, string> {\n if (options === false) {\n return {};\n }\n\n const originHeader = getHeader(requestHeaders, 'origin');\n const refererHeader = getHeader(requestHeaders, 'referer');\n const origin =\n originHeader ?? (refererHeader ? new URL(refererHeader).origin : '*');\n\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((allowedOrigin) => allowedOrigin.trim());\n\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n const allowedHeaders =\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n getHeader(requestHeaders, 'access-control-request-headers');\n\n const CORS_HEADERS = (\n isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n ...(allowedHeaders\n ? { 'Access-Control-Allow-Headers': allowedHeaders }\n : {}),\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS ||\n options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n 'Access-Control-Max-Age': options?.maxAge || '600',\n Vary: 'Origin',\n }\n : {}\n ) as Record<string, string>;\n\n return CORS_HEADERS;\n}\n\n/**\n * Returns security headers that prevent the remote component pages from being\n * embedded in frames. Remote components are fetched via HTTP and rendered\n * directly into the host's DOM — they should never be loaded in an iframe.\n */\nexport function getSecurityHeaders(): Record<string, string> {\n return {\n 'Content-Security-Policy': \"frame-ancestors 'none'\",\n 'X-Frame-Options': 'DENY',\n };\n}\n\n/**\n * Handles CORS preflight OPTIONS requests.\n *\n * @param method - The HTTP method of the incoming request\n * @param headers - Headers from the incoming request (can be a Headers object or a plain object)\n * @param options - CORS configuration options\n * @returns Response object for the preflight request, or null if not a preflight\n */\nexport function handleCorsPreflightRequest(\n method: string,\n headers: Record<string, string> | Headers | IncomingHttpHeaders,\n options?: RemoteComponentProxyOptions['cors'],\n): Response | null {\n if (method !== 'OPTIONS' || options === false) {\n return null;\n }\n\n const corsHeaders = getCorsHeaders(options, headers);\n\n return new Response(undefined, {\n status: 200,\n headers: { ...corsHeaders, ...getSecurityHeaders() },\n });\n}\n"],"mappings":"AAqBO,SAAS,UACd,SACA,MACoB;AACpB,MAAI,mBAAmB,SAAS;AAC9B,WAAO,QAAQ,IAAI,IAAI,KAAK;AAAA,EAC9B;AACA,QAAM,QAAQ,QAAQ,IAAI;AAE1B,SAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC3C;AASO,SAAS,eACd,SACA,gBACwB;AACxB,MAAI,YAAY,OAAO;AACrB,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,eAAe,UAAU,gBAAgB,QAAQ;AACvD,QAAM,gBAAgB,UAAU,gBAAgB,SAAS;AACzD,QAAM,SACJ,iBAAiB,gBAAgB,IAAI,IAAI,aAAa,EAAE,SAAS;AAEnE,QAAM,mBACJ,QAAQ,IAAI,sCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,KAAK,GAAG,IACvB,SAAS,WACb,KAEC,MAAM,GAAG,EACT,IAAI,CAAC,kBAAkB,cAAc,KAAK,CAAC;AAE9C,QAAM,YACJ,gBAAgB,SAAS,GAAG,KAAK,gBAAgB,SAAS,MAAM;AAElE,QAAM,iBACJ,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,OAAO,IAC3B,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC7C,SAAS,YACb,UAAU,gBAAgB,gCAAgC;AAE5D,QAAM,eACJ,YACI;AAAA,IACE,+BAA+B;AAAA,IAC/B,gCACE,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC5C,SAAS,WACb;AAAA,IACF,GAAI,iBACA,EAAE,gCAAgC,eAAe,IACjD,CAAC;AAAA,IACL,GAAI,QAAQ,IAAI,uCAChB,SAAS,cACL;AAAA,MACE,oCACE,QAAQ,IAAI,uCAAuC;AAAA,IACvD,IACA,CAAC;AAAA,IACL,0BAA0B,SAAS,UAAU;AAAA,IAC7C,MAAM;AAAA,EACR,IACA,CAAC;AAGP,SAAO;AACT;AAOO,SAAS,qBAA6C;AAC3D,SAAO;AAAA,IACL,2BAA2B;AAAA,IAC3B,mBAAmB;AAAA,EACrB;AACF;AAUO,SAAS,2BACd,QACA,SACA,SACiB;AACjB,MAAI,WAAW,aAAa,YAAY,OAAO;AAC7C,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,eAAe,SAAS,OAAO;AAEnD,SAAO,IAAI,SAAS,QAAW;AAAA,IAC7B,QAAQ;AAAA,IACR,SAAS,EAAE,GAAG,aAAa,GAAG,mBAAmB,EAAE;AAAA,EACrD,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-components",
3
- "version": "0.0.50",
3
+ "version": "0.0.51",
4
4
  "private": false,
5
5
  "description": "Compose remote components at runtime between microfrontends applications.",
6
6
  "keywords": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/shared/ssr/fetch-with-protected-rc-fallback.ts"],"sourcesContent":["import { isAbortError } from '#internal/shared/utils/abort';\nimport { logError, logInfo } from '#internal/shared/utils/logger';\n\nexport const RC_PROTECTED_REMOTE_FETCH_PATHNAME = '/rc-fetch-protected-remote';\n\n/**\n * When a vercel host preview fetches a vercel protected remote preview on the\n * client, the request will reject as it's not possible to authenticate for the\n * protected deployment in a client side fetch - cookies cannot be shared across\n * domains. To enable previews, this request is proxied via the host where the\n * process.env.VERCEL_AUTOMATION_BYPASS_SECRET will be added.\n *\n * @param url - The URL to fetch\n * @param init - Fetch init options (should include signal for abort support)\n */\nexport async function fetchWithProtectedRcFallback(\n url: URL | string,\n init?: RequestInit,\n): Promise<Response> {\n try {\n const res = await fetch(url, init);\n return res;\n } catch (error) {\n // Re-throw AbortError immediately - don't try fallback for cancelled requests\n if (isAbortError(error)) {\n throw error;\n }\n\n if (\n typeof document === 'object' &&\n typeof document.location === 'object' &&\n document.location.origin !== new URL(url).origin\n ) {\n logInfo(\n 'FetchRemoteComponent',\n 'Request failed due to CORS, attempting to fetch it via the withRemoteComponentsHost proxy.',\n );\n // Pass signal to proxy fetch as well so it can be cancelled\n const proxiedRes = await fetch(\n `${RC_PROTECTED_REMOTE_FETCH_PATHNAME}?url=${url}`,\n init?.signal ? { signal: init.signal } : undefined,\n );\n if (proxiedRes.status === 200) {\n return proxiedRes;\n } else {\n logError(\n 'FetchRemoteComponent',\n `Could not proxy remote: [response status ${\n proxiedRes.status\n }] ${await proxiedRes.text()}`,\n );\n }\n }\n\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA6B;AAC7B,oBAAkC;AAE3B,MAAM,qCAAqC;AAYlD,eAAsB,6BACpB,KACA,MACmB;AACnB,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,KAAK,IAAI;AACjC,WAAO;AAAA,EACT,SAAS,OAAP;AAEA,YAAI,2BAAa,KAAK,GAAG;AACvB,YAAM;AAAA,IACR;AAEA,QACE,OAAO,aAAa,YACpB,OAAO,SAAS,aAAa,YAC7B,SAAS,SAAS,WAAW,IAAI,IAAI,GAAG,EAAE,QAC1C;AACA;AAAA,QACE;AAAA,QACA;AAAA,MACF;AAEA,YAAM,aAAa,MAAM;AAAA,QACvB,GAAG,0CAA0C;AAAA,QAC7C,MAAM,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI;AAAA,MAC3C;AACA,UAAI,WAAW,WAAW,KAAK;AAC7B,eAAO;AAAA,MACT,OAAO;AACL;AAAA,UACE;AAAA,UACA,4CACE,WAAW,WACR,MAAM,WAAW,KAAK;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/shared/ssr/fetch-with-protected-rc-fallback.ts"],"sourcesContent":["import { isAbortError } from '#internal/shared/utils/abort';\nimport { logError, logInfo } from '#internal/shared/utils/logger';\n\nexport const RC_PROTECTED_REMOTE_FETCH_PATHNAME = '/rc-fetch-protected-remote';\n\n/**\n * When a vercel host preview fetches a vercel protected remote preview on the\n * client, the request will reject as it's not possible to authenticate for the\n * protected deployment in a client side fetch - cookies cannot be shared across\n * domains. To enable previews, this request is proxied via the host where the\n * process.env.VERCEL_AUTOMATION_BYPASS_SECRET will be added.\n *\n * @param url - The URL to fetch\n * @param init - Fetch init options (should include signal for abort support)\n */\nexport async function fetchWithProtectedRcFallback(\n url: URL | string,\n init?: RequestInit,\n): Promise<Response> {\n try {\n const res = await fetch(url, init);\n return res;\n } catch (error) {\n // Re-throw AbortError immediately - don't try fallback for cancelled requests\n if (isAbortError(error)) {\n throw error;\n }\n\n if (\n typeof document === 'object' &&\n typeof document.location === 'object' &&\n document.location.origin !== new URL(url).origin\n ) {\n logInfo(\n 'FetchRemoteComponent',\n 'Request failed due to CORS, attempting to fetch it via the withRemoteComponentsHost proxy.',\n );\n // Pass signal to proxy fetch as well so it can be cancelled\n const proxiedRes = await fetch(\n `${RC_PROTECTED_REMOTE_FETCH_PATHNAME}?url=${url}`,\n init?.signal ? { signal: init.signal } : undefined,\n );\n if (proxiedRes.status === 200) {\n return proxiedRes;\n } else {\n logError(\n 'FetchRemoteComponent',\n `Could not proxy remote: [response status ${\n proxiedRes.status\n }] ${await proxiedRes.text()}`,\n );\n }\n }\n\n throw error;\n }\n}\n"],"mappings":"AAAA,SAAS,oBAAoB;AAC7B,SAAS,UAAU,eAAe;AAE3B,MAAM,qCAAqC;AAYlD,eAAsB,6BACpB,KACA,MACmB;AACnB,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,KAAK,IAAI;AACjC,WAAO;AAAA,EACT,SAAS,OAAP;AAEA,QAAI,aAAa,KAAK,GAAG;AACvB,YAAM;AAAA,IACR;AAEA,QACE,OAAO,aAAa,YACpB,OAAO,SAAS,aAAa,YAC7B,SAAS,SAAS,WAAW,IAAI,IAAI,GAAG,EAAE,QAC1C;AACA;AAAA,QACE;AAAA,QACA;AAAA,MACF;AAEA,YAAM,aAAa,MAAM;AAAA,QACvB,GAAG,0CAA0C;AAAA,QAC7C,MAAM,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI;AAAA,MAC3C;AACA,UAAI,WAAW,WAAW,KAAK;AAC7B,eAAO;AAAA,MACT,OAAO;AACL;AAAA,UACE;AAAA,UACA,4CACE,WAAW,WACR,MAAM,WAAW,KAAK;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AACF;","names":[]}