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.
- package/dist/html/host.cjs +330 -166
- package/dist/html/host.cjs.map +1 -1
- package/dist/html/host.js +330 -166
- package/dist/html/host.js.map +1 -1
- package/dist/html/remote.cjs +114 -20
- package/dist/html/remote.cjs.map +1 -1
- package/dist/html/remote.js +114 -20
- package/dist/html/remote.js.map +1 -1
- package/dist/internal/shared/{ssr → client}/fetch-with-protected-rc-fallback.cjs +9 -6
- package/dist/internal/shared/client/fetch-with-protected-rc-fallback.cjs.map +1 -0
- package/dist/internal/shared/{ssr → client}/fetch-with-protected-rc-fallback.d.ts +1 -2
- package/dist/internal/shared/{ssr → client}/fetch-with-protected-rc-fallback.js +10 -6
- package/dist/internal/shared/client/fetch-with-protected-rc-fallback.js.map +1 -0
- package/dist/internal/shared/client/protected-rc-fallback.cjs +32 -0
- package/dist/internal/shared/client/protected-rc-fallback.cjs.map +1 -0
- package/dist/internal/shared/client/protected-rc-fallback.d.ts +6 -0
- package/dist/internal/shared/client/protected-rc-fallback.js +8 -0
- package/dist/internal/shared/client/protected-rc-fallback.js.map +1 -0
- package/dist/internal/shared/client/remote-component.cjs +190 -100
- package/dist/internal/shared/client/remote-component.cjs.map +1 -1
- package/dist/internal/shared/client/remote-component.js +190 -100
- package/dist/internal/shared/client/remote-component.js.map +1 -1
- package/dist/internal/shared/constants.cjs +29 -0
- package/dist/internal/shared/constants.cjs.map +1 -0
- package/dist/internal/shared/constants.d.ts +3 -0
- package/dist/internal/shared/constants.js +5 -0
- package/dist/internal/shared/constants.js.map +1 -0
- package/dist/internal/shared/ssr/fetch-remote-component.cjs +1 -2
- package/dist/internal/shared/ssr/fetch-remote-component.cjs.map +1 -1
- package/dist/internal/shared/ssr/fetch-remote-component.js +1 -2
- package/dist/internal/shared/ssr/fetch-remote-component.js.map +1 -1
- package/dist/internal/shared/ssr/fetch-with-hooks.cjs +1 -1
- package/dist/internal/shared/ssr/fetch-with-hooks.cjs.map +1 -1
- package/dist/internal/shared/ssr/fetch-with-hooks.js +1 -1
- package/dist/internal/shared/ssr/fetch-with-hooks.js.map +1 -1
- package/dist/internal/shared/utils/logger.cjs.map +1 -1
- package/dist/internal/shared/utils/logger.d.ts +1 -1
- package/dist/internal/shared/utils/logger.js.map +1 -1
- package/dist/next/config.cjs.map +1 -1
- package/dist/next/config.js.map +1 -1
- package/dist/next/host/client/index.cjs +202 -107
- package/dist/next/host/client/index.cjs.map +1 -1
- package/dist/next/host/client/index.js +202 -107
- package/dist/next/host/client/index.js.map +1 -1
- package/dist/next/host/pages-router-server.cjs +1 -2
- package/dist/next/host/pages-router-server.cjs.map +1 -1
- package/dist/next/host/pages-router-server.d.ts +5 -0
- package/dist/next/host/pages-router-server.js +1 -2
- package/dist/next/host/pages-router-server.js.map +1 -1
- package/dist/next/proxy.cjs +36 -24
- package/dist/next/proxy.cjs.map +1 -1
- package/dist/next/proxy.d.ts +0 -1
- package/dist/next/proxy.js +36 -24
- package/dist/next/proxy.js.map +1 -1
- package/dist/react/index.cjs +202 -107
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +202 -107
- package/dist/react/index.js.map +1 -1
- package/dist/shared/host/proxy.cjs +23 -20
- package/dist/shared/host/proxy.cjs.map +1 -1
- package/dist/shared/host/proxy.d.ts +0 -3
- package/dist/shared/host/proxy.js +23 -19
- package/dist/shared/host/proxy.js.map +1 -1
- package/dist/shared/remote/proxy.cjs +9 -1
- package/dist/shared/remote/proxy.cjs.map +1 -1
- package/dist/shared/remote/proxy.d.ts +7 -1
- package/dist/shared/remote/proxy.js +8 -1
- package/dist/shared/remote/proxy.js.map +1 -1
- package/package.json +1 -1
- package/dist/internal/shared/ssr/fetch-with-protected-rc-fallback.cjs.map +0 -1
- 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
|
-
|
|
27
|
-
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
3
|
-
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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,
|
|
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,
|
|
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 +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":[]}
|