Skip to content

Commit a02e149

Browse files
A preview can hold every resource in its loading or error arm
An editor's canvas sets data-wf-resource="loading" or "error" on the page's root, and each resource reads as in that arm, an error it has not met reading as a 503 a server gives, so a page is designed as it loads and as it fails without breaking its requests. The request still runs and its answer is kept for when the arm is let go. A signal, so the page follows the attribute as it comes and goes, as data-wf-validation does.
1 parent 68465aa commit a02e149

2 files changed

Lines changed: 60 additions & 3 deletions

File tree

‎src/runtime/modules/net.js‎

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,39 @@
241241
/// that fails keeps the data and "ready", and sets `error()` — until the
242242
/// next answer clears it — so a page can say the data is stale.
243243
/// `pending()` is true while any request of it runs, either kind.
244+
/// The arm an editor's preview holds every resource in — the page's root
245+
/// carrying `data-wf-resource="loading"` or `"error"` — so a page can be
246+
/// designed as it loads and as it fails without breaking its requests.
247+
/// A signal, so the page follows the attribute as it comes and goes.
248+
let heldArm = null;
249+
function armHeld() {
250+
if (typeof document === "undefined" || !document.documentElement) return null;
251+
if (!heldArm) {
252+
const root = document.documentElement;
253+
const read = () => {
254+
const v = root.getAttribute("data-wf-resource");
255+
return v === "loading" || v === "error" ? v : null;
256+
};
257+
heldArm = signal(read());
258+
if (typeof MutationObserver === "function") {
259+
new MutationObserver(() => heldArm.set(read()))
260+
.observe(root, { attributes: true, attributeFilter: ["data-wf-resource"] });
261+
}
262+
}
263+
return heldArm();
264+
}
265+
244266
function resource(url, options) {
245-
const state = signal("loading");
267+
const real = signal("loading");
246268
const data = signal(null);
247-
const error = signal(null);
269+
const failed = signal(null);
270+
// What the page reads: the request's own state and error, unless the
271+
// preview holds the arm, when an error it has not met reads as one a
272+
// server gives.
273+
const state = () => armHeld() || real();
274+
state.set = (v) => real.set(v);
275+
const error = () => armHeld() === "error" ? failed() || netError("status", "503 Service Unavailable", { status: 503 }) : failed();
276+
error.set = (v) => failed.set(v);
248277
const refreshing = signal(false);
249278
const pending = signal(false);
250279
const opts = requestOptions(options);

‎tests/js/net.test.mjs‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function load(fetchImpl, { online = true } = {}) {
2323
window, document, Node, Element, DocumentFragment, setTimeout, clearTimeout,
2424
fetchImpl, navigator, globalThis.AbortController, class {},
2525
);
26-
return { WF };
26+
return { WF, document };
2727
}
2828

2929
/// Every microtask the engine has queued, run.
@@ -167,3 +167,31 @@ test("the network is a value a page can read", () => {
167167
assert.equal(typeof net.effectiveType, "string");
168168
assert.equal(typeof net.saveData, "boolean");
169169
});
170+
171+
test("an editor's preview holds every resource in its loading or error arm", async () => {
172+
{
173+
const { WF, document } = load(async () => response([{ id: 1 }]));
174+
document.documentElement.setAttribute("data-wf-resource", "error");
175+
const rows = WF.resource("/api/rows", null);
176+
await settle();
177+
assert.equal(rows.state(), "error", "held in the error arm, though the request answered");
178+
assert.equal(rows.error().status, 503, "with an error a server gives");
179+
assert.equal(rows.error().message, "503 Service Unavailable");
180+
assert.deepEqual(rows.data(), [{ id: 1 }], "and the answer kept for when it is let go");
181+
}
182+
{
183+
const { WF, document } = load(async () => response([]));
184+
document.documentElement.setAttribute("data-wf-resource", "loading");
185+
const rows = WF.resource("/api/rows", null);
186+
await settle();
187+
assert.equal(rows.state(), "loading");
188+
assert.equal(rows.error(), null);
189+
}
190+
{
191+
const { WF, document } = load(async () => response([]));
192+
document.documentElement.setAttribute("data-wf-resource", "ready");
193+
const rows = WF.resource("/api/rows", null);
194+
await settle();
195+
assert.equal(rows.state(), "ready", "anything else is the request's own state");
196+
}
197+
});

0 commit comments

Comments
 (0)