feat(deploy): single-origin path-based deployment + project site #34

Merged
developer merged 4 commits from feature/deploy-single-origin into development 2026-05-23 17:24:25 +00:00
4 changed files with 38 additions and 28 deletions
Showing only changes of commit 9cb5097f54 - Show all commits
+24
View File
@@ -0,0 +1,24 @@
// Tombstone service worker for the site origin's root scope (`/`).
//
// The game UI used to be served at this origin's root with a
// root-scoped service worker. It now lives under `/game/` (its own
// scoped worker), and the project site served at `/` ships no service
// worker of its own. This file exists only so any lingering old
// root-scoped worker, on its next update check, replaces itself with
// this one — which unregisters itself and reloads its controlled pages
// so they fall through to the live network (the site) instead of a
// stale cache. New visitors never register it; nothing here calls
// `register`.
self.addEventListener("install", () => self.skipWaiting());
self.addEventListener("activate", (event) => {
event.waitUntil(
(async () => {
await self.registration.unregister();
const clients = await self.clients.matchAll({ type: "window" });
for (const client of clients) {
client.navigate(client.url);
}
})(),
);
});
+4 -1
View File
@@ -90,7 +90,10 @@
} }
if (session.status === "anonymous" && pathname !== "/login") { if (session.status === "anonymous" && pathname !== "/login") {
void goto(withBase("/login"), { replaceState: true }); void goto(withBase("/login"), { replaceState: true });
} else if (session.status === "authenticated" && pathname === "/login") { } else if (
session.status === "authenticated" &&
(pathname === "/login" || pathname === "/")
) {
void goto(withBase("/lobby"), { replaceState: true }); void goto(withBase("/lobby"), { replaceState: true });
} }
}); });
+10 -14
View File
@@ -1,22 +1,18 @@
<script lang="ts"> <script lang="ts">
import { APP_VERSION } from "$lib/version"; // The app root renders no content of its own. The root layout's auth
// guard redirects "/" to /lobby (authenticated) or /login
// (anonymous); this placeholder only shows for the brief moment
// before that client-side redirect resolves.
import { i18n } from "$lib/i18n/index.svelte";
</script> </script>
<main> <main class="status">
<h1>Galaxy</h1> <p>{i18n.t("common.loading")}</p>
<p>Cross-platform UI client — workspace skeleton.</p>
</main> </main>
<footer data-testid="app-version">version {APP_VERSION}</footer>
<style> <style>
main { .status {
padding: 2rem; padding: var(--space-6);
font-family: system-ui, sans-serif; font-family: var(--font-sans);
}
footer {
padding: 1rem 2rem;
opacity: 0.6;
font-size: 0.875rem;
} }
</style> </style>
-13
View File
@@ -1,13 +0,0 @@
import { render } from "@testing-library/svelte";
import { describe, expect, it } from "vitest";
import Page from "../src/routes/+page.svelte";
describe("landing page", () => {
it("renders a non-empty version string in the footer", () => {
const { getByTestId } = render(Page);
const footer = getByTestId("app-version");
expect(footer).toBeInTheDocument();
expect(footer.textContent?.trim()).not.toBe("");
expect(footer.textContent).toMatch(/version\s+\S+/);
});
});