Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:

- name: Download Staticalize
run: |
wget https://github.com/thefrontside/staticalize/releases/download/v0.2.6/staticalize-linux.tar.gz \
wget https://github.com/thefrontside/staticalize/releases/download/v0.2.7/staticalize-linux.tar.gz \
-O /tmp/staticalize-linux.tar.gz
tar -xzf /tmp/staticalize-linux.tar.gz -C /usr/local/bin
chmod +x /usr/local/bin/staticalize-linux
Expand All @@ -57,7 +57,9 @@ jobs:
staticalize-linux \
--site=http://127.0.0.1:8005 \
--output=built \
--base=https://frontside.com
--base=https://frontside.com \
--concurrency=25 \
--retries=5

- name: Deploy Preview to Netlify
id: netlify
Expand Down Expand Up @@ -130,7 +132,7 @@ jobs:

- name: Download Staticalize
run: |
wget https://github.com/thefrontside/staticalize/releases/download/v0.2.6/staticalize-linux.tar.gz \
wget https://github.com/thefrontside/staticalize/releases/download/v0.2.7/staticalize-linux.tar.gz \
-O /tmp/staticalize-linux.tar.gz
tar -xzf /tmp/staticalize-linux.tar.gz -C /usr/local/bin
chmod +x /usr/local/bin/staticalize-linux
Expand All @@ -140,7 +142,9 @@ jobs:
staticalize-linux \
--site=http://127.0.0.1:8005 \
--output=built \
--base=https://frontside.com
--base=https://frontside.com \
--concurrency=25 \
--retries=5

- name: Deploy to Production
run: |
Expand Down
31 changes: 16 additions & 15 deletions components/project-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,33 +78,34 @@ const projects = [
{
title: "Interactors",
description: "Page Objects for components libraries",
url: "https://frontside.com/interactors",
url: "/interactors",
version: "v1",
img:
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNCIgaGVpZ2h0PSIzNCIgdmlld0JveD0iMCAwIDM0IDM0Ij4KICA8ZGVmcz4KICAgIDxzdHlsZT4KICAgICAgLmEgewogICAgICAgIGZpbGw6ICNmZmY7CiAgICAgIH0KCiAgICAgIC5hLCAuYiwgLmMgewogICAgICAgIHN0cm9rZTogIzE0MzA1YzsKICAgICAgICBzdHJva2UtbWl0ZXJsaW1pdDogMTA7CiAgICAgICAgc3Ryb2tlLXdpZHRoOiAycHg7CiAgICAgIH0KCiAgICAgIC5iIHsKICAgICAgICBmaWxsOiAjMjZhYmU4OwogICAgICB9CgogICAgICAuYyB7CiAgICAgICAgZmlsbDogI2Y3NGQ3YjsKICAgICAgfQogICAgPC9zdHlsZT4KICA8L2RlZnM+CiAgPGc+CiAgICA8ZWxsaXBzZSBjbGFzcz0iYSIgY3g9IjI4LjQ1IiBjeT0iNi45MSIgcng9IjQiIHJ5PSIzLjg5Ii8+CiAgICA8cGF0aCBjbGFzcz0iYiIgZD0iTTI4LjQ1LDEwLjhhMy45NCwzLjk0LDAsMCwxLTQtMy44OSw0LDQsMCwwLDEsLjI1LTEuMzQsNi40NSw2LjQ1LDAsMCwwLTEuMzMtLjE0QTYuMyw2LjMsMCwwLDAsMTcsMTEuNjRhNi4zOCw2LjM4LDAsMCwwLDEyLjc1LDAsNi45MSw2LjkxLDAsMCwwLS4xLTFBNC4zLDQuMywwLDAsMSwyOC40NSwxMC44WiIvPgogICAgPHBhdGggY2xhc3M9ImMiIGQ9Ik0xNywxMS42NGE2LjI2LDYuMjYsMCwwLDEsLjEtMSwxMS4xMSwxMS4xMSwwLDAsMC00LjY5LTEsMTAuODIsMTAuODIsMCwwLDAtMTEsMTAuNjhBMTAuODIsMTAuODIsMCwwLDAsMTIuNDEsMzFhMTAuODMsMTAuODMsMCwwLDAsMTEtMTAuNjgsMTAuNjYsMTAuNjYsMCwwLDAtLjMxLTIuNDhBNi4yNyw2LjI3LDAsMCwxLDE3LDExLjY0WiIvPgogIDwvZz4KPC9zdmc+Cg==",
},
{
title: "Effection",
description: "Structured Concurrency for JavaScript",
url: "https://frontside.com/effection",
version: "v3",
url: "/effection",
version: "v4",
img:
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNCIgaGVpZ2h0PSIzNCIgdmlld0JveD0iMCAwIDM0IDM0Ij4KICA8ZGVmcz4KICAgIDxzdHlsZT4KICAgICAgLmEgewogICAgICAgIGZpbGw6ICMxYzMwNWE7CiAgICAgIH0KCiAgICAgIC5iIHsKICAgICAgICBmaWxsOiAjMjZhYmU4OwogICAgICB9CgogICAgICAuYyB7CiAgICAgICAgZmlsbDogI2ZmZjsKICAgICAgfQoKICAgICAgLmQgewogICAgICAgIGZpbGw6ICNmNzRkN2I7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDxnPgogICAgPHJlY3QgY2xhc3M9ImEiIHg9IjEzLjU4IiB5PSI2Ljg5IiB3aWR0aD0iMTcuMTgiIGhlaWdodD0iMTcuMTgiIHJ4PSIxLjk2IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNC40NiAyMC4yMSkgcm90YXRlKC00NSkiLz4KICAgIDxyZWN0IGNsYXNzPSJhIiB4PSI4LjM5IiB5PSIxMi4wOCIgd2lkdGg9IjE3LjE4IiBoZWlnaHQ9IjE3LjE4IiByeD0iMS45NiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTkuNjQgMTguMDYpIHJvdGF0ZSgtNDUpIi8+CiAgICA8cmVjdCBjbGFzcz0iYSIgeD0iMy4yNCIgeT0iNi45NyIgd2lkdGg9IjE3LjE4IiBoZWlnaHQ9IjE3LjE4IiByeD0iMS45NiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTcuNTMgMTIuOTIpIHJvdGF0ZSgtNDUpIi8+CiAgICA8cmVjdCBjbGFzcz0iYiIgeD0iMTYuMjgiIHk9IjkuNjEiIHdpZHRoPSIxMS43OCIgaGVpZ2h0PSIxMS43OCIgcng9IjIuMjYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC00LjQ3IDIwLjIxKSByb3RhdGUoLTQ1KSIvPgogICAgPHJlY3QgY2xhc3M9ImMiIHg9IjExLjA3IiB5PSIxNC44MSIgd2lkdGg9IjExLjc4IiBoZWlnaHQ9IjExLjc4IiByeD0iMi4yNiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTkuNjcgMTguMDYpIHJvdGF0ZSgtNDUpIi8+CiAgICA8cmVjdCBjbGFzcz0iZCIgeD0iNS45NyIgeT0iOS43MSIgd2lkdGg9IjExLjc4IiBoZWlnaHQ9IjExLjc4IiByeD0iMi4yNiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTcuNTYgMTIuOTUpIHJvdGF0ZSgtNDUpIi8+CiAgPC9nPgo8L3N2Zz4K",
},
{
title: "Auth0 Simulator",
description: "Enabling testing and local development",
url: "https://github.com/thefrontside/simulacrum/tree/v0/packages/auth0",
title: "Simulacrum",
description: "Simulation enabling testing and local development",
url: "https://github.com/thefrontside/simulacrum",
version: "v0",
img:
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNCIgaGVpZ2h0PSIzNCIgdmlld0JveD0iMCAwIDM0IDM0Ij4KICA8ZGVmcz4KICAgIDxzdHlsZT4KICAgICAgLmEgewogICAgICAgIGZpbGw6ICMxNDMxNWQ7CiAgICAgIH0KCiAgICAgIC5iIHsKICAgICAgICBmaWxsOiAjMjZhYmU4OwogICAgICB9CgogICAgICAuYyB7CiAgICAgICAgZmlsbDogI2Y3NGQ3YjsKICAgICAgICBmaWxsLXJ1bGU6IGV2ZW5vZGQ7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDxnPgogICAgPHBhdGggY2xhc3M9ImEiIGQ9Ik0xNyw1LjE2bDEwLjMzLDUuOTVWMjIuODlMMTcsMjguODQsNi42NywyMi44OVYxMS4xMUwxNyw1LjE2TTE3LC4yOCwyLjQ1LDguNjZWMjUuMzRMMTcsMzMuNzJsMTQuNTUtOC4zOFY4LjY2TDE3LC4yOFoiLz4KICAgIDxwYXRoIGNsYXNzPSJiIiBkPSJNMTcsNy42OWw4LjEyLDQuNjd2OS4yOEwxNywyNi4zMSw4Ljg4LDIxLjY0VjEyLjM2TDE3LDcuNjltMC0zLjI1TDYuMDYsMTAuNzRWMjMuMjZMMTcsMjkuNTZsMTAuOTQtNi4zVjEwLjc0TDE3LDQuNDRaIi8+CiAgICA8cG9seWdvbiBjbGFzcz0iYyIgcG9pbnRzPSIxNyAxMy44OCAxNC4yOCAxNS40NCAxNC4yOCAxOC41NiAxNyAyMC4xMiAxOS43MiAxOC41NiAxOS43MiAxNS40NCAxNyAxMy44OCIvPgogIDwvZz4KPC9zdmc+Cg==",
},
{
title: "Bigtest",
description: "Universal test runner, GraphQL driven",
url: "https://frontside.com/bigtest",
version: "v0",
img:
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNCIgaGVpZ2h0PSIzNCIgdmlld0JveD0iMCAwIDM0IDM0Ij4KICA8ZGVmcz4KICAgIDxzdHlsZT4KICAgICAgLmEgewogICAgICAgIGZpbGw6ICMxNDMxNWQ7CiAgICAgIH0KCiAgICAgIC5iIHsKICAgICAgICBmaWxsOiAjZmZmOwogICAgICB9CgogICAgICAuYyB7CiAgICAgICAgZmlsbDogI2Y3NGQ3YjsKICAgICAgfQoKICAgICAgLmQgewogICAgICAgIGZpbGw6ICMyNmFiZTg7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDxnPgogICAgPHBvbHlnb24gY2xhc3M9ImEiIHBvaW50cz0iMTYuOTYgMC4yNyAyLjU0IDUuMyAzLjU0IDI1LjIyIDE2Ljk2IDMzLjU4IDMwLjM5IDI1LjIyIDMxLjM5IDUuMyAxNi45NiAwLjI3Ii8+CiAgICA8cG9seWdvbiBjbGFzcz0iYiIgcG9pbnRzPSIxNi45NiAzLjUyIDEyLjUzIDUuMTMgMTEuNjggMTYuOTQgMTYuOTYgMTkuNTcgMTUuOTUgMjkuNzIgMTYuOTUgMzAuMzYgMTguNDMgMjkuNDIgMjMuNiAxNi45NCAxOC4zOSAxNC4xNCAyMC44OCA0Ljk0IDE2Ljk2IDMuNTIiLz4KICAgIDxwb2x5Z29uIGNsYXNzPSJjIiBwb2ludHM9IjUuMzMgNy43NCAxMi41MyA1LjEzIDExLjY4IDE2Ljk0IDE2Ljk2IDE5LjU3IDE1Ljk1IDI5LjcyIDYuNDEgMjMuNjMgNS4zMyA3Ljc0Ii8+CiAgICA8cG9seWdvbiBjbGFzcz0iZCIgcG9pbnRzPSIyNy41MiAyMy42MyAxOC40MyAyOS40MiAyMy42IDE2Ljk0IDE4LjM5IDE0LjE0IDIwLjg4IDQuOTQgMjguNiA3Ljc0IDI3LjUyIDIzLjYzIi8+CiAgPC9nPgo8L3N2Zz4K",
},
}
// we aren't actively maintaining, remove for now
// {
// title: "Bigtest",
// description: "Universal test runner, GraphQL driven",
// url: "/bigtest",
// version: "v0",
// img:
// "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNCIgaGVpZ2h0PSIzNCIgdmlld0JveD0iMCAwIDM0IDM0Ij4KICA8ZGVmcz4KICAgIDxzdHlsZT4KICAgICAgLmEgewogICAgICAgIGZpbGw6ICMxNDMxNWQ7CiAgICAgIH0KCiAgICAgIC5iIHsKICAgICAgICBmaWxsOiAjZmZmOwogICAgICB9CgogICAgICAuYyB7CiAgICAgICAgZmlsbDogI2Y3NGQ3YjsKICAgICAgfQoKICAgICAgLmQgewogICAgICAgIGZpbGw6ICMyNmFiZTg7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDxnPgogICAgPHBvbHlnb24gY2xhc3M9ImEiIHBvaW50cz0iMTYuOTYgMC4yNyAyLjU0IDUuMyAzLjU0IDI1LjIyIDE2Ljk2IDMzLjU4IDMwLjM5IDI1LjIyIDMxLjM5IDUuMyAxNi45NiAwLjI3Ii8+CiAgICA8cG9seWdvbiBjbGFzcz0iYiIgcG9pbnRzPSIxNi45NiAzLjUyIDEyLjUzIDUuMTMgMTEuNjggMTYuOTQgMTYuOTYgMTkuNTcgMTUuOTUgMjkuNzIgMTYuOTUgMzAuMzYgMTguNDMgMjkuNDIgMjMuNiAxNi45NCAxOC4zOSAxNC4xNCAyMC44OCA0Ljk0IDE2Ljk2IDMuNTIiLz4KICAgIDxwb2x5Z29uIGNsYXNzPSJjIiBwb2ludHM9IjUuMzMgNy43NCAxMi41MyA1LjEzIDExLjY4IDE2Ljk0IDE2Ljk2IDE5LjU3IDE1Ljk1IDI5LjcyIDYuNDEgMjMuNjMgNS4zMyA3Ljc0Ii8+CiAgICA8cG9seWdvbiBjbGFzcz0iZCIgcG9pbnRzPSIyNy41MiAyMy42MyAxOC40MyAyOS40MiAyMy42IDE2Ljk0IDE4LjM5IDE0LjE0IDIwLjg4IDQuOTQgMjguNiA3Ljc0IDI3LjUyIDIzLjYzIi8+CiAgPC9nPgo8L3N2Zz4K",
// },
];
12 changes: 6 additions & 6 deletions main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ await main(function* (args) {
route("/redirects", redirectsRoute()),
route("/platformscript", platformscriptRoute()),
$route("/platformscript/(.*)", redirect("/platformscript")),
proxyRoute({ ...proxies.effection, pattern: "/effection(.*)" }),
proxyRoute({ ...proxies.graphgen, pattern: "/graphgen(.*)" }),
proxyRoute(proxies.effection),
proxyRoute(proxies.graphgen),
$route("/assets(.*)", assetsRoute("assets")),
proxyRoute({ ...proxies.interactors, pattern: "/interactors(.*)" }),
proxyRoute(proxies.interactors),
],

plugins: [
Expand Down Expand Up @@ -127,16 +127,16 @@ function proxySites() {
effection: {
prefix: "effection",
website: Deno.env.get("EFFECTION_URL") ??
"https://effection.deno.dev",
"https://effection.netlify.app",
},
interactors: {
prefix: "interactors",
website: Deno.env.get("INTERACTORS_URL") ??
"https://interactors.deno.dev",
"https://interactors.netlify.app",
},
graphgen: {
prefix: "graphgen",
website: Deno.env.get("GRAPHGEN_URL") ?? "https://graphgen.deno.dev",
website: Deno.env.get("GRAPHGEN_URL") ?? "https://graphgen-data.netlify.app",
},
} as const;
}
151 changes: 83 additions & 68 deletions routes/proxy-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,80 +42,97 @@ export function proxyRoute(options: ProxyRouteOptions): HTTPMiddleware {

if ([301, 302, 307, 308].includes(response.status)) {
let location = response.headers.get("location");
if (location?.startsWith(String(website))) {
let headers = copyHeaders(response);

let url = new URL(request.url);
if (location) {
// Resolve relative Location headers against the upstream website
// - Netlify returns relative redirects like "/search/""
// - Deno Deploy returns absolute ones like "https://host/search/"
let loc = location.startsWith("http")
? new URL(location)
: new URL(location, website);

if (loc.origin === website.origin) {
let headers = copyHeaders(response);
let url = new URL(request.url);

if (!options.root) {
loc.pathname = `${options.prefix}${loc.pathname}`;
}
headers.location = loc.toString().replace(target.origin, url.origin);

let loc = new URL(location);
if (!options.root) {
loc.pathname = `${options.prefix}${loc.pathname}`;
response = new Response(null, {
status: response.status,
statusText: response.statusText,
headers,
});
}
headers.location = loc.toString().replace(target.origin, url.origin);

response = new Response(null, {
status: response.status,
statusText: response.statusText,
headers,
});
}
} else if (
response.headers.get("Content-Type")?.match(/html/) && !options.root
) {
let body = yield* call(() => response.text());
let tree = fromHtml(body);
try {
let body = yield* call(() => response.text());
let tree = fromHtml(body);

yield* injectPlausible(tree);
yield* injectUmami(tree);
yield* injectMatomo(tree);
yield* injectPlausible(tree);
yield* injectUmami(tree);
yield* injectMatomo(tree);

let elements = selectAll(
'[href^="/"],[src^="/"],form[action],meta[content]',
tree,
);
let elements = selectAll(
'[href^="/"],[src^="/"],form[action],meta[content]',
tree,
);

for (let element of elements) {
let properties = element.properties!;
for (let element of elements) {
let properties = element.properties!;

if (properties.href) {
properties.href = posixNormalize(
`${base.pathname}${properties.href}`,
);
}
if (properties.src) {
properties.src = posixNormalize(`${base.pathname}${properties.src}`);
}
if (properties.action) {
properties.action = posixNormalize(
`${base.pathname}${properties.action}`,
);
}
if (properties.content) {
if (typeof properties.content === "string") {
const parts = properties.content.match(/\d;\s*url=(.*)/);
if (parts) {
const [, url] = parts;
properties.content = properties.content.replace(
url,
posixNormalize(`${base.pathname}${url}`),
);
} else if (properties.content.startsWith("http")) {
properties.content = properties.content.replace(
target.origin,
base.href.replace(/\/?$/, ""),
);
if (properties.href) {
properties.href = posixNormalize(
`${base.pathname}${properties.href}`,
);
}
if (properties.src) {
properties.src = posixNormalize(
`${base.pathname}${properties.src}`,
);
}
if (properties.action) {
properties.action = posixNormalize(
`${base.pathname}${properties.action}`,
);
}
if (properties.content) {
if (typeof properties.content === "string") {
const parts = properties.content.match(/\d;\s*url=(.*)/);
if (parts) {
const [, url] = parts;
properties.content = properties.content.replace(
url,
posixNormalize(`${base.pathname}${url}`),
);
} else if (properties.content.startsWith("http")) {
properties.content = properties.content.replace(
target.origin,
base.href.replace(/\/?$/, ""),
);
}
}
}
}
response = new Response(toHtml(tree), {
status: response.status,
statusText: response.statusText,
headers: copyHeaders(response),
});
} catch (error) {
console.error(`Proxy HTML rewrite failed for ${request.url}:`, error);
}
response = new Response(toHtml(tree), {
status: response.status,
statusText: response.statusText,
headers: copyHeaders(response),
});
}

return response;
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: copyHeaders(response),
});
};

if (options.prefix) {
Expand All @@ -135,17 +152,15 @@ export function proxyRoute(options: ProxyRouteOptions): HTTPMiddleware {
};
}

if (options.pattern) {
let handler = revolutionRoute(options.pattern, middleware);
if (middleware.sitemapExtension) {
Object.defineProperty(handler, "sitemapExtension", {
value: middleware.sitemapExtension,
});
}
return handler;
}
let pattern = options.pattern ?? `/${options.prefix}(.*)`;

return middleware;
let handler = revolutionRoute(pattern, middleware);
if (middleware.sitemapExtension) {
Object.defineProperty(handler, "sitemapExtension", {
value: middleware.sitemapExtension,
});
}
return handler;
}

// Copy an upstream response's headers, dropping the ones that describe how the
Expand Down