diff --git a/assets/scripts/games/two-rivers/core/content.mjs b/assets/scripts/games/two-rivers/core/content.js
similarity index 97%
rename from assets/scripts/games/two-rivers/core/content.mjs
rename to assets/scripts/games/two-rivers/core/content.js
index fca5667..85dd3bc 100644
--- a/assets/scripts/games/two-rivers/core/content.mjs
+++ b/assets/scripts/games/two-rivers/core/content.js
@@ -1,3 +1,4 @@
+// Served as .js so strict production MIME checks accept this native ES module.
const ROOT = "/assets/games/two-rivers/data";
const FILES = {
assets: "assets.json",
diff --git a/assets/scripts/games/two-rivers/core/save.mjs b/assets/scripts/games/two-rivers/core/save.js
similarity index 98%
rename from assets/scripts/games/two-rivers/core/save.mjs
rename to assets/scripts/games/two-rivers/core/save.js
index aee61f5..9e10526 100644
--- a/assets/scripts/games/two-rivers/core/save.mjs
+++ b/assets/scripts/games/two-rivers/core/save.js
@@ -1,4 +1,4 @@
-import {normalizeState} from "./state.mjs";
+import {normalizeState} from "./state.js";
export const SAVE_KEY = "zxh_two_rivers_saves_v1";
export const SETTINGS_KEY = "zxh_two_rivers_settings_v1";
diff --git a/assets/scripts/games/two-rivers/core/state.mjs b/assets/scripts/games/two-rivers/core/state.js
similarity index 99%
rename from assets/scripts/games/two-rivers/core/state.mjs
rename to assets/scripts/games/two-rivers/core/state.js
index 597255d..81f92af 100644
--- a/assets/scripts/games/two-rivers/core/state.mjs
+++ b/assets/scripts/games/two-rivers/core/state.js
@@ -1,3 +1,4 @@
+// Native ES module; the .js suffix matches the production server's JavaScript MIME mapping.
export const RELATIONSHIPS = ["trust", "respect", "comfort", "humour", "adventure", "romance", "familyApproval"];
export const SAVE_VERSION = 1;
diff --git a/assets/scripts/games/two-rivers/main.js b/assets/scripts/games/two-rivers/main.js
index c3c0ff2..cc5210f 100644
--- a/assets/scripts/games/two-rivers/main.js
+++ b/assets/scripts/games/two-rivers/main.js
@@ -1,9 +1,9 @@
-import {loadContent} from "./core/content.mjs";
-import {createStore, freshState} from "./core/state.mjs";
-import {createSaveService} from "./core/save.mjs";
-import {AudioDirector} from "./systems/audio.mjs";
-import {Interface} from "./ui/interface.mjs";
-import {createScenes} from "./scenes/game-scenes.mjs";
+import {loadContent} from "./core/content.js";
+import {createStore, freshState} from "./core/state.js";
+import {createSaveService} from "./core/save.js";
+import {AudioDirector} from "./systems/audio.js";
+import {Interface} from "./ui/interface.js";
+import {createScenes} from "./scenes/game-scenes.js";
const root = document.querySelector("[data-two-rivers]");
diff --git a/assets/scripts/games/two-rivers/scenes/game-scenes.mjs b/assets/scripts/games/two-rivers/scenes/game-scenes.js
similarity index 99%
rename from assets/scripts/games/two-rivers/scenes/game-scenes.mjs
rename to assets/scripts/games/two-rivers/scenes/game-scenes.js
index b5b8f86..fb5659a 100644
--- a/assets/scripts/games/two-rivers/scenes/game-scenes.mjs
+++ b/assets/scripts/games/two-rivers/scenes/game-scenes.js
@@ -1,4 +1,4 @@
-import {requirementMet} from "../core/state.mjs";
+import {requirementMet} from "../core/state.js";
const WIDTH = 1280;
const HEIGHT = 720;
diff --git a/assets/scripts/games/two-rivers/systems/audio.mjs b/assets/scripts/games/two-rivers/systems/audio.js
similarity index 97%
rename from assets/scripts/games/two-rivers/systems/audio.mjs
rename to assets/scripts/games/two-rivers/systems/audio.js
index 8b2a918..dc554b3 100644
--- a/assets/scripts/games/two-rivers/systems/audio.mjs
+++ b/assets/scripts/games/two-rivers/systems/audio.js
@@ -1,3 +1,4 @@
+// Native ES module served with the production JavaScript MIME type.
export class AudioDirector {
constructor(records, getSettings) {
this.getSettings = getSettings;
diff --git a/assets/scripts/games/two-rivers/ui/interface.mjs b/assets/scripts/games/two-rivers/ui/interface.js
similarity index 99%
rename from assets/scripts/games/two-rivers/ui/interface.mjs
rename to assets/scripts/games/two-rivers/ui/interface.js
index 5752d9d..7132bf2 100644
--- a/assets/scripts/games/two-rivers/ui/interface.mjs
+++ b/assets/scripts/games/two-rivers/ui/interface.js
@@ -1,4 +1,4 @@
-import {applyEffects, relationshipStage, RELATIONSHIPS, requirementMet, resolveEnding} from "../core/state.mjs";
+import {applyEffects, relationshipStage, RELATIONSHIPS, requirementMet, resolveEnding} from "../core/state.js";
const LABELS = {
trust: "Trust", respect: "Respect", comfort: "Comfort", humour: "Humour",
diff --git a/play/two-rivers.org b/play/two-rivers.org
index f7696ac..d912e5b 100644
--- a/play/two-rivers.org
+++ b/play/two-rivers.org
@@ -98,6 +98,6 @@
-
+
#+END_EXPORT
diff --git a/tests/two-rivers/game.test.mjs b/tests/two-rivers/game.test.mjs
index 220b80e..78cbbc2 100644
--- a/tests/two-rivers/game.test.mjs
+++ b/tests/two-rivers/game.test.mjs
@@ -6,9 +6,9 @@ import {fileURLToPath} from "node:url";
import {
applyEffects, createStore, freshState, normalizeState, relationshipStage,
resolveEnding, requirementMet, RELATIONSHIPS
-} from "../../assets/scripts/games/two-rivers/core/state.mjs";
-import {validateContent} from "../../assets/scripts/games/two-rivers/core/content.mjs";
-import {createSaveService, SAVE_KEY} from "../../assets/scripts/games/two-rivers/core/save.mjs";
+} from "../../assets/scripts/games/two-rivers/core/state.js";
+import {validateContent} from "../../assets/scripts/games/two-rivers/core/content.js";
+import {createSaveService, SAVE_KEY} from "../../assets/scripts/games/two-rivers/core/save.js";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const read = (file) => fs.readFileSync(path.join(root, file), "utf8");
@@ -134,17 +134,19 @@ test("page uses local pinned libraries and production source is clean", () => {
assert.match(page, /gsap-3\.13\.0\.min\.js/);
assert.match(page, /howler-2\.2\.4\.min\.js/);
assert.match(page, /games\/two-rivers\/gate\.js\?v=1\.0\.1/);
+ assert.match(page, /games\/two-rivers\/main\.js\?v=1\.0\.2/);
assert.doesNotMatch(page, /https?:\/\//);
const source = [
page,
read("assets/scripts/games/two-rivers/main.js"),
read("assets/scripts/games/two-rivers/gate.js"),
- read("assets/scripts/games/two-rivers/core/state.mjs"),
- read("assets/scripts/games/two-rivers/ui/interface.mjs"),
- read("assets/scripts/games/two-rivers/scenes/game-scenes.mjs")
+ read("assets/scripts/games/two-rivers/core/state.js"),
+ read("assets/scripts/games/two-rivers/ui/interface.js"),
+ read("assets/scripts/games/two-rivers/scenes/game-scenes.js")
].join("\n");
assert.doesNotMatch(source, /\bTODO\b|placeholder implementation|lorem ipsum/i);
assert.doesNotMatch(source, /\bReact\b|\bVue\b|\bAngular\b|\bjQuery\b/);
+ assert.doesNotMatch(source, /\.mjs(?:["'?#]|$)/, "production runtime must use the server's JavaScript MIME mapping");
assert.ok(read("assets/styles/pages/two-rivers.css").length > 15000);
});