Version static app assets for CDN releases

This commit is contained in:
2026-07-30 18:37:39 -04:00
parent 792714c6d5
commit eec422661b
22 changed files with 75 additions and 75 deletions
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Account — Roast Planner</title> <title>Account — Roast Planner</title>
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#A8481A" /> <meta name="theme-color" content="#A8481A" />
</head> </head>
@@ -254,6 +254,6 @@
</main> </main>
</div> </div>
</div> </div>
<script type="module" src="/js/account.js"></script> <script type="module" src="/js/account.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Admin — Roast Planner</title> <title>Admin — Roast Planner</title>
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#A8481A" /> <meta name="theme-color" content="#A8481A" />
</head> </head>
@@ -197,6 +197,6 @@
</main> </main>
</div> </div>
</div> </div>
<script type="module" src="/js/admin.js"></script> <script type="module" src="/js/admin.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Cupping — Roast Planner</title> <title>Cupping — Roast Planner</title>
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#A8481A" /> <meta name="theme-color" content="#A8481A" />
</head> </head>
@@ -260,6 +260,6 @@
document.getElementById("cupping-session-view").classList.remove("hidden"); document.getElementById("cupping-session-view").classList.remove("hidden");
} }
</script> </script>
<script type="module" src="/js/cupping.js"></script> <script type="module" src="/js/cupping.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Reset your password — Roast Planner</title> <title>Reset your password — Roast Planner</title>
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#A8481A" /> <meta name="theme-color" content="#A8481A" />
</head> </head>
@@ -38,6 +38,6 @@
</div> </div>
</section> </section>
</main> </main>
<script type="module" src="/js/forgot.js"></script> <script type="module" src="/js/forgot.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+2 -2
View File
@@ -11,7 +11,7 @@
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/icon.svg" /> <link rel="apple-touch-icon" href="/icon.svg" />
<link rel="stylesheet" href="/worksheet.css" media="print" /> <link rel="stylesheet" href="/worksheet.css" media="print" />
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
</head> </head>
<body> <body>
<div class="app-shell"> <div class="app-shell">
@@ -2492,6 +2492,6 @@
<datalist id="cultivar-list"></datalist> <datalist id="cultivar-list"></datalist>
<script type="module" src="/js/main.js"></script> <script type="module" src="/js/main.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Inventory — Roast Planner</title> <title>Inventory — Roast Planner</title>
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#A8481A" /> <meta name="theme-color" content="#A8481A" />
</head> </head>
@@ -217,6 +217,6 @@
</main> </main>
</div> </div>
</div> </div>
<script type="module" src="/js/inventory.js"></script> <script type="module" src="/js/inventory.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+3 -3
View File
@@ -1,6 +1,6 @@
import { api, protectedFetch } from "./api.js"; import { api, protectedFetch } from "./api.js?v=20260730-release2";
import { initSideNav, loadNavUser } from "./nav.js"; import { initSideNav, loadNavUser } from "./nav.js?v=20260730-release2";
import { showToast } from "./toast.js"; import { showToast } from "./toast.js?v=20260730-release2";
function fmtDate(value) { function fmtDate(value) {
return value ? new Date(value).toLocaleString() : "—"; return value ? new Date(value).toLocaleString() : "—";
+3 -3
View File
@@ -1,6 +1,6 @@
import { api, protectedFetch } from "./api.js"; import { api, protectedFetch } from "./api.js?v=20260730-release2";
import { initSideNav, loadNavUser } from "./nav.js"; import { initSideNav, loadNavUser } from "./nav.js?v=20260730-release2";
import { showToast } from "./toast.js"; import { showToast } from "./toast.js?v=20260730-release2";
let currentUsers = []; let currentUsers = [];
let planFilterUserId = null; let planFilterUserId = null;
+5 -5
View File
@@ -1,7 +1,7 @@
import { api, protectedFetch } from "./api.js"; import { api, protectedFetch } from "./api.js?v=20260730-release2";
import { initSideNav, loadNavUser } from "./nav.js"; import { initSideNav, loadNavUser } from "./nav.js?v=20260730-release2";
import { showToast } from "./toast.js"; import { showToast } from "./toast.js?v=20260730-release2";
import { wireWhyPanels } from "./why-panels.js"; import { wireWhyPanels } from "./why-panels.js?v=20260730-release2";
import { import {
SCORE_ATTRS, SCORE_ATTRS,
SCORE_LABELS, SCORE_LABELS,
@@ -11,7 +11,7 @@ import {
MAX_FLAVOR_TAGS, MAX_FLAVOR_TAGS,
MAX_CUP_COUNT, MAX_CUP_COUNT,
computeTotalScore, computeTotalScore,
} from "/shared/cupping.js"; } from "/shared/cupping.js?v=20260730-release2";
const sessionId = new URLSearchParams(location.search).get("session"); const sessionId = new URLSearchParams(location.search).get("session");
+3 -3
View File
@@ -1,6 +1,6 @@
import { api, protectedFetch } from "./api.js"; import { api, protectedFetch } from "./api.js?v=20260730-release2";
import { initSideNav, loadNavUser } from "./nav.js"; import { initSideNav, loadNavUser } from "./nav.js?v=20260730-release2";
import { showToast } from "./toast.js"; import { showToast } from "./toast.js?v=20260730-release2";
let lots = []; let lots = [];
let showArchived = false; let showArchived = false;
+2 -2
View File
@@ -1,5 +1,5 @@
import { api, protectedFetch } from "./api.js"; import { api, protectedFetch } from "./api.js?v=20260730-release2";
import { showToast } from "./toast.js"; import { showToast } from "./toast.js?v=20260730-release2";
let lots = []; let lots = [];
+13 -13
View File
@@ -1,19 +1,19 @@
import { FIELD_IDS, blankPlan } from "/shared/fields.js"; import { FIELD_IDS, blankPlan } from "/shared/fields.js?v=20260730-release2";
import { computeLedger } from "/shared/ledger.js"; import { computeLedger } from "/shared/ledger.js?v=20260730-release2";
import { import {
formatDuration, formatDuration,
formatSigned, formatSigned,
parseRangeMidpoint, parseRangeMidpoint,
} from "/shared/time.js"; } from "/shared/time.js?v=20260730-release2";
import { CULTIVARS, findCultivar } from "/shared/reference-data.js"; import { CULTIVARS, findCultivar } from "/shared/reference-data.js?v=20260730-release2";
import { buildPlanCurve, pointsToPathD, tToX, tempToY } from "/shared/curve.js"; import { buildPlanCurve, pointsToPathD, tToX, tempToY } from "/shared/curve.js?v=20260730-release2";
import { initPrefillPanel } from "./prefill-ui.js"; import { initPrefillPanel } from "./prefill-ui.js?v=20260730-release2";
import { initAlogPanel } from "./alog-ui.js"; import { initAlogPanel } from "./alog-ui.js?v=20260730-release2";
import { initPrint } from "./print.js"; import { initPrint } from "./print.js?v=20260730-release2";
import { initLotPicker } from "./lot-picker.js"; import { initLotPicker } from "./lot-picker.js?v=20260730-release2";
import { protectedFetch, csrfToken } from "./api.js"; import { protectedFetch, csrfToken } from "./api.js?v=20260730-release2";
import { initSideNav } from "./nav.js"; import { initSideNav } from "./nav.js?v=20260730-release2";
import { wireWhyPanels } from "./why-panels.js"; import { wireWhyPanels } from "./why-panels.js?v=20260730-release2";
const FIELD_ID_SET = new Set(FIELD_IDS); const FIELD_ID_SET = new Set(FIELD_IDS);
const STORAGE_PREFIX = "roastPlannerPlan.v2"; const STORAGE_PREFIX = "roastPlannerPlan.v2";
@@ -903,7 +903,7 @@ function wirePwa() {
const hadController = !!navigator.serviceWorker.controller; const hadController = !!navigator.serviceWorker.controller;
window.addEventListener("load", () => { window.addEventListener("load", () => {
navigator.serviceWorker navigator.serviceWorker
.register("/sw.js") .register("/sw.js?v=20260730-release2")
.then((registration) => { .then((registration) => {
const showUpdate = () => const showUpdate = () =>
document.getElementById("btn-refresh").classList.remove("hidden"); document.getElementById("btn-refresh").classList.remove("hidden");
+1 -1
View File
@@ -8,7 +8,7 @@
name="description" name="description"
content="A structured worksheet that computes first crack, development, and drop from your bean — then keeps the plan next to what actually happened." content="A structured worksheet that computes first crack, development, and drop from your bean — then keeps the plan next to what actually happened."
/> />
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#2A1D16" /> <meta name="theme-color" content="#2A1D16" />
</head> </head>
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Log in — Roast Planner</title> <title>Log in — Roast Planner</title>
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#A8481A" /> <meta name="theme-color" content="#A8481A" />
</head> </head>
@@ -44,6 +44,6 @@
</div> </div>
</section> </section>
</main> </main>
<script type="module" src="/js/login.js"></script> <script type="module" src="/js/login.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Set a new password — Roast Planner</title> <title>Set a new password — Roast Planner</title>
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#A8481A" /> <meta name="theme-color" content="#A8481A" />
</head> </head>
@@ -46,6 +46,6 @@
</div> </div>
</section> </section>
</main> </main>
<script type="module" src="/js/reset.js"></script> <script type="module" src="/js/reset.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Administrator setup — Roast Planner</title> <title>Administrator setup — Roast Planner</title>
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#A8481A" /> <meta name="theme-color" content="#A8481A" />
</head> </head>
@@ -58,6 +58,6 @@
</div> </div>
</section> </section>
</main> </main>
<script type="module" src="/js/setup.js"></script> <script type="module" src="/js/setup.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+2 -2
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Create an account — Roast Planner</title> <title>Create an account — Roast Planner</title>
<link rel="stylesheet" href="/app.css?v=20260730-5ef8274" /> <link rel="stylesheet" href="/app.css?v=20260730-release2" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" /> <link rel="icon" href="/icon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#A8481A" /> <meta name="theme-color" content="#A8481A" />
</head> </head>
@@ -55,6 +55,6 @@
</div> </div>
</section> </section>
</main> </main>
<script type="module" src="/js/signup.js"></script> <script type="module" src="/js/signup.js?v=20260730-release2"></script>
</body> </body>
</html> </html>
+20 -20
View File
@@ -1,4 +1,4 @@
const CACHE_NAME = "roast-planner-static-v7"; const CACHE_NAME = "roast-planner-static-v8";
// Only /app is precached as a navigable page: it is a data-free authenticated shell (draft // Only /app is precached as a navigable page: it is a data-free authenticated shell (draft
// data lives only in account-scoped local storage, and logout clears that namespace), so it is // data lives only in account-scoped local storage, and logout clears that namespace), so it is
// the one page that is both safe and useful to relaunch offline. The marketing page, auth // the one page that is both safe and useful to relaunch offline. The marketing page, auth
@@ -7,25 +7,25 @@ const CACHE_NAME = "roast-planner-static-v7";
// stale content. // stale content.
const APP_SHELL = [ const APP_SHELL = [
"/app", "/app",
"/app.css?v=20260730-5ef8274", "/app.css?v=20260730-release2",
"/worksheet.css", "/worksheet.css?v=20260730-release2",
"/manifest.webmanifest", "/manifest.webmanifest?v=20260730-release2",
"/icon.svg", "/icon.svg?v=20260730-release2",
"/js/main.js", "/js/main.js?v=20260730-release2",
"/js/api.js", "/js/api.js?v=20260730-release2",
"/js/nav.js", "/js/nav.js?v=20260730-release2",
"/js/toast.js", "/js/toast.js?v=20260730-release2",
"/js/why-panels.js", "/js/why-panels.js?v=20260730-release2",
"/js/lot-picker.js", "/js/lot-picker.js?v=20260730-release2",
"/js/prefill-ui.js", "/js/prefill-ui.js?v=20260730-release2",
"/js/alog-ui.js", "/js/alog-ui.js?v=20260730-release2",
"/js/print.js", "/js/print.js?v=20260730-release2",
"/shared/fields.js", "/shared/fields.js?v=20260730-release2",
"/shared/ledger.js", "/shared/ledger.js?v=20260730-release2",
"/shared/time.js", "/shared/time.js?v=20260730-release2",
"/shared/reference-data.js", "/shared/reference-data.js?v=20260730-release2",
"/shared/curve.js", "/shared/curve.js?v=20260730-release2",
"/shared/cupping.js", "/shared/cupping.js?v=20260730-release2",
]; ];
self.addEventListener("install", (event) => { self.addEventListener("install", (event) => {
+1 -1
View File
@@ -1,7 +1,7 @@
// Browser-safe. Geometry for the roast-curve SVG grid (viewBox 0 0 714 334), matching // Browser-safe. Geometry for the roast-curve SVG grid (viewBox 0 0 714 334), matching
// manual-roast-planner.html's worksheet box 10 grid exactly. // manual-roast-planner.html's worksheet box 10 grid exactly.
import { MACHINE } from "./reference-data.js"; import { MACHINE } from "./reference-data.js?v=20260730-release2";
export const PLOT = { x0: 52, x1: 652, y0: 24, y1: 294, tMaxS: 900 }; export const PLOT = { x0: 52, x1: 652, y0: 24, y1: 294, tMaxS: 900 };
+2 -2
View File
@@ -2,8 +2,8 @@
// Mirrors manual-roast-planner.html's worksheet box 6 (lines 1-9, totals A/C/D) // Mirrors manual-roast-planner.html's worksheet box 6 (lines 1-9, totals A/C/D)
// and box 7 (the four sanity checks) exactly. Imported by both server and browser. // and box 7 (the four sanity checks) exactly. Imported by both server and browser.
import { parseDuration, parseRangeMidpoint } from "./time.js"; import { parseDuration, parseRangeMidpoint } from "./time.js?v=20260730-release2";
import { YELLOW_RATIO, SANITY_BANDS } from "./reference-data.js"; import { YELLOW_RATIO, SANITY_BANDS } from "./reference-data.js?v=20260730-release2";
/** /**
* @param {import("./fields.js").Plan} plan * @param {import("./fields.js").Plan} plan
+1 -1
View File
@@ -2,7 +2,7 @@
// (Step 1 cultivar table, Step 3 processing table, Step 4 roast-level table, Step 8 machine bands, // (Step 1 cultivar table, Step 3 processing table, Step 4 roast-level table, Step 8 machine bands,
// Step 7 sanity bands). Keep this the single source of truth — don't retype these numbers elsewhere. // Step 7 sanity bands). Keep this the single source of truth — don't retype these numbers elsewhere.
import { parseRangeMidpoint } from "./time.js"; import { parseRangeMidpoint } from "./time.js?v=20260730-release2";
export const YELLOW_RATIO = 0.56; export const YELLOW_RATIO = 0.56;
const DEFAULT_DEV_THIRD_S = 90; // "1:30", the modal third-profile-number most cultivar rows share const DEFAULT_DEV_THIRD_S = 90; // "1:30", the modal third-profile-number most cultivar rows share
+1 -1
View File
@@ -25,7 +25,7 @@ test("strict CSP/static modules, no-store data, auth lifecycle, and ownership sh
assert.equal(login.status, 200); assert.equal(login.status, 200);
assert.match( assert.match(
login.text, login.text,
/<script type="module" src="\/js\/login\.js"><\/script>/, /<script type="module" src="\/js\/login\.js\?v=[^"]+"><\/script>/,
); );
assert.equal((await anonymous.get("/signup")).status, 200); assert.equal((await anonymous.get("/signup")).status, 200);
assert.equal((await anonymous.get("/forgot")).status, 200); assert.equal((await anonymous.get("/forgot")).status, 200);