Skip to content

Commit 5fcdf02

Browse files
committed
Show exact local API server URL when static login is disabled - PR_26158_035-local-api-url-guidance
1 parent 71ec738 commit 5fcdf02

11 files changed

Lines changed: 215 additions & 28 deletions

assets/theme-v2/js/gamefoundry-partials.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110

111111
const currentScript = document.currentScript || document.querySelector("script[src*='gamefoundry-partials.js']");
112112
const assetRoot = currentScript ? new URL("../", currentScript.src) : null;
113-
const apiBackedLoginDiagnostic = "Use the API-backed local server for login.";
113+
const apiBackedLoginDiagnostic = "Use the API-backed local server for login. Run npm run dev:local-api and open http://127.0.0.1:5501/login.html.";
114114

115115
function assetUrl(path) {
116116
if (!assetRoot) return rootPrefix() + path;

assets/theme-v2/js/login-session.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ const modeDisabledMessage = document.querySelector("[data-login-mode-disabled-me
1414
const userControls = document.querySelector("[data-login-user-controls]");
1515
const userStatus = document.querySelector("[data-login-user-status]");
1616
const continueLink = document.querySelector("[data-login-continue]");
17-
const apiBackedLoginDiagnostic = "Use the API-backed local server for login.";
18-
const staticModeDisabledMessage = "Use the API-backed local server for login. Local Mem and Local DB are disabled until the local API server is running.";
17+
const apiBackedLoginDiagnostic = "Use the API-backed local server for login. Run npm run dev:local-api and open http://127.0.0.1:5501/login.html.";
18+
const staticModeDisabledMessage = "Use the API-backed local server for login. Run npm run dev:local-api and open http://127.0.0.1:5501/login.html. Local Mem and Local DB are disabled until the local API server is running.";
1919

2020
function currentReturnTo() {
2121
const params = new URLSearchParams(window.location.search);

docs_build/dev/reports/coverage_changed_js_guardrail.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Missing changed runtime JS files are WARN, not FAIL.
66
Source: Playwright/Chromium built-in V8 coverage from the active Playwright run.
77

88
Changed runtime JS files considered:
9-
(100%) none changed - no changed runtime JS files
9+
(0%) src/dev-runtime/server/local-api-server.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
1010

1111
Guardrail warnings:
12-
(100%) none changed - no changed runtime JS files
12+
(0%) src/dev-runtime/server/local-api-server.mjs - WARNING: changed runtime JS file missing from coverage; advisory only
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# PR_26158_035 Local API URL Guidance Report
2+
3+
## Executive Summary
4+
5+
Updated the static-only login disabled state to tell users exactly how to start and open the API-backed local login server:
6+
7+
`npm run dev:local-api`
8+
9+
`http://127.0.0.1:5501/login.html`
10+
11+
Local Mem and Local DB remain disabled from static-only `127.0.0.1:5500`, and API-backed Local Mem / Local DB behavior is preserved when opened from the correct server URL.
12+
13+
## Implementation
14+
15+
| Area | Change | Evidence |
16+
| --- | --- | --- |
17+
| Repo script | Added `dev:local-api` as the repo-owned API-backed local server command. | `package.json` |
18+
| Local API server entrypoint | Added a small local server wrapper using the existing mock API router and static repo file serving at fixed port `5501`. | `src/dev-runtime/server/local-api-server.mjs`; `scripts/start-local-api-server.mjs` |
19+
| Login page disabled guidance | Updated the visible static-only disabled message with the exact command and URL. | `login.html`; `assets/theme-v2/js/login-session.js` |
20+
| Shared static/protected-page diagnostic | Updated the shared static login diagnostic to the same exact command and URL. | `assets/theme-v2/js/gamefoundry-partials.js` |
21+
| Static Playwright | Added assertions for the exact command and URL in both visible disabled-message locations. | `tests/playwright/tools/StaticOnlyLoginApiRequired.spec.mjs` |
22+
23+
## Server Command Discovery
24+
25+
| Finding | Result |
26+
| --- | --- |
27+
| Pre-existing fixed local API-backed login npm script | No fixed user-facing script/port existed before this PR; existing Playwright helpers used ephemeral ports. |
28+
| Exact repo script established by this PR | `npm run dev:local-api` |
29+
| Exact API-backed login URL established by this PR | `http://127.0.0.1:5501/login.html` |
30+
| Server probe | PASS; `/login.html` and `/api/session/current` both returned HTTP 200 from `127.0.0.1:5501`. |
31+
32+
## Requirement Checklist
33+
34+
| Requirement | Evidence | Status |
35+
| --- | --- | --- |
36+
| Read `docs_build/dev/PROJECT_INSTRUCTIONS.md` first. | Read before implementation and validation. | PASS |
37+
| Update static-only login disabled message to include exact local API-backed server start command and URL from repo scripts. | Message now includes `npm run dev:local-api` and `http://127.0.0.1:5501/login.html`; `package.json` owns the command. | PASS |
38+
| Discover actual npm script and port used by the local API-backed server. | Found no fixed repo script/port; added `dev:local-api` using port `5501` and validated it by server probe. | PASS |
39+
| Keep Local Mem and Local DB disabled on static-only 5500. | Static Playwright verifies Local Mem and Local DB buttons are disabled and unselected. | PASS |
40+
| Preserve API-backed Local Mem and Local DB behavior when opened from the correct server URL. | `LoginSessionMode.spec.mjs` passed 5/5. | PASS |
41+
| Do not add fallback behavior. | Static-only lane verifies no `/api/session/current` or `/api/session/` requests are made and login modes remain unusable from 5500. | PASS |
42+
| Run changed-file syntax checks. | `node --check` passed for changed JS/MJS/spec files; `package.json` parsed successfully. | PASS |
43+
| Run LoginSessionMode Playwright with API-backed server running. | `npx playwright test tests/playwright/tools/LoginSessionMode.spec.mjs` passed 5/5. | PASS |
44+
| Run static-only login Playwright proving disabled buttons and exact guidance are visible. | `npx playwright test tests/playwright/tools/StaticOnlyLoginApiRequired.spec.mjs` passed 1/1 and verifies command/URL text. | PASS |
45+
| Required reports and review artifacts generated. | This report, `testing_lane_execution_report.md`, V8 coverage report, review diff, changed files, and ZIP artifact generated for PR closeout. | PASS |
46+
47+
## Validation Commands
48+
49+
| Command | Result |
50+
| --- | --- |
51+
| `node --check assets/theme-v2/js/login-session.js` | PASS |
52+
| `node --check assets/theme-v2/js/gamefoundry-partials.js` | PASS |
53+
| `node --check tests/playwright/tools/StaticOnlyLoginApiRequired.spec.mjs` | PASS |
54+
| `node --check scripts/start-local-api-server.mjs` | PASS |
55+
| `node --check src/dev-runtime/server/local-api-server.mjs` | PASS |
56+
| `node -e "JSON.parse(require('fs').readFileSync('package.json','utf8')); console.log('package.json OK')"` | PASS |
57+
| Inline `startLocalApiServer` probe for `http://127.0.0.1:5501/login.html` and `/api/session/current` | PASS, both 200 |
58+
| `npx playwright test tests/playwright/tools/LoginSessionMode.spec.mjs` | PASS, 5/5 |
59+
| `npx playwright test tests/playwright/tools/StaticOnlyLoginApiRequired.spec.mjs` | PASS, 1/1 |
60+
| `git diff --check` | PASS, with Git line-ending warnings only |
61+
62+
## Skipped Lanes
63+
64+
| Lane | Decision | Reason |
65+
| --- | --- | --- |
66+
| ToolboxRoutePages Playwright | SKIP | No toolbox route/page rendering files were changed. |
67+
| AdminDbViewer Playwright | SKIP | Admin DB Viewer behavior and DB adapter rendering were not changed. |
68+
| ProjectJourneyTool Playwright | SKIP | Project Journey files and persistence paths were not changed. |
69+
| Full samples smoke | SKIP | No samples, sample loader, or shared sample framework changed. |
70+
71+
## Notes
72+
73+
- No CSS was added.
74+
- No fallback login behavior was added.
75+
- Existing SQLite experimental warnings and seed-only audit diagnostics appeared during API-backed validation; they were existing runtime diagnostics and did not affect the PR requirement results.

docs_build/dev/reports/playwright_v8_coverage_report.txt

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,25 @@ Note: entry percentages use function coverage when available, otherwise line cov
1212
Note: coverage entries are aggregated across every page/tool where coverageReporter.start(page) and coverageReporter.stop(page) ran.
1313

1414
Exercised tool entry points detected:
15-
(61%) Toolbox Index - exercised 3 runtime JS files
15+
(0%) Toolbox Index - not exercised by this Playwright run
1616
(0%) Tool Template V2 - not exercised by this Playwright run
17-
(81%) Theme V2 Shared JS - exercised 3 runtime JS files
17+
(70%) Theme V2 Shared JS - exercised 2 runtime JS files
1818

1919
Changed runtime JS files covered:
20-
(100%) none changed - no changed runtime JS files
20+
(0%) src/dev-runtime/server/local-api-server.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
2121

2222
Files with executed line/function counts where available:
23-
(53%) src/engine/api/server-api-client.js - executed lines 159/159; executed functions 10/19
24-
(55%) toolbox/project-journey/project-journey.js - executed lines 1003/1003; executed functions 54/99
25-
(60%) src/engine/api/mock-db-api-client.js - executed lines 19/19; executed functions 3/5
26-
(64%) assets/theme-v2/js/tool-display-mode.js - executed lines 201/201; executed functions 9/14
27-
(67%) admin/db-viewer.js - executed lines 53/53; executed functions 4/6
28-
(81%) toolbox/tool-registry-api-client.js - executed lines 148/148; executed functions 22/27
29-
(83%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 442/442; executed functions 33/40
30-
(85%) src/engine/api/mock-db-viewer-ui.js - executed lines 510/510; executed functions 82/96
31-
(88%) src/engine/api/session-api-client.js - executed lines 34/34; executed functions 7/8
32-
(89%) assets/theme-v2/js/login-session.js - executed lines 192/192; executed functions 16/18
33-
(100%) toolbox/project-journey/project-journey-api-client.js - executed lines 12/12; executed functions 2/2
23+
(7%) src/engine/api/server-api-client.js - executed lines 159/159; executed functions 1/14
24+
(13%) src/engine/api/session-api-client.js - executed lines 34/34; executed functions 1/8
25+
(59%) assets/theme-v2/js/login-session.js - executed lines 192/192; executed functions 10/17
26+
(75%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 442/442; executed functions 30/40
3427

3528
Uncovered or low-coverage changed JS files:
36-
(100%) none changed - no changed runtime JS files
29+
(0%) src/dev-runtime/server/local-api-server.mjs - WARNING: uncovered changed runtime JS file; advisory only
3730

3831
Changed JS files considered:
32+
(0%) scripts/start-local-api-server.mjs - changed JS file not collected as browser runtime coverage
33+
(0%) src/dev-runtime/server/local-api-server.mjs - changed JS file not collected as browser runtime coverage
3934
(0%) tests/playwright/tools/StaticOnlyLoginApiRequired.spec.mjs - changed JS file not collected as browser runtime coverage
40-
(83%) assets/theme-v2/js/gamefoundry-partials.js - changed JS file with browser V8 coverage
41-
(89%) assets/theme-v2/js/login-session.js - changed JS file with browser V8 coverage
35+
(59%) assets/theme-v2/js/login-session.js - changed JS file with browser V8 coverage
36+
(75%) assets/theme-v2/js/gamefoundry-partials.js - changed JS file with browser V8 coverage
Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
# PR_26158_034 Testing Lane Execution Report
1+
# PR_26158_035 Testing Lane Execution Report
22

33
## Lanes Run
44

55
| Lane | Command | Result |
66
| --- | --- | --- |
7-
| Changed-file syntax | `node --check assets/theme-v2/js/login-session.js`; `node --check tests/playwright/tools/StaticOnlyLoginApiRequired.spec.mjs` | PASS |
8-
| Static 127.0.0.1:5500 login Playwright | `npx playwright test tests/playwright/tools/StaticOnlyLoginApiRequired.spec.mjs` | PASS, 1/1 |
7+
| Changed-file syntax | `node --check assets/theme-v2/js/login-session.js`; `node --check assets/theme-v2/js/gamefoundry-partials.js`; `node --check tests/playwright/tools/StaticOnlyLoginApiRequired.spec.mjs`; `node --check scripts/start-local-api-server.mjs`; `node --check src/dev-runtime/server/local-api-server.mjs`; `node -e "JSON.parse(require('fs').readFileSync('package.json','utf8')); console.log('package.json OK')"` | PASS |
8+
| API-backed local server probe | Inline Node import of `startLocalApiServer({ host: "127.0.0.1", port: 5501 })`; fetched `/login.html` and `/api/session/current` | PASS; both returned 200 |
99
| LoginSessionMode Playwright | `npx playwright test tests/playwright/tools/LoginSessionMode.spec.mjs` | PASS, 5/5 |
10+
| Static 127.0.0.1:5500 login Playwright | `npx playwright test tests/playwright/tools/StaticOnlyLoginApiRequired.spec.mjs` | PASS, 1/1 |
1011
| Changed-file/static validation | `git diff --check` | PASS, with Git line-ending warnings only |
1112
| Runtime JS V8 coverage | Playwright coverage generated by targeted runs | PASS/WARN; `docs_build/dev/reports/playwright_v8_coverage_report.txt` and `coverage_changed_js_guardrail.txt` generated. |
1213

@@ -15,15 +16,23 @@
1516
| Check | Evidence | Result |
1617
| --- | --- | --- |
1718
| Static `127.0.0.1:5500` login keeps Local Mem and Local DB disabled. | `StaticOnlyLoginApiRequired.spec.mjs` verifies both `[data-login-mode]` buttons are disabled and not selected. | PASS |
18-
| Static `127.0.0.1:5500` login shows API-required text near disabled mode buttons. | `StaticOnlyLoginApiRequired.spec.mjs` verifies `[data-login-mode-disabled-message]` is visible and contains `Use the API-backed local server for login.` plus `Local Mem and Local DB are disabled`. | PASS |
19+
| Static login disabled message includes exact start command. | `StaticOnlyLoginApiRequired.spec.mjs` verifies `[data-login-mode-disabled-message]` and `[data-login-mode-status]` include `npm run dev:local-api`. | PASS |
20+
| Static login disabled message includes exact API-backed URL. | `StaticOnlyLoginApiRequired.spec.mjs` verifies `[data-login-mode-disabled-message]` and `[data-login-mode-status]` include `http://127.0.0.1:5501/login.html`. | PASS |
1921
| Static `127.0.0.1:5500` does not call session API routes. | Static lane records all requests and asserts no request URL contains `/api/session/current` or `/api/session/`. | PASS |
2022
| API-backed Local Mem and Local DB behavior is unchanged. | `LoginSessionMode.spec.mjs` passed 5/5, including Local DB session behavior. | PASS |
23+
| Documented API-backed URL is real. | Server probe fetched `http://127.0.0.1:5501/login.html` and `http://127.0.0.1:5501/api/session/current` with 200 responses. | PASS |
2124

2225
## Skipped Lanes
2326

2427
| Lane | Decision | Reason |
2528
| --- | --- | --- |
26-
| ToolboxRoutePages Playwright | SKIP | Toolbox route rendering was not changed by this PR. |
29+
| ToolboxRoutePages Playwright | SKIP | Tool route rendering was not changed by this PR. |
2730
| AdminDbViewer Playwright | SKIP | DB Viewer routes, DB adapters, and admin DB rendering were not changed. |
31+
| ProjectJourneyTool Playwright | SKIP | Project Journey behavior and persistence paths were not changed. |
2832
| Full samples smoke | SKIP | No samples, shared sample loader, or sample framework files changed. |
2933
| Full Playwright suite | SKIP | Static 5500 login and API-backed login cover the changed login/session behavior. |
34+
35+
## Notes
36+
37+
- Playwright and the API-backed server probe emitted existing Node SQLite experimental warnings and existing seed-only audit fallback diagnostics; they did not fail validation.
38+
- Playwright impacted: Yes. The changed behavior is login/session UI messaging and enabled/disabled state under static-only and API-backed local serving.

login.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ <h1>Login</h1>
2525
<aside class="side-menu" aria-label="Session modes">
2626
<button class="btn btn--compact" type="button" data-login-mode="local-mem">Local Mem</button>
2727
<button class="btn btn--compact" type="button" data-login-mode="local-db">Local DB</button>
28-
<p class="status" role="status" data-login-mode-disabled-message hidden>Use the API-backed local server for login. Local Mem and Local DB are disabled until the local API server is running.</p>
28+
<p class="status" role="status" data-login-mode-disabled-message hidden>Use the API-backed local server for login. Run npm run dev:local-api and open http://127.0.0.1:5501/login.html. Local Mem and Local DB are disabled until the local API server is running.</p>
2929
</aside>
3030
<div class="card">
3131
<div class="card-body content-stack">

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"test:lane:integration": "node ./scripts/run-targeted-test-lanes.mjs --lane integration",
2424
"test:lane:engine-src": "node ./scripts/run-targeted-test-lanes.mjs --lane engine-src",
2525
"test:lane:samples": "node ./scripts/run-targeted-test-lanes.mjs --lane samples --include-samples",
26+
"dev:local-api": "node ./scripts/start-local-api-server.mjs",
2627
"check:shared-extraction-guard": "node docs_build/dev/toolbox/checkSharedExtractionGuard.mjs",
2728
"check:phase24-closeout-guard": "node docs_build/dev/toolbox/checkPhase24CloseoutExecutionGuard.mjs",
2829
"check:style-system-guard": "node docs_build/dev/toolbox/checkStyleSystemGuard.mjs",

scripts/start-local-api-server.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import process from "node:process";
2+
import { startLocalApiServer } from "../src/dev-runtime/server/local-api-server.mjs";
3+
4+
const host = process.env.GAMEFOUNDRY_LOCAL_API_HOST || "127.0.0.1";
5+
const port = Number(process.env.GAMEFOUNDRY_LOCAL_API_PORT || 5501);
6+
7+
const localServer = await startLocalApiServer({ host, port });
8+
9+
console.log(`GameFoundry API-backed local server running at ${localServer.baseUrl}/login.html`);
10+
console.log("Press Ctrl+C to stop.");
11+
12+
for (const signal of ["SIGINT", "SIGTERM"]) {
13+
process.once(signal, async () => {
14+
await localServer.close();
15+
process.exit(0);
16+
});
17+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import fs from "node:fs/promises";
2+
import http from "node:http";
3+
import path from "node:path";
4+
import { fileURLToPath } from "node:url";
5+
import { createMockApiRouter } from "./mock-api-router.mjs";
6+
7+
const __filename = fileURLToPath(import.meta.url);
8+
const __dirname = path.dirname(__filename);
9+
const repoRoot = path.resolve(__dirname, "..", "..", "..");
10+
11+
function contentTypeForPath(filePath) {
12+
const extension = path.extname(filePath).toLowerCase();
13+
if (extension === ".html") return "text/html; charset=utf-8";
14+
if (extension === ".js" || extension === ".mjs") return "text/javascript; charset=utf-8";
15+
if (extension === ".json") return "application/json; charset=utf-8";
16+
if (extension === ".css") return "text/css; charset=utf-8";
17+
if (extension === ".svg") return "image/svg+xml";
18+
return "application/octet-stream";
19+
}
20+
21+
function isInsideRepoRoot(absolutePath) {
22+
const relativePath = path.relative(repoRoot, absolutePath);
23+
return relativePath === "" || (!relativePath.startsWith("..") && !path.isAbsolute(relativePath));
24+
}
25+
26+
function resolveBrowserRoutePath(decodedPath) {
27+
const normalizedPath = path.normalize(decodedPath).replace(/^(\.\.[/\\])+/, "");
28+
const webPath = normalizedPath.replace(/\\/g, "/");
29+
if (webPath === "/tools" || webPath.startsWith("/tools/")) {
30+
return `/toolbox${webPath.slice("/tools".length)}`;
31+
}
32+
return normalizedPath;
33+
}
34+
35+
export async function startLocalApiServer({
36+
host = "127.0.0.1",
37+
port = 5501,
38+
} = {}) {
39+
const handleMockApiRequest = createMockApiRouter();
40+
const server = http.createServer(async (request, response) => {
41+
try {
42+
const requestUrl = new URL(request.url || "/", `http://${host}:${port}`);
43+
if (await handleMockApiRequest(request, response, requestUrl)) {
44+
return;
45+
}
46+
const decodedPath = decodeURIComponent(requestUrl.pathname);
47+
const normalizedPath = resolveBrowserRoutePath(decodedPath);
48+
const absolutePath = path.resolve(repoRoot, `.${normalizedPath}`);
49+
if (!isInsideRepoRoot(absolutePath)) {
50+
response.statusCode = 403;
51+
response.end("Forbidden");
52+
return;
53+
}
54+
let targetPath = absolutePath;
55+
const stat = await fs.stat(targetPath).catch(() => null);
56+
if (stat && stat.isDirectory()) {
57+
targetPath = path.join(targetPath, "index.html");
58+
}
59+
const fileContents = await fs.readFile(targetPath);
60+
response.statusCode = 200;
61+
response.setHeader("Content-Type", contentTypeForPath(targetPath));
62+
response.end(fileContents);
63+
} catch {
64+
response.statusCode = 404;
65+
response.end("Not Found");
66+
}
67+
});
68+
69+
await new Promise((resolve, reject) => {
70+
server.once("error", reject);
71+
server.listen(port, host, resolve);
72+
});
73+
74+
return {
75+
baseUrl: `http://${host}:${port}`,
76+
close: async () => {
77+
await new Promise((resolve, reject) => {
78+
server.close((error) => {
79+
if (error) reject(error);
80+
else resolve();
81+
});
82+
});
83+
},
84+
server,
85+
};
86+
}

0 commit comments

Comments
 (0)