feat(deploy): single-origin path-based deployment + project site
Serve the whole stack behind one host: site at /, game UI at /game/, gateway REST at /api + /healthz, Connect at /rpc (prefix stripped by the edge Caddy). The built artifact is domain-agnostic — the UI talks to the gateway same-origin via relative URLs, so the same bundle runs under any host with no rebuild and with CORS disabled. - Rename the Connect proto service galaxy.gateway.v1.EdgeGateway -> edge.v1.Gateway; regenerate Go + TS; public path /rpc/edge.v1.Gateway. - Move the game UI under base path /game (env BASE_PATH); make the manifest, service-worker scope, WASM loader, and all navigation base-aware via a withBase helper. - Relative API + /rpc Connect prefix; Vite dev proxy mirrors the strip. - Rewrite the edge Caddy (dev + prod) for path-based routing; empty CORS allow-lists (same-origin); single host. - New VitePress project site (site/): i18n en/ru with switcher, LaTeX math, minimal monospace theme; built and served at /. - dev-deploy compose/Makefile + CI (dev-deploy, prod-build, new site-build) build and seed the site; probes hit /, /game/, /healthz. - Sync docs (ARCHITECTURE, gateway README/openapi, dev-deploy & local-dev READMEs, CLAUDE.md, ui/PLAN). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+6
-6
@@ -474,11 +474,11 @@ documents the historical labelling in its package doc.
|
||||
Artifacts (delivered):
|
||||
|
||||
- `gateway/buf.gen.yaml` extended with `buf.build/connectrpc/go`,
|
||||
generating `gateway/proto/galaxy/gateway/v1/gatewayv1connect/edge_gateway.connect.go`
|
||||
generating `gateway/proto/edge/v1/edgev1connect/edge_gateway.connect.go`
|
||||
- `gateway/internal/grpcapi/server.go` rewritten around `http.Server`
|
||||
+ `h2c.NewHandler` + `gatewayv1connect.NewEdgeGatewayHandler`
|
||||
+ `h2c.NewHandler` + `edgev1connect.NewGatewayHandler`
|
||||
- new `gateway/internal/grpcapi/connect_handler.go` adapting the
|
||||
existing `gatewayv1.EdgeGatewayServer` decorator stack to the
|
||||
existing `edgev1.GatewayServer` decorator stack to the
|
||||
Connect handler interface, including a `grpc.ServerStreamingServer`
|
||||
shim around `*connect.ServerStream[GatewayEvent]` and a gRPC
|
||||
`status.Error` → `*connect.Error` translation helper
|
||||
@@ -492,7 +492,7 @@ Artifacts (delivered):
|
||||
`gateway/docs/runbook.md`, and `docs/ARCHITECTURE.md` §15
|
||||
- migrated tests: `gateway/internal/grpcapi/server_test.go`,
|
||||
`test_fixtures_test.go`, and every `*_integration_test.go` in that
|
||||
package now drive a `gatewayv1connect.EdgeGatewayClient` over
|
||||
package now drive a `edgev1connect.GatewayClient` over
|
||||
HTTP/2 cleartext loopback
|
||||
- migrated harness: `integration/testenv/grpc_client.go` →
|
||||
`connect_client.go`. `SignedGatewayClient` keeps the same public
|
||||
@@ -580,10 +580,10 @@ Artifacts (delivered):
|
||||
browsers; the JSDOM test path lives next to it in
|
||||
`ui/frontend/tests/setup-wasm.ts`.
|
||||
- `ui/frontend/src/api/connect.ts` — typed Connect-Web transport +
|
||||
`EdgeGatewayClient` factory.
|
||||
`GatewayClient` factory.
|
||||
- `ui/frontend/src/api/galaxy-client.ts` — `GalaxyClient` skeleton
|
||||
with injected `Signer` and `Sha256` dependencies.
|
||||
- `ui/frontend/src/proto/galaxy/gateway/v1/edge_gateway_pb.ts`
|
||||
- `ui/frontend/src/proto/edge/v1/edge_gateway_pb.ts`
|
||||
(generated) and `ui/frontend/src/proto/buf/validate/validate_pb.ts`
|
||||
(generated as a transitive import via `--include-imports`).
|
||||
- `ui/frontend/static/core.wasm` (903 KB) + `wasm_exec.js` (TinyGo
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Package types defines the v1 transport envelopes carried over the
|
||||
// wire between the Galaxy client and gateway. Envelope shapes mirror
|
||||
// the protobuf messages in `gateway/proto/galaxy/gateway/v1/`, but are
|
||||
// the protobuf messages in `gateway/proto/edge/v1/`, but are
|
||||
// kept as plain Go structs here so the UI client can read and produce
|
||||
// them without depending on the protobuf runtime in WASM and gomobile
|
||||
// builds.
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
# Gateway public REST + Connect-Web edge listener. Points at the Vite
|
||||
# dev server's own origin so the browser sees same-origin requests;
|
||||
# Vite then proxies `/api` and `/galaxy.gateway.v1.EdgeGateway` to the
|
||||
# real gateway at `http://localhost:8080`. See `vite.config.ts`. To
|
||||
# work against a non-local gateway, override the proxy target via
|
||||
# `VITE_DEV_PROXY_TARGET=http://gateway.host:8080 pnpm dev` (no UI
|
||||
# Vite then proxies `/api` to the REST listener (`:8080`) and `/rpc` to
|
||||
# the Connect listener (`:9090`, prefix stripped), mirroring the
|
||||
# single-origin edge Caddy. See `vite.config.ts`. To work against a
|
||||
# non-local gateway, override the proxy targets via
|
||||
# `VITE_DEV_PROXY_TARGET` / `VITE_DEV_GRPC_PROXY_TARGET` (no UI
|
||||
# rebuild needed).
|
||||
VITE_GATEWAY_BASE_URL=http://localhost:5173
|
||||
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
// `createEdgeGatewayClient` builds a typed Connect-Web client for the
|
||||
// `createGatewayClient` builds a typed Connect-Web client for the
|
||||
// gateway's authenticated edge surface. It speaks the Connect protocol
|
||||
// over HTTP/1.1 (or HTTP/2 if the host upgrades the connection) — the
|
||||
// gateway listener built in Phase 4 natively serves Connect, gRPC, and
|
||||
// gRPC-Web on the same h2c port.
|
||||
//
|
||||
// The factory is intentionally thin: callers provide the gateway base
|
||||
// URL (e.g. https://api.galaxy.test), and receive a typed
|
||||
// `EdgeGatewayClient`. Authentication, signing, and response
|
||||
// The factory is intentionally thin: callers provide the Connect base
|
||||
// URL (the same-origin `/rpc` prefix from `gatewayRpcBaseUrl`), and
|
||||
// receive a typed
|
||||
// `GatewayClient`. Authentication, signing, and response
|
||||
// verification live one layer up, in `GalaxyClient`.
|
||||
|
||||
import { createClient, type Client } from "@connectrpc/connect";
|
||||
import { createConnectTransport } from "@connectrpc/connect-web";
|
||||
import { EdgeGateway } from "../proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { Gateway } from "../proto/edge/v1/edge_gateway_pb";
|
||||
|
||||
export type EdgeGatewayClient = Client<typeof EdgeGateway>;
|
||||
export type GatewayClient = Client<typeof Gateway>;
|
||||
|
||||
export function createEdgeGatewayClient(baseUrl: string): EdgeGatewayClient {
|
||||
return createClient(EdgeGateway, createConnectTransport({ baseUrl }));
|
||||
export function createGatewayClient(baseUrl: string): GatewayClient {
|
||||
return createClient(Gateway, createConnectTransport({ baseUrl }));
|
||||
}
|
||||
|
||||
@@ -26,10 +26,10 @@ import type { DeviceKeypair } from "../platform/store/index";
|
||||
import {
|
||||
SubscribeEventsRequestSchema,
|
||||
type GatewayEvent,
|
||||
} from "../proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { GATEWAY_BASE_URL } from "../lib/env";
|
||||
} from "../proto/edge/v1/edge_gateway_pb";
|
||||
import { gatewayRpcBaseUrl } from "../lib/env";
|
||||
import { session } from "../lib/session-store.svelte";
|
||||
import { createEdgeGatewayClient, type EdgeGatewayClient } from "./connect";
|
||||
import { createGatewayClient, type GatewayClient } from "./connect";
|
||||
|
||||
const PROTOCOL_VERSION = "v1";
|
||||
const SUBSCRIBE_MESSAGE_TYPE = "gateway.subscribe";
|
||||
@@ -83,7 +83,7 @@ export type ConnectionStatus =
|
||||
* consumer cannot resolve by itself. Production code reads `core`,
|
||||
* `keypair`, and `deviceSessionId` from the session store and the
|
||||
* gateway public key from `lib/env`; tests inject a fake
|
||||
* `EdgeGatewayClient` and deterministic `sleep`/`random` to drive
|
||||
* `GatewayClient` and deterministic `sleep`/`random` to drive
|
||||
* backoff in fake-timer mode.
|
||||
*/
|
||||
export interface EventStreamStartOptions {
|
||||
@@ -91,8 +91,8 @@ export interface EventStreamStartOptions {
|
||||
keypair: DeviceKeypair;
|
||||
deviceSessionId: string;
|
||||
gatewayResponsePublicKey: Uint8Array;
|
||||
/** Custom transport client. Defaults to `createEdgeGatewayClient(GATEWAY_BASE_URL)`. */
|
||||
client?: EdgeGatewayClient;
|
||||
/** Custom transport client. Defaults to `createGatewayClient(gatewayRpcBaseUrl())`. */
|
||||
client?: GatewayClient;
|
||||
/** Sleep hook for tests; defaults to a real-time `setTimeout`. */
|
||||
sleep?: (ms: number) => Promise<void>;
|
||||
/** Random source for full-jitter backoff; defaults to `Math.random`. */
|
||||
@@ -189,7 +189,7 @@ export class EventStream {
|
||||
const sleep = opts.sleep ?? defaultSleep;
|
||||
const random = opts.random ?? Math.random;
|
||||
const onlineProbe = opts.onlineProbe ?? defaultOnlineProbe;
|
||||
const client = opts.client ?? createEdgeGatewayClient(GATEWAY_BASE_URL);
|
||||
const client = opts.client ?? createGatewayClient(gatewayRpcBaseUrl());
|
||||
|
||||
let attempt = 0;
|
||||
while (!signal.aborted && this.running) {
|
||||
@@ -311,7 +311,7 @@ export class EventStream {
|
||||
}
|
||||
|
||||
async function openStream(
|
||||
client: EdgeGatewayClient,
|
||||
client: GatewayClient,
|
||||
opts: EventStreamStartOptions,
|
||||
signal: AbortSignal,
|
||||
): Promise<AsyncIterable<GatewayEvent>> {
|
||||
|
||||
@@ -15,8 +15,8 @@ import type { Core } from "../platform/core/index";
|
||||
import {
|
||||
ExecuteCommandRequestSchema,
|
||||
type ExecuteCommandResponse,
|
||||
} from "../proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import type { EdgeGatewayClient } from "./connect";
|
||||
} from "../proto/edge/v1/edge_gateway_pb";
|
||||
import type { GatewayClient } from "./connect";
|
||||
|
||||
/**
|
||||
* Signer produces a raw 64-byte Ed25519 signature over canonicalBytes.
|
||||
@@ -35,7 +35,7 @@ export type Sha256 = (payload: Uint8Array) => Promise<Uint8Array>;
|
||||
|
||||
export interface GalaxyClientOptions {
|
||||
core: Core;
|
||||
edge: EdgeGatewayClient;
|
||||
edge: GatewayClient;
|
||||
signer: Signer;
|
||||
sha256: Sha256;
|
||||
deviceSessionId: string;
|
||||
@@ -53,7 +53,7 @@ export interface ExecuteCommandResult {
|
||||
|
||||
export class GalaxyClient {
|
||||
private readonly core: Core;
|
||||
private readonly edge: EdgeGatewayClient;
|
||||
private readonly edge: GatewayClient;
|
||||
private readonly signer: Signer;
|
||||
private readonly sha256: Sha256;
|
||||
private readonly deviceSessionId: string;
|
||||
|
||||
@@ -12,6 +12,7 @@ header now — we just hand the routes down as callbacks so the
|
||||
viewer keeps its prop-driven contract.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { getContext } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
@@ -126,10 +127,10 @@ viewer keeps its prop-driven contract.
|
||||
});
|
||||
|
||||
function backToReport() {
|
||||
goto(`/games/${gameId}/report`);
|
||||
goto(withBase(`/games/${gameId}/report`));
|
||||
}
|
||||
function backToMap() {
|
||||
goto(`/games/${gameId}/map`);
|
||||
goto(withBase(`/games/${gameId}/map`));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ fractions is a Phase 21 decision documented in
|
||||
`ui/docs/science-designer-ux.md`.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { getContext, tick } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { page } from "$app/state";
|
||||
@@ -125,7 +126,7 @@ fractions is a Phase 21 decision documented in
|
||||
}
|
||||
|
||||
function backToTable(): void {
|
||||
void goto(`/games/${gameId}/table/sciences`);
|
||||
void goto(withBase(`/games/${gameId}/table/sciences`));
|
||||
}
|
||||
|
||||
async function save(): Promise<void> {
|
||||
|
||||
@@ -20,6 +20,7 @@ Phase 29 wires the wrap-mode toggle on top of the per-game `wrapMode`
|
||||
preference the store already manages.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { getContext, onDestroy, onMount, untrack } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { page } from "$app/state";
|
||||
@@ -636,14 +637,14 @@ preference the store already manages.
|
||||
const gameId = page.params.id ?? "";
|
||||
const turn = store?.report?.turn ?? 0;
|
||||
void goto(
|
||||
`/games/${gameId}/battle/${target.battleId}?turn=${turn}`,
|
||||
withBase(`/games/${gameId}/battle/${target.battleId}?turn=${turn}`),
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "bombing": {
|
||||
const gameId = page.params.id ?? "";
|
||||
void goto(
|
||||
`/games/${gameId}/report#report-bombings`,
|
||||
withBase(`/games/${gameId}/report#report-bombings`),
|
||||
).then(() => {
|
||||
if (typeof document === "undefined") return;
|
||||
const row = document.querySelector(
|
||||
|
||||
@@ -20,6 +20,7 @@ The active section is computed by the orchestrator
|
||||
`activeSlug` prop. The TOC itself owns no observers.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
import { i18n, type TranslationKey } from "$lib/i18n/index.svelte";
|
||||
@@ -62,7 +63,7 @@ The active section is computed by the orchestrator
|
||||
}
|
||||
|
||||
async function backToMap(): Promise<void> {
|
||||
await goto(`/games/${gameId}/map`);
|
||||
await goto(withBase(`/games/${gameId}/map`));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ monospace `<span>`; the rewire here is the one-liner the Phase 23
|
||||
decision log called out.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { getContext } from "svelte";
|
||||
import { page } from "$app/state";
|
||||
|
||||
@@ -47,7 +48,7 @@ decision log called out.
|
||||
</span>
|
||||
<a
|
||||
class="uuid"
|
||||
href={`/games/${gameId}/battle/${b.id}?turn=${turn}`}
|
||||
href={withBase(`/games/${gameId}/battle/${b.id}?turn=${turn}`)}
|
||||
data-testid="report-battle-row"
|
||||
data-id={b.id}
|
||||
>{b.id}</a>
|
||||
|
||||
@@ -17,6 +17,7 @@ The component sits inside the active-view slot owned by
|
||||
data fetching is performed here — the layout is responsible.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { getContext } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { page } from "$app/state";
|
||||
@@ -117,11 +118,11 @@ data fetching is performed here — the layout is responsible.
|
||||
}
|
||||
|
||||
function openDesigner(name: string): void {
|
||||
void goto(`/games/${gameId}/designer/science/${encodeURIComponent(name)}`);
|
||||
void goto(withBase(`/games/${gameId}/designer/science/${encodeURIComponent(name)}`));
|
||||
}
|
||||
|
||||
function newScience(): void {
|
||||
void goto(`/games/${gameId}/designer/science`);
|
||||
void goto(withBase(`/games/${gameId}/designer/science`));
|
||||
}
|
||||
|
||||
async function deleteScience(name: string): Promise<void> {
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
// at the first import.
|
||||
//
|
||||
// `VITE_GATEWAY_BASE_URL` is the base URL of the gateway public REST
|
||||
// surface and the Connect-Web authenticated edge (same host, same
|
||||
// port; the gateway listener serves both). It defaults to the local
|
||||
// dev address used by `tools/local-ci` and the integration suite.
|
||||
// surface. An empty value means "same origin": the single-origin
|
||||
// deployment serves the UI, the REST surface (`/api/...`), and the
|
||||
// authenticated Connect-Web edge (`/rpc/...`) behind one host, so the
|
||||
// browser issues same-origin requests and needs no absolute base. A
|
||||
// non-empty value (the Vite dev proxy, `tools/local-ci`, the
|
||||
// integration suite) points REST at that absolute host instead. The
|
||||
// Connect base is derived from this value by `gatewayRpcBaseUrl`,
|
||||
// which appends the `/rpc` routing prefix.
|
||||
//
|
||||
// `VITE_GATEWAY_RESPONSE_PUBLIC_KEY` is the gateway's response-signing
|
||||
// Ed25519 public key, encoded as standard (non-URL-safe) base64 of
|
||||
@@ -26,6 +31,21 @@ const RAW_RESPONSE_PUBLIC_KEY: string =
|
||||
|
||||
export const GATEWAY_BASE_URL: string = stripTrailingSlash(RAW_BASE_URL);
|
||||
|
||||
/**
|
||||
* gatewayRpcBaseUrl is the base URL for the authenticated Connect-Web
|
||||
* surface. The edge Caddy and the Vite dev proxy route the `/rpc`
|
||||
* prefix to the gateway's Connect listener, stripping it before the
|
||||
* request reaches the proto-derived `edge.v1.Gateway` service path.
|
||||
* When `GATEWAY_BASE_URL` is empty the gateway shares the document
|
||||
* origin, so the origin is resolved at call time from `window`.
|
||||
*/
|
||||
export function gatewayRpcBaseUrl(): string {
|
||||
const origin =
|
||||
GATEWAY_BASE_URL ||
|
||||
(typeof window !== "undefined" ? window.location.origin : "");
|
||||
return `${origin}/rpc`;
|
||||
}
|
||||
|
||||
export const GATEWAY_RESPONSE_PUBLIC_KEY: Uint8Array = decodeBase64(
|
||||
RAW_RESPONSE_PUBLIC_KEY,
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ navigation. Phase 26 introduces the history-mode entry; Phase 35
|
||||
polishes microcopy.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { onMount } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { i18n, type TranslationKey } from "$lib/i18n/index.svelte";
|
||||
@@ -41,7 +42,7 @@ polishes microcopy.
|
||||
|
||||
function go(path: string): void {
|
||||
open = false;
|
||||
void goto(path);
|
||||
void goto(withBase(path));
|
||||
}
|
||||
|
||||
function onKeyDown(event: KeyboardEvent): void {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Base-path helpers for the single-origin deployment. The game UI is
|
||||
// served under `kit.paths.base` — empty at the root for local dev,
|
||||
// vitest, and Playwright, and `/game` in the deployed single-origin
|
||||
// build. SvelteKit does not auto-prefix `goto`, `<a href>`, raw asset
|
||||
// fetches, or the service-worker scope, so every app-internal absolute
|
||||
// path is routed through `withBase`.
|
||||
//
|
||||
// `base` from `$app/paths` is the low-level primitive that `resolve()`
|
||||
// builds on. We use it directly here (rather than `resolve()`) because
|
||||
// the client navigates to and fetches dynamic, runtime-built paths
|
||||
// (command routes, `core.wasm`, the service worker) that `resolve()`'s
|
||||
// statically-typed route-id surface cannot express.
|
||||
import { base } from "$app/paths";
|
||||
|
||||
/** appBase is the configured base path (empty string at the root). */
|
||||
export const appBase = base;
|
||||
|
||||
/**
|
||||
* withBase prefixes an app-internal absolute path (leading slash) with
|
||||
* the configured base path. At the root it returns the path unchanged;
|
||||
* under the single-origin deployment it yields e.g. `/game/lobby`.
|
||||
*/
|
||||
export function withBase(path: string): string {
|
||||
return `${base}${path}`;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ exists; until then the convenience of one source of truth for
|
||||
destinations beats the duplication.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { onMount } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { i18n, type TranslationKey } from "$lib/i18n/index.svelte";
|
||||
@@ -47,13 +48,13 @@ destinations beats the duplication.
|
||||
async function selectTool(tool: MobileTool): Promise<void> {
|
||||
moreOpen = false;
|
||||
onSelectTool(tool);
|
||||
await goto(`/games/${gameId}/map`);
|
||||
await goto(withBase(`/games/${gameId}/map`));
|
||||
}
|
||||
|
||||
async function go(path: string): Promise<void> {
|
||||
moreOpen = false;
|
||||
onSelectTool("map");
|
||||
await goto(path);
|
||||
await goto(withBase(path));
|
||||
}
|
||||
|
||||
function toggleMore(): void {
|
||||
|
||||
@@ -34,6 +34,7 @@ import type {
|
||||
WeaponsBlockInput,
|
||||
WeaponsForAttackInput,
|
||||
} from "./index";
|
||||
import { withBase } from "$lib/paths";
|
||||
|
||||
/**
|
||||
* GalaxyCoreBridge is the shape Go installs on `globalThis.galaxyCore`.
|
||||
@@ -143,7 +144,7 @@ async function bootBrowserWasm(): Promise<Core> {
|
||||
throw new Error("loadWasmCore: Go runtime missing after wasm_exec.js load");
|
||||
}
|
||||
const go = new Go();
|
||||
const response = await fetch("/core.wasm");
|
||||
const response = await fetch(withBase("/core.wasm"));
|
||||
const bytes = await response.arrayBuffer();
|
||||
const { instance } = await WebAssembly.instantiate(bytes, go.importObject);
|
||||
void go.run(instance);
|
||||
@@ -156,7 +157,7 @@ async function ensureGoRuntimeLoaded(): Promise<void> {
|
||||
}
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
script.src = "/wasm_exec.js";
|
||||
script.src = withBase("/wasm_exec.js");
|
||||
script.onload = () => resolve();
|
||||
script.onerror = () => reject(new Error("failed to load /wasm_exec.js"));
|
||||
document.head.appendChild(script);
|
||||
|
||||
+26
-26
@@ -1,22 +1,22 @@
|
||||
// @generated by protoc-gen-es v2.12.0 with parameter "target=ts"
|
||||
// @generated from file galaxy/gateway/v1/edge_gateway.proto (package galaxy.gateway.v1, syntax proto3)
|
||||
// @generated from file edge/v1/edge_gateway.proto (package edge.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2";
|
||||
import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2";
|
||||
import { file_buf_validate_validate } from "../../../buf/validate/validate_pb";
|
||||
import { file_buf_validate_validate } from "../../buf/validate/validate_pb";
|
||||
import type { Message } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* Describes the file galaxy/gateway/v1/edge_gateway.proto.
|
||||
* Describes the file edge/v1/edge_gateway.proto.
|
||||
*/
|
||||
export const file_galaxy_gateway_v1_edge_gateway: GenFile = /*@__PURE__*/
|
||||
fileDesc("CiRnYWxheHkvZ2F0ZXdheS92MS9lZGdlX2dhdGV3YXkucHJvdG8SEWdhbGF4eS5nYXRld2F5LnYxIqYCChVFeGVjdXRlQ29tbWFuZFJlcXVlc3QSIQoQcHJvdG9jb2xfdmVyc2lvbhgBIAEoCUIHukgEcgIQARIiChFkZXZpY2Vfc2Vzc2lvbl9pZBgCIAEoCUIHukgEcgIQARIdCgxtZXNzYWdlX3R5cGUYAyABKAlCB7pIBHICEAESHQoMdGltZXN0YW1wX21zGAQgASgDQge6SAQiAiAAEhsKCnJlcXVlc3RfaWQYBSABKAlCB7pIBHICEAESHgoNcGF5bG9hZF9ieXRlcxgGIAEoDEIHukgEegIQARIdCgxwYXlsb2FkX2hhc2gYByABKAxCB7pIBHoCEAESGgoJc2lnbmF0dXJlGAggASgMQge6SAR6AhABEhAKCHRyYWNlX2lkGAkgASgJIrEBChZFeGVjdXRlQ29tbWFuZFJlc3BvbnNlEhgKEHByb3RvY29sX3ZlcnNpb24YASABKAkSEgoKcmVxdWVzdF9pZBgCIAEoCRIUCgx0aW1lc3RhbXBfbXMYAyABKAMSEwoLcmVzdWx0X2NvZGUYBCABKAkSFQoNcGF5bG9hZF9ieXRlcxgFIAEoDBIUCgxwYXlsb2FkX2hhc2gYBiABKAwSEQoJc2lnbmF0dXJlGAcgASgMIp4CChZTdWJzY3JpYmVFdmVudHNSZXF1ZXN0EiEKEHByb3RvY29sX3ZlcnNpb24YASABKAlCB7pIBHICEAESIgoRZGV2aWNlX3Nlc3Npb25faWQYAiABKAlCB7pIBHICEAESHQoMbWVzc2FnZV90eXBlGAMgASgJQge6SARyAhABEh0KDHRpbWVzdGFtcF9tcxgEIAEoA0IHukgEIgIgABIbCgpyZXF1ZXN0X2lkGAUgASgJQge6SARyAhABEh0KDHBheWxvYWRfaGFzaBgGIAEoDEIHukgEegIQARIaCglzaWduYXR1cmUYByABKAxCB7pIBHoCEAESFQoNcGF5bG9hZF9ieXRlcxgIIAEoDBIQCgh0cmFjZV9pZBgJIAEoCSKwAQoMR2F0ZXdheUV2ZW50EhIKCmV2ZW50X3R5cGUYASABKAkSEAoIZXZlbnRfaWQYAiABKAkSFAoMdGltZXN0YW1wX21zGAMgASgDEhUKDXBheWxvYWRfYnl0ZXMYBCABKAwSFAoMcGF5bG9hZF9oYXNoGAUgASgMEhEKCXNpZ25hdHVyZRgGIAEoDBISCgpyZXF1ZXN0X2lkGAcgASgJEhAKCHRyYWNlX2lkGAggASgJMtUBCgtFZGdlR2F0ZXdheRJlCg5FeGVjdXRlQ29tbWFuZBIoLmdhbGF4eS5nYXRld2F5LnYxLkV4ZWN1dGVDb21tYW5kUmVxdWVzdBopLmdhbGF4eS5nYXRld2F5LnYxLkV4ZWN1dGVDb21tYW5kUmVzcG9uc2USXwoPU3Vic2NyaWJlRXZlbnRzEikuZ2FsYXh5LmdhdGV3YXkudjEuU3Vic2NyaWJlRXZlbnRzUmVxdWVzdBofLmdhbGF4eS5nYXRld2F5LnYxLkdhdGV3YXlFdmVudDABQjJaMGdhbGF4eS9nYXRld2F5L3Byb3RvL2dhbGF4eS9nYXRld2F5L3YxO2dhdGV3YXl2MWIGcHJvdG8z", [file_buf_validate_validate]);
|
||||
export const file_edge_v1_edge_gateway: GenFile = /*@__PURE__*/
|
||||
fileDesc("ChplZGdlL3YxL2VkZ2VfZ2F0ZXdheS5wcm90bxIHZWRnZS52MSKmAgoVRXhlY3V0ZUNvbW1hbmRSZXF1ZXN0EiEKEHByb3RvY29sX3ZlcnNpb24YASABKAlCB7pIBHICEAESIgoRZGV2aWNlX3Nlc3Npb25faWQYAiABKAlCB7pIBHICEAESHQoMbWVzc2FnZV90eXBlGAMgASgJQge6SARyAhABEh0KDHRpbWVzdGFtcF9tcxgEIAEoA0IHukgEIgIgABIbCgpyZXF1ZXN0X2lkGAUgASgJQge6SARyAhABEh4KDXBheWxvYWRfYnl0ZXMYBiABKAxCB7pIBHoCEAESHQoMcGF5bG9hZF9oYXNoGAcgASgMQge6SAR6AhABEhoKCXNpZ25hdHVyZRgIIAEoDEIHukgEegIQARIQCgh0cmFjZV9pZBgJIAEoCSKxAQoWRXhlY3V0ZUNvbW1hbmRSZXNwb25zZRIYChBwcm90b2NvbF92ZXJzaW9uGAEgASgJEhIKCnJlcXVlc3RfaWQYAiABKAkSFAoMdGltZXN0YW1wX21zGAMgASgDEhMKC3Jlc3VsdF9jb2RlGAQgASgJEhUKDXBheWxvYWRfYnl0ZXMYBSABKAwSFAoMcGF5bG9hZF9oYXNoGAYgASgMEhEKCXNpZ25hdHVyZRgHIAEoDCKeAgoWU3Vic2NyaWJlRXZlbnRzUmVxdWVzdBIhChBwcm90b2NvbF92ZXJzaW9uGAEgASgJQge6SARyAhABEiIKEWRldmljZV9zZXNzaW9uX2lkGAIgASgJQge6SARyAhABEh0KDG1lc3NhZ2VfdHlwZRgDIAEoCUIHukgEcgIQARIdCgx0aW1lc3RhbXBfbXMYBCABKANCB7pIBCICIAASGwoKcmVxdWVzdF9pZBgFIAEoCUIHukgEcgIQARIdCgxwYXlsb2FkX2hhc2gYBiABKAxCB7pIBHoCEAESGgoJc2lnbmF0dXJlGAcgASgMQge6SAR6AhABEhUKDXBheWxvYWRfYnl0ZXMYCCABKAwSEAoIdHJhY2VfaWQYCSABKAkisAEKDEdhdGV3YXlFdmVudBISCgpldmVudF90eXBlGAEgASgJEhAKCGV2ZW50X2lkGAIgASgJEhQKDHRpbWVzdGFtcF9tcxgDIAEoAxIVCg1wYXlsb2FkX2J5dGVzGAQgASgMEhQKDHBheWxvYWRfaGFzaBgFIAEoDBIRCglzaWduYXR1cmUYBiABKAwSEgoKcmVxdWVzdF9pZBgHIAEoCRIQCgh0cmFjZV9pZBgIIAEoCTKpAQoHR2F0ZXdheRJRCg5FeGVjdXRlQ29tbWFuZBIeLmVkZ2UudjEuRXhlY3V0ZUNvbW1hbmRSZXF1ZXN0Gh8uZWRnZS52MS5FeGVjdXRlQ29tbWFuZFJlc3BvbnNlEksKD1N1YnNjcmliZUV2ZW50cxIfLmVkZ2UudjEuU3Vic2NyaWJlRXZlbnRzUmVxdWVzdBoVLmVkZ2UudjEuR2F0ZXdheUV2ZW50MAFCJVojZ2FsYXh5L2dhdGV3YXkvcHJvdG8vZWRnZS92MTtlZGdldjFiBnByb3RvMw", [file_buf_validate_validate]);
|
||||
|
||||
/**
|
||||
* @generated from message galaxy.gateway.v1.ExecuteCommandRequest
|
||||
* @generated from message edge.v1.ExecuteCommandRequest
|
||||
*/
|
||||
export type ExecuteCommandRequest = Message<"galaxy.gateway.v1.ExecuteCommandRequest"> & {
|
||||
export type ExecuteCommandRequest = Message<"edge.v1.ExecuteCommandRequest"> & {
|
||||
/**
|
||||
* protocol_version identifies the request envelope version. The gateway
|
||||
* accepts only the literal "v1" after required-field validation succeeds.
|
||||
@@ -69,16 +69,16 @@ export type ExecuteCommandRequest = Message<"galaxy.gateway.v1.ExecuteCommandReq
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message galaxy.gateway.v1.ExecuteCommandRequest.
|
||||
* Describes the message edge.v1.ExecuteCommandRequest.
|
||||
* Use `create(ExecuteCommandRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const ExecuteCommandRequestSchema: GenMessage<ExecuteCommandRequest> = /*@__PURE__*/
|
||||
messageDesc(file_galaxy_gateway_v1_edge_gateway, 0);
|
||||
messageDesc(file_edge_v1_edge_gateway, 0);
|
||||
|
||||
/**
|
||||
* @generated from message galaxy.gateway.v1.ExecuteCommandResponse
|
||||
* @generated from message edge.v1.ExecuteCommandResponse
|
||||
*/
|
||||
export type ExecuteCommandResponse = Message<"galaxy.gateway.v1.ExecuteCommandResponse"> & {
|
||||
export type ExecuteCommandResponse = Message<"edge.v1.ExecuteCommandResponse"> & {
|
||||
/**
|
||||
* @generated from field: string protocol_version = 1;
|
||||
*/
|
||||
@@ -116,16 +116,16 @@ export type ExecuteCommandResponse = Message<"galaxy.gateway.v1.ExecuteCommandRe
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message galaxy.gateway.v1.ExecuteCommandResponse.
|
||||
* Describes the message edge.v1.ExecuteCommandResponse.
|
||||
* Use `create(ExecuteCommandResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const ExecuteCommandResponseSchema: GenMessage<ExecuteCommandResponse> = /*@__PURE__*/
|
||||
messageDesc(file_galaxy_gateway_v1_edge_gateway, 1);
|
||||
messageDesc(file_edge_v1_edge_gateway, 1);
|
||||
|
||||
/**
|
||||
* @generated from message galaxy.gateway.v1.SubscribeEventsRequest
|
||||
* @generated from message edge.v1.SubscribeEventsRequest
|
||||
*/
|
||||
export type SubscribeEventsRequest = Message<"galaxy.gateway.v1.SubscribeEventsRequest"> & {
|
||||
export type SubscribeEventsRequest = Message<"edge.v1.SubscribeEventsRequest"> & {
|
||||
/**
|
||||
* protocol_version identifies the request envelope version. The gateway
|
||||
* accepts only the literal "v1" after required-field validation succeeds.
|
||||
@@ -179,16 +179,16 @@ export type SubscribeEventsRequest = Message<"galaxy.gateway.v1.SubscribeEventsR
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message galaxy.gateway.v1.SubscribeEventsRequest.
|
||||
* Describes the message edge.v1.SubscribeEventsRequest.
|
||||
* Use `create(SubscribeEventsRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const SubscribeEventsRequestSchema: GenMessage<SubscribeEventsRequest> = /*@__PURE__*/
|
||||
messageDesc(file_galaxy_gateway_v1_edge_gateway, 2);
|
||||
messageDesc(file_edge_v1_edge_gateway, 2);
|
||||
|
||||
/**
|
||||
* @generated from message galaxy.gateway.v1.GatewayEvent
|
||||
* @generated from message edge.v1.GatewayEvent
|
||||
*/
|
||||
export type GatewayEvent = Message<"galaxy.gateway.v1.GatewayEvent"> & {
|
||||
export type GatewayEvent = Message<"edge.v1.GatewayEvent"> & {
|
||||
/**
|
||||
* @generated from field: string event_type = 1;
|
||||
*/
|
||||
@@ -231,18 +231,18 @@ export type GatewayEvent = Message<"galaxy.gateway.v1.GatewayEvent"> & {
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message galaxy.gateway.v1.GatewayEvent.
|
||||
* Describes the message edge.v1.GatewayEvent.
|
||||
* Use `create(GatewayEventSchema)` to create a new message.
|
||||
*/
|
||||
export const GatewayEventSchema: GenMessage<GatewayEvent> = /*@__PURE__*/
|
||||
messageDesc(file_galaxy_gateway_v1_edge_gateway, 3);
|
||||
messageDesc(file_edge_v1_edge_gateway, 3);
|
||||
|
||||
/**
|
||||
* @generated from service galaxy.gateway.v1.EdgeGateway
|
||||
* @generated from service edge.v1.Gateway
|
||||
*/
|
||||
export const EdgeGateway: GenService<{
|
||||
export const Gateway: GenService<{
|
||||
/**
|
||||
* @generated from rpc galaxy.gateway.v1.EdgeGateway.ExecuteCommand
|
||||
* @generated from rpc edge.v1.Gateway.ExecuteCommand
|
||||
*/
|
||||
executeCommand: {
|
||||
methodKind: "unary";
|
||||
@@ -250,7 +250,7 @@ export const EdgeGateway: GenService<{
|
||||
output: typeof ExecuteCommandResponseSchema;
|
||||
},
|
||||
/**
|
||||
* @generated from rpc galaxy.gateway.v1.EdgeGateway.SubscribeEvents
|
||||
* @generated from rpc edge.v1.Gateway.SubscribeEvents
|
||||
*/
|
||||
subscribeEvents: {
|
||||
methodKind: "server_streaming";
|
||||
@@ -258,5 +258,5 @@ export const EdgeGateway: GenService<{
|
||||
output: typeof GatewayEventSchema;
|
||||
},
|
||||
}> = /*@__PURE__*/
|
||||
serviceDesc(file_galaxy_gateway_v1_edge_gateway, 0);
|
||||
serviceDesc(file_edge_v1_edge_gateway, 0);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { goto } from "$app/navigation";
|
||||
import { page } from "$app/state";
|
||||
import { dev } from "$app/environment";
|
||||
import { appBase, withBase } from "$lib/paths";
|
||||
import { i18n } from "$lib/i18n/index.svelte";
|
||||
import { session } from "$lib/session-store.svelte";
|
||||
import { eventStream } from "../api/events.svelte";
|
||||
@@ -26,7 +27,9 @@
|
||||
// in svelte.config.js) so `vite dev` and the dev-server e2e suite
|
||||
// run without the worker intercepting requests.
|
||||
if (!dev && "serviceWorker" in navigator) {
|
||||
void navigator.serviceWorker.register("/service-worker.js");
|
||||
void navigator.serviceWorker.register(withBase("/service-worker.js"), {
|
||||
scope: withBase("/"),
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
eventStream.stop();
|
||||
@@ -75,7 +78,9 @@
|
||||
streamSessionId = null;
|
||||
}
|
||||
|
||||
const pathname = page.url.pathname;
|
||||
// page.url.pathname includes the configured base path; strip it so
|
||||
// the route comparisons below stay base-agnostic.
|
||||
const pathname = page.url.pathname.slice(appBase.length);
|
||||
// Debug-only routes under /__debug/* run their own bootstrap
|
||||
// path against the storage primitives and must bypass the
|
||||
// auth guard so Phase 6's Playwright spec can drive the
|
||||
@@ -84,9 +89,9 @@
|
||||
return;
|
||||
}
|
||||
if (session.status === "anonymous" && pathname !== "/login") {
|
||||
void goto("/login", { replaceState: true });
|
||||
void goto(withBase("/login"), { replaceState: true });
|
||||
} else if (session.status === "authenticated" && pathname === "/login") {
|
||||
void goto("/lobby", { replaceState: true });
|
||||
void goto(withBase("/lobby"), { replaceState: true });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -43,6 +43,7 @@ the next game's snapshot — and the next game's selection — start
|
||||
fresh.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { onDestroy, onMount, setContext, untrack } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { page } from "$app/state";
|
||||
@@ -86,9 +87,9 @@ fresh.
|
||||
import { session } from "$lib/session-store.svelte";
|
||||
import { loadStore } from "../../../platform/store/index";
|
||||
import { loadCore } from "../../../platform/core/index";
|
||||
import { createEdgeGatewayClient } from "../../../api/connect";
|
||||
import { createGatewayClient } from "../../../api/connect";
|
||||
import { GalaxyClient } from "../../../api/galaxy-client";
|
||||
import { GATEWAY_BASE_URL, GATEWAY_RESPONSE_PUBLIC_KEY } from "$lib/env";
|
||||
import { gatewayRpcBaseUrl, GATEWAY_RESPONSE_PUBLIC_KEY } from "$lib/env";
|
||||
import {
|
||||
getSyntheticReport,
|
||||
isSyntheticGameId,
|
||||
@@ -373,7 +374,7 @@ fresh.
|
||||
if (isSyntheticGameId(gameId)) {
|
||||
const report = getSyntheticReport(gameId);
|
||||
if (report === undefined) {
|
||||
await goto("/lobby");
|
||||
await goto(withBase("/lobby"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -420,7 +421,7 @@ fresh.
|
||||
coreHolder.set(core);
|
||||
const client = new GalaxyClient({
|
||||
core,
|
||||
edge: createEdgeGatewayClient(GATEWAY_BASE_URL),
|
||||
edge: createGatewayClient(gatewayRpcBaseUrl()),
|
||||
signer: (canonical) => keypair.sign(canonical),
|
||||
sha256,
|
||||
deviceSessionId,
|
||||
@@ -472,7 +473,7 @@ fresh.
|
||||
messageParams: { from: parsed.from },
|
||||
actionLabelKey: "game.events.mail_new.action",
|
||||
onAction: () => {
|
||||
void goto(`/games/${gameId}/mail`);
|
||||
void goto(withBase(`/games/${gameId}/mail`));
|
||||
},
|
||||
durationMs: 8000,
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { goto } from "$app/navigation";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { createEdgeGatewayClient } from "../../api/connect";
|
||||
import { createGatewayClient } from "../../api/connect";
|
||||
import { GalaxyClient } from "../../api/galaxy-client";
|
||||
import {
|
||||
LobbyError,
|
||||
@@ -19,7 +20,7 @@
|
||||
} from "../../api/lobby";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
import { AccountResponse } from "../../proto/galaxy/fbs/user";
|
||||
import { GATEWAY_BASE_URL, GATEWAY_RESPONSE_PUBLIC_KEY } from "$lib/env";
|
||||
import { gatewayRpcBaseUrl, GATEWAY_RESPONSE_PUBLIC_KEY } from "$lib/env";
|
||||
import {
|
||||
SyntheticReportError,
|
||||
loadSyntheticReportFromJSON,
|
||||
@@ -184,11 +185,11 @@
|
||||
}
|
||||
|
||||
function gotoCreate(): void {
|
||||
goto("/lobby/create");
|
||||
goto(withBase("/lobby/create"));
|
||||
}
|
||||
|
||||
function gotoGame(gameId: string): void {
|
||||
goto(`/games/${gameId}/map`);
|
||||
goto(withBase(`/games/${gameId}/map`));
|
||||
}
|
||||
|
||||
async function onSyntheticFileChange(
|
||||
@@ -207,7 +208,7 @@
|
||||
const text = await file.text();
|
||||
const json: unknown = JSON.parse(text);
|
||||
const { gameId } = loadSyntheticReportFromJSON(json);
|
||||
await goto(`/games/${gameId}/map`);
|
||||
await goto(withBase(`/games/${gameId}/map`));
|
||||
} catch (err) {
|
||||
if (err instanceof SyntheticReportError) {
|
||||
syntheticError = err.message;
|
||||
@@ -250,7 +251,7 @@
|
||||
const core = await loadCore();
|
||||
client = new GalaxyClient({
|
||||
core,
|
||||
edge: createEdgeGatewayClient(GATEWAY_BASE_URL),
|
||||
edge: createGatewayClient(gatewayRpcBaseUrl()),
|
||||
signer: (canonical) => keypair.sign(canonical),
|
||||
sha256,
|
||||
deviceSessionId: session.deviceSessionId,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { goto } from "$app/navigation";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { createEdgeGatewayClient } from "../../../api/connect";
|
||||
import { createGatewayClient } from "../../../api/connect";
|
||||
import { GalaxyClient } from "../../../api/galaxy-client";
|
||||
import { LobbyError, createGame } from "../../../api/lobby";
|
||||
import { GATEWAY_BASE_URL, GATEWAY_RESPONSE_PUBLIC_KEY } from "$lib/env";
|
||||
import { gatewayRpcBaseUrl, GATEWAY_RESPONSE_PUBLIC_KEY } from "$lib/env";
|
||||
import { i18n, type TranslationKey } from "$lib/i18n/index.svelte";
|
||||
import { loadCore } from "../../../platform/core/index";
|
||||
import { session } from "$lib/session-store.svelte";
|
||||
@@ -51,7 +52,7 @@
|
||||
}
|
||||
|
||||
function cancel(): void {
|
||||
goto("/lobby");
|
||||
goto(withBase("/lobby"));
|
||||
}
|
||||
|
||||
async function submit(): Promise<void> {
|
||||
@@ -93,7 +94,7 @@
|
||||
turnSchedule: trimmedSchedule,
|
||||
targetEngineVersion: targetEngineVersion.trim() || DEFAULT_TARGET_ENGINE_VERSION,
|
||||
});
|
||||
goto("/lobby");
|
||||
goto(withBase("/lobby"));
|
||||
} catch (err) {
|
||||
formError = describeLobbyError(err);
|
||||
} finally {
|
||||
@@ -116,7 +117,7 @@
|
||||
const core = await loadCore();
|
||||
client = new GalaxyClient({
|
||||
core,
|
||||
edge: createEdgeGatewayClient(GATEWAY_BASE_URL),
|
||||
edge: createGatewayClient(gatewayRpcBaseUrl()),
|
||||
signer: (canonical) => keypair.sign(canonical),
|
||||
sha256,
|
||||
deviceSessionId: session.deviceSessionId,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { withBase } from "$lib/paths";
|
||||
import { goto } from "$app/navigation";
|
||||
import {
|
||||
AuthError,
|
||||
@@ -88,7 +89,7 @@
|
||||
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
});
|
||||
await session.signIn(result.deviceSessionId);
|
||||
void goto("/lobby", { replaceState: true });
|
||||
void goto(withBase("/lobby"), { replaceState: true });
|
||||
} catch (err) {
|
||||
if (err instanceof AuthError && err.code === "invalid_request") {
|
||||
challengeId = null;
|
||||
|
||||
@@ -9,14 +9,16 @@
|
||||
//
|
||||
// SvelteKit registers this worker automatically in the production build.
|
||||
|
||||
import { build, files, version } from "$service-worker";
|
||||
import { base, build, files, version } from "$service-worker";
|
||||
|
||||
const sw = self as unknown as ServiceWorkerGlobalScope;
|
||||
|
||||
const CACHE = `galaxy-cache-${version}`;
|
||||
// "/" is the SPA shell (adapter-static fallback); precaching it makes the
|
||||
// start_url load offline.
|
||||
const PRECACHE = ["/", ...build, ...files];
|
||||
// `${base}/` is the SPA shell (adapter-static fallback); precaching it
|
||||
// makes the start_url load offline. `base` is empty at the root and
|
||||
// `/game` under the single-origin deployment, and `$service-worker`
|
||||
// derives it from `location.pathname` so it stays correct in a subdir.
|
||||
const PRECACHE = [`${base}/`, ...build, ...files];
|
||||
|
||||
sw.addEventListener("install", (event) => {
|
||||
event.waitUntil(
|
||||
@@ -65,7 +67,7 @@ sw.addEventListener("fetch", (event) => {
|
||||
const cached = await cache.match(request);
|
||||
if (cached) return cached;
|
||||
if (request.mode === "navigate") {
|
||||
const shell = await cache.match("/");
|
||||
const shell = await cache.match(`${base}/`);
|
||||
if (shell) return shell;
|
||||
}
|
||||
throw err;
|
||||
|
||||
@@ -2,28 +2,28 @@
|
||||
"name": "Galaxy",
|
||||
"short_name": "Galaxy",
|
||||
"description": "Galaxy — a turn-based space strategy game.",
|
||||
"id": "/",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"id": "./",
|
||||
"start_url": "./",
|
||||
"scope": "./",
|
||||
"display": "standalone",
|
||||
"orientation": "any",
|
||||
"background_color": "#0a0e1a",
|
||||
"theme_color": "#0a0e1a",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon-192.png",
|
||||
"src": "icons/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-512.png",
|
||||
"src": "icons/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-maskable-512.png",
|
||||
"src": "icons/icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
|
||||
@@ -11,6 +11,13 @@ export default {
|
||||
fallback: "index.html",
|
||||
strict: true,
|
||||
}),
|
||||
paths: {
|
||||
// Base path the app is served under. Empty by default so local
|
||||
// dev, vitest, and Playwright run at the root unchanged; the
|
||||
// deployed single-origin build sets BASE_PATH=/game and the
|
||||
// edge Caddy serves the SPA under that prefix.
|
||||
base: process.env.BASE_PATH ?? "",
|
||||
},
|
||||
serviceWorker: {
|
||||
// Registered manually in the root layout for production only.
|
||||
// SvelteKit's auto-registration also runs under `vite dev`, where
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
// server picks up via `VITE_GATEWAY_RESPONSE_PUBLIC_KEY`.
|
||||
//
|
||||
// The Connect-Web request URL pattern is
|
||||
// <baseUrl>/galaxy.gateway.v1.EdgeGateway/<MethodName>
|
||||
// <baseUrl>/edge.v1.Gateway/<MethodName>
|
||||
// so the route handlers below match against the trailing path
|
||||
// suffix and ignore the host.
|
||||
|
||||
import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
|
||||
import {
|
||||
buildAccountResponsePayload,
|
||||
@@ -54,7 +54,7 @@ async function mockGatewayHappyPath(
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -105,7 +105,7 @@ async function mockGatewayHappyPath(
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async (route) => {
|
||||
// Hold the stream open until the test releases it via
|
||||
// `pendingSubscribes`. Releasing fulfils with a Connect
|
||||
|
||||
@@ -15,7 +15,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
|
||||
import { GameBattleRequest } from "../../src/proto/galaxy/fbs/battle";
|
||||
@@ -91,7 +91,7 @@ async function mockGatewayAndBattle(page: Page): Promise<void> {
|
||||
};
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
CommandPlanetRouteRemove,
|
||||
@@ -110,7 +110,7 @@ async function mockGateway(page: Page): Promise<MockHandle> {
|
||||
let submitCount = 0;
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -267,7 +267,7 @@ async function mockGateway(page: Page): Promise<MockHandle> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
import { create, toJsonString } from "@bufbuild/protobuf";
|
||||
import { webcrypto } from "node:crypto";
|
||||
import { GatewayEventSchema } from "../../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { GatewayEventSchema } from "../../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { buildEventSigningInput } from "./canon";
|
||||
import {
|
||||
FIXTURE_PRIVATE_KEY_PKCS8_BASE64,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import { create, toJson, toJsonString } from "@bufbuild/protobuf";
|
||||
import { webcrypto } from "node:crypto";
|
||||
import { ExecuteCommandResponseSchema } from "../../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandResponseSchema } from "../../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import {
|
||||
FIXTURE_PRIVATE_KEY_PKCS8_BASE64,
|
||||
decodeBase64,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
|
||||
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
|
||||
@@ -46,7 +46,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<void> {
|
||||
};
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -93,7 +93,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<void> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
|
||||
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
|
||||
@@ -53,7 +53,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockState> {
|
||||
};
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -112,7 +112,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockState> {
|
||||
// the watcher's catch path logs the abort and returns without a
|
||||
// sign-out — same convention as `tests/e2e/lobby-flow.spec.ts`.
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -30,7 +30,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
|
||||
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
|
||||
import {
|
||||
@@ -70,7 +70,7 @@ async function mockGateway(page: Page): Promise<MockState> {
|
||||
});
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -147,7 +147,7 @@ async function mockGateway(page: Page): Promise<MockState> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { GameCreateRequest } from "../../src/proto/galaxy/fbs/lobby";
|
||||
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
|
||||
import {
|
||||
@@ -74,7 +74,7 @@ async function mockGateway(page: Page, initial: Partial<LobbyState> = {}): Promi
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand", async (route) => {
|
||||
await page.route("**/edge.v1.Gateway/ExecuteCommand", async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
await route.fulfill({ status: 400 });
|
||||
@@ -208,7 +208,7 @@ async function mockGateway(page: Page, initial: Partial<LobbyState> = {}): Promi
|
||||
});
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async (route) => {
|
||||
const action = await new Promise<"endOfStream" | "abort">((resolve) => {
|
||||
mocks.pendingSubscribes.push(() => resolve("endOfStream"));
|
||||
|
||||
@@ -10,7 +10,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import { UserGamesOrderGet } from "../../src/proto/galaxy/fbs/order";
|
||||
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
|
||||
@@ -44,7 +44,7 @@ async function mockGateway(page: Page): Promise<void> {
|
||||
};
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -116,7 +116,7 @@ async function mockGateway(page: Page): Promise<void> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
|
||||
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
|
||||
@@ -57,7 +57,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<void> {
|
||||
};
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -183,7 +183,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<void> {
|
||||
// sign the session out mid-test (same convention as
|
||||
// `game-shell-map.spec.ts`).
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
UserGamesOrder,
|
||||
@@ -76,7 +76,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
let submitCalls = 0;
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -204,7 +204,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
|
||||
let subscribeServed = false;
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async (route) => {
|
||||
if (opts.subscribeFrame !== undefined && !subscribeServed) {
|
||||
subscribeServed = true;
|
||||
|
||||
@@ -12,7 +12,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
CommandPlanetProduce,
|
||||
@@ -74,7 +74,7 @@ async function mockGateway(page: Page): Promise<MockHandle> {
|
||||
let submitCount = 0;
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -187,7 +187,7 @@ async function mockGateway(page: Page): Promise<MockHandle> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -14,7 +14,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
CommandPayload,
|
||||
@@ -65,7 +65,7 @@ async function mockGateway(page: Page): Promise<MockHandle> {
|
||||
let lastVote: MockHandle["lastVote"] = null;
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -239,7 +239,7 @@ async function mockGateway(page: Page): Promise<MockHandle> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
UserGamesOrder,
|
||||
@@ -65,7 +65,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
let lastReportName = "Earth";
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -181,7 +181,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
|
||||
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
|
||||
@@ -92,7 +92,7 @@ async function mockGateway(page: Page): Promise<void> {
|
||||
const storedOrder: CommandResultFixture[] = [];
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -195,7 +195,7 @@ async function mockGateway(page: Page): Promise<void> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
CommandPayload,
|
||||
@@ -51,7 +51,7 @@ async function mockGateway(page: Page): Promise<void> {
|
||||
let storedOrder: CommandResultFixture[] = [];
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -155,7 +155,7 @@ async function mockGateway(page: Page): Promise<void> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
CommandPayload,
|
||||
@@ -94,7 +94,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
const reportSciences: ScienceFixture[] = [...(opts.initialSciences ?? [])];
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -242,7 +242,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
CommandPayload,
|
||||
@@ -88,7 +88,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
const reportClasses: ShipClassFixture[] = [...(opts.initialClasses ?? [])];
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -220,7 +220,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
|
||||
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
|
||||
@@ -52,7 +52,7 @@ async function mockGateway(page: Page): Promise<MockState> {
|
||||
});
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/ExecuteCommand",
|
||||
"**/edge.v1.Gateway/ExecuteCommand",
|
||||
async (route) => {
|
||||
const reqText = route.request().postData();
|
||||
if (reqText === null) {
|
||||
@@ -111,7 +111,7 @@ async function mockGateway(page: Page): Promise<MockState> {
|
||||
// end-of-body) are held open indefinitely so the toast stays
|
||||
// visible long enough for the test to interact with it.
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
"**/edge.v1.Gateway/SubscribeEvents",
|
||||
async (route) => {
|
||||
state.subscribeHits += 1;
|
||||
if (state.subscribeHits === 1) {
|
||||
|
||||
@@ -16,10 +16,10 @@ import {
|
||||
createRouterTransport,
|
||||
} from "@connectrpc/connect";
|
||||
import {
|
||||
EdgeGateway,
|
||||
Gateway,
|
||||
GatewayEventSchema,
|
||||
type GatewayEvent,
|
||||
} from "../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
} from "../src/proto/edge/v1/edge_gateway_pb";
|
||||
|
||||
let sessionStatus: "anonymous" | "authenticated" = "anonymous";
|
||||
const signOutSpy = vi.fn();
|
||||
@@ -91,9 +91,9 @@ function buildEvent(eventType: string, payload: Uint8Array): GatewayEvent {
|
||||
|
||||
function makeRouter(
|
||||
streamFactory: () => AsyncIterable<GatewayEvent>,
|
||||
): ReturnType<typeof createClient<typeof EdgeGateway>> {
|
||||
): ReturnType<typeof createClient<typeof Gateway>> {
|
||||
const transport = createRouterTransport(({ service }) => {
|
||||
service(EdgeGateway, {
|
||||
service(Gateway, {
|
||||
executeCommand() {
|
||||
throw new Error("not used in this test");
|
||||
},
|
||||
@@ -104,7 +104,7 @@ function makeRouter(
|
||||
},
|
||||
});
|
||||
});
|
||||
return createClient(EdgeGateway, transport);
|
||||
return createClient(Gateway, transport);
|
||||
}
|
||||
|
||||
describe("EventStream", () => {
|
||||
|
||||
@@ -16,10 +16,10 @@ import { createClient, createRouterTransport } from "@connectrpc/connect";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { GalaxyClient } from "../src/api/galaxy-client";
|
||||
import {
|
||||
EdgeGateway,
|
||||
Gateway,
|
||||
ExecuteCommandResponseSchema,
|
||||
type ExecuteCommandRequest,
|
||||
} from "../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
} from "../src/proto/edge/v1/edge_gateway_pb";
|
||||
import type {
|
||||
Core,
|
||||
RequestSigningFields,
|
||||
@@ -48,7 +48,7 @@ describe("GalaxyClient.executeCommand", () => {
|
||||
const sha256 = vi.fn(async () => new Uint8Array(32).fill(0x33));
|
||||
let captured: ExecuteCommandRequest | undefined;
|
||||
const transport = createRouterTransport(({ service }) => {
|
||||
service(EdgeGateway, {
|
||||
service(Gateway, {
|
||||
executeCommand(req) {
|
||||
captured = req;
|
||||
return create(ExecuteCommandResponseSchema, {
|
||||
@@ -66,7 +66,7 @@ describe("GalaxyClient.executeCommand", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
const edge = createClient(EdgeGateway, transport);
|
||||
const edge = createClient(Gateway, transport);
|
||||
|
||||
const client = new GalaxyClient({
|
||||
core,
|
||||
@@ -115,7 +115,7 @@ describe("GalaxyClient.executeCommand", () => {
|
||||
verifyPayloadHashImpl: () => true,
|
||||
});
|
||||
const transport = createRouterTransport(({ service }) => {
|
||||
service(EdgeGateway, {
|
||||
service(Gateway, {
|
||||
executeCommand: () =>
|
||||
create(ExecuteCommandResponseSchema, {
|
||||
protocolVersion: "v1",
|
||||
@@ -133,7 +133,7 @@ describe("GalaxyClient.executeCommand", () => {
|
||||
});
|
||||
const client = new GalaxyClient({
|
||||
core,
|
||||
edge: createClient(EdgeGateway, transport),
|
||||
edge: createClient(Gateway, transport),
|
||||
signer: async () => new Uint8Array(64),
|
||||
sha256: async () => new Uint8Array(32),
|
||||
deviceSessionId: "device-session-1",
|
||||
@@ -153,7 +153,7 @@ describe("GalaxyClient.executeCommand", () => {
|
||||
verifyPayloadHashImpl: () => false,
|
||||
});
|
||||
const transport = createRouterTransport(({ service }) => {
|
||||
service(EdgeGateway, {
|
||||
service(Gateway, {
|
||||
executeCommand: () =>
|
||||
create(ExecuteCommandResponseSchema, {
|
||||
protocolVersion: "v1",
|
||||
@@ -171,7 +171,7 @@ describe("GalaxyClient.executeCommand", () => {
|
||||
});
|
||||
const client = new GalaxyClient({
|
||||
core,
|
||||
edge: createClient(EdgeGateway, transport),
|
||||
edge: createClient(Gateway, transport),
|
||||
signer: async () => new Uint8Array(64),
|
||||
sha256: async () => new Uint8Array(32),
|
||||
deviceSessionId: "device-session-1",
|
||||
|
||||
@@ -39,11 +39,12 @@ vi.mock("../src/api/lobby", async () => {
|
||||
|
||||
vi.mock("../src/lib/env", () => ({
|
||||
GATEWAY_BASE_URL: "http://gateway.test",
|
||||
gatewayRpcBaseUrl: () => "http://gateway.test/rpc",
|
||||
GATEWAY_RESPONSE_PUBLIC_KEY: new Uint8Array(32).fill(0x55),
|
||||
}));
|
||||
|
||||
vi.mock("../src/api/connect", () => ({
|
||||
createEdgeGatewayClient: vi.fn(() => ({})),
|
||||
createGatewayClient: vi.fn(() => ({})),
|
||||
}));
|
||||
|
||||
vi.mock("../src/api/galaxy-client", () => {
|
||||
|
||||
@@ -55,11 +55,12 @@ vi.mock("../src/api/lobby", async () => {
|
||||
|
||||
vi.mock("../src/lib/env", () => ({
|
||||
GATEWAY_BASE_URL: "http://gateway.test",
|
||||
gatewayRpcBaseUrl: () => "http://gateway.test/rpc",
|
||||
GATEWAY_RESPONSE_PUBLIC_KEY: new Uint8Array(32).fill(0x55),
|
||||
}));
|
||||
|
||||
vi.mock("../src/api/connect", () => ({
|
||||
createEdgeGatewayClient: vi.fn(() => ({})),
|
||||
createGatewayClient: vi.fn(() => ({})),
|
||||
}));
|
||||
|
||||
vi.mock("../src/api/galaxy-client", () => {
|
||||
|
||||
@@ -78,9 +78,10 @@ export default defineConfig(({ mode }) => {
|
||||
target: DEV_PROXY_TARGET,
|
||||
changeOrigin: false,
|
||||
},
|
||||
"/galaxy.gateway.v1.EdgeGateway": {
|
||||
"/rpc": {
|
||||
target: DEV_GRPC_PROXY_TARGET,
|
||||
changeOrigin: false,
|
||||
rewrite: (path) => path.replace(/^\/rpc/, ""),
|
||||
// Connect-Web server-streaming
|
||||
// (`SubscribeEvents`) uses chunked HTTP
|
||||
// responses; http-proxy passes them through
|
||||
|
||||
Reference in New Issue
Block a user