Skip to content

Add optional ServerAPI hooks + API stubs for server-side autosave#1306

Closed
SUNsung wants to merge 4 commits intoAzgaar:masterfrom
SUNsung:master
Closed

Add optional ServerAPI hooks + API stubs for server-side autosave#1306
SUNsung wants to merge 4 commits intoAzgaar:masterfrom
SUNsung:master

Conversation

@SUNsung
Copy link

@SUNsung SUNsung commented Feb 4, 2026

Description

This PR introduces an optional server integration layer without changing the default (fully client-side) behavior.

  • Added public/modules/io/server-api.js that exposes a small, vanilla API via window.ServerAPI (getJson, postData, checkData).
  • Added server stub endpoints under /api (e.g. info.json, load-map.json, catch-error.json) to enable seamless local/proxy setups.
  • Hooked existing flows to optionally report:
    • map saves (saveToStorage → POST api/load-map.json)
    • autosave errors (initiateAutosave catch → POST api/catch-error.json)
    • saveMap errors (saveMap catch → POST api/catch-error.json)
  • Included server-api.js in index.html.

By default nothing changes: if /api/info.json is not available (or reports disabled), ServerAPI becomes a no-op and the app works exactly as before.

Motivation: make it easy to add server support later by simply routing /api/* to a backend. For now it allows catching autosaves and error reports; in future it can be extended to fetch server-provided presets/configs for City Generator / Village Generator to enable a fully local ecosystem.

Type of change

  • Bug fix
  • New feature
  • Refactoring / style
  • Documentation update / chore
  • Other (please describe): Optional server integration hooks (non-breaking)

Versioning

  • Version is updated
  • Changed files hash is updated

@netlify
Copy link

netlify bot commented Feb 4, 2026

Deploy Preview for afmg ready!

Name Link
🔨 Latest commit 103153e
🔍 Latest deploy log https://app.netlify.com/projects/afmg/deploys/6983a73312de5600083446a4
😎 Deploy Preview https://deploy-preview-1306--afmg.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an optional “server integration” layer that can be enabled via /api/info.json, allowing the app to report autosaves/saves and client errors to a backend while preserving default client-only behavior when disabled.

Changes:

  • Introduces window.ServerAPI with getJson / postData helpers and an enablement probe against /api/info.json
  • Hooks save/autosave flows to optionally POST map data and error reports to /api/*
  • Adds /public/api/*.json placeholder endpoints and wires server-api.js into index.html

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/index.html Loads the new ServerAPI module before existing IO modules
public/modules/io/server-api.js Implements the optional server API wrapper and enablement probe
public/modules/io/save.js Adds optional server POST hooks for saves and error paths
public/api/info.json Placeholder probe response indicating server integration disabled by default
public/api/load-map.json Placeholder “load/save map” endpoint response
public/api/catch-error.json Placeholder “catch error” endpoint response

Comment on lines +1 to +6
"use strict";

window.ServerAPI = (function () {
const base = "./";
let ok = false;

Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module mixes 4-space indentation and spacing that differs from other public/modules/io/*.js files (e.g. public/modules/io/load.js:1+ uses 2-space indentation). Please reformat server-api.js to match the established style to reduce diff noise and keep consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +98 to +102
if (!res.ok) {
const payload = await readJsonSafe(res);
console.error("ServerAPI GET failed:", { url, status: res.status, payload });
return null;
}
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getJson logs console.error on any non-2xx response. For the initial /api/info.json probe, a missing endpoint (404) is an expected “server disabled” state per PR description, so this will create noisy errors in console (and can break setups that treat console errors as failures). Consider treating 404 / network failures for the probe as a silent disabled state (or downgrade to console.info/debug).

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +66
if (looksJson) {
try {
const data = await res.json();
ok = checkData(data);
return data;

} catch (e) {
console.error("ServerAPI JSON-parse failed:", e);
ok = false;
return null;
}
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok is mutated on every response parse (ok = checkData(data)), meaning a single endpoint returning {ok:false,...} (or a transient parse failure) will globally disable all future postData calls. This makes server enablement fragile and can be flipped by unrelated requests; consider separating “server enabled” (set once from info.json) from per-request success, and avoid toggling the global flag based on arbitrary response bodies.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +116
async function postData(path, mapData, params) {
if (!ok) {return null;}

const url = buildUrl(path, params);
const body = toTextBlob(mapData);

Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

postData hard-gates on the module-level ok flag. Because ok is only set asynchronously after the startup getJson("api/info.json") completes, any early calls to postData (e.g. user saving immediately on load) will be dropped even if the server is actually enabled. Consider exposing/awaiting a readiness promise or queueing calls until the enablement probe finishes.

Copilot uses AI. Check for mistakes.
if (method === "dropbox") saveToDropbox(mapData, filename);
} catch (error) {
ERROR && console.error(error);
await window.ServerAPI.postData("api/catch-error.json", error, {point: "saveMap", timestamp: Date.now()});
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error is passed directly into ServerAPI.postData. In server-api.js, non-Blob inputs are turned into a text Blob via new Blob([mapData]), so an Error object will typically serialize to "[object Error]" and lose message/stack. Serialize the error explicitly (e.g., {message, stack, name} or parseError(error)) before sending.

Suggested change
await window.ServerAPI.postData("api/catch-error.json", error, {point: "saveMap", timestamp: Date.now()});
const serializedError = {
name: error && error.name,
message: error && error.message,
stack: error && error.stack,
toString: String(error)
};
await window.ServerAPI.postData(
"api/catch-error.json",
JSON.stringify(serializedError),
{point: "saveMap", timestamp: Date.now()}
);

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +5
{
"ok": false,
"info": "server not enabled",
"data": {}
} No newline at end of file
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These /public/api/*.json files are static assets. They can be fetched via GET, but they will not act as stub handlers for POST requests to api/load-map.json / api/catch-error.json on most static/dev servers (typically 404/405), so the “stub endpoint” behavior described won’t be testable without an actual backend/proxy or dev middleware. Consider adding a Vite dev-server middleware/mock for /api/* POSTs or clarifying the intended usage.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +5
{
"ok": false,
"info": "server not enabled",
"data": {}
} No newline at end of file
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like load-map.json, this is a static JSON asset and won’t behave as a stub POST endpoint on most servers, so ServerAPI.postData("api/catch-error.json", ...) can’t be exercised without a backend/proxy or dev middleware. Consider adding a dev mock handler for POST /api/* or documenting that these files are GET-only placeholders.

Copilot uses AI. Check for mistakes.
async function saveToStorage(mapData, showTip = false) {
const blob = new Blob([mapData], { type: "text/plain" });
await ldb.set("lastMap", blob);
await window.ServerAPI.postData("api/load-map.json", blob, {showTip: showTip});
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saveToStorage now awaits a network call (ServerAPI.postData). When server mode is enabled, this makes local saves/autosaves block on network latency/failures, which can noticeably slow autosave and introduce UI timing issues. Consider making the server hook fire-and-forget (no await) or adding a short timeout so local persistence isn’t delayed by the server integration.

Suggested change
await window.ServerAPI.postData("api/load-map.json", blob, {showTip: showTip});
if (window.ServerAPI && typeof window.ServerAPI.postData === "function") {
// fire-and-forget server sync so local saves/autosaves are not blocked by network latency
window.ServerAPI
.postData("api/load-map.json", blob, {showTip: showTip})
.catch(error => {
ERROR && console.error(error);
});
}

Copilot uses AI. Check for mistakes.
lastSavedAt = Date.now();
} catch (error) {
ERROR && console.error(error);
await window.ServerAPI.postData("api/catch-error.json", error, {point: "initiateAutosave", timestamp: Date.now()});
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as the saveMap catch: posting the raw error object will not reliably include useful details (often becomes "[object Error]"). Serialize the error fields you need (message/stack/name + context) before sending to the server.

Suggested change
await window.ServerAPI.postData("api/catch-error.json", error, {point: "initiateAutosave", timestamp: Date.now()});
const errorPayload = {
name: error && error.name,
message: error && error.message,
stack: error && error.stack,
stringified: String(error)
};
await window.ServerAPI.postData(
"api/catch-error.json",
errorPayload,
{point: "initiateAutosave", timestamp: Date.now()}
);

Copilot uses AI. Check for mistakes.
<script defer src="modules/coa-renderer.js?v=1.99.00"></script>
<script defer src="libs/rgbquant.min.js"></script>
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
<script defer src="modules/io/server-api.js"></script>
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

server-api.js is included without the cache-busting ?v=... query used for other internal modules in this script block, which can leave clients with a stale cached copy after deploys. Add a version/hash parameter consistent with the surrounding modules/* scripts (and update it when changing the file).

Copilot uses AI. Check for mistakes.
@SUNsung SUNsung closed this Feb 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants