clone

nostr relays on demand at <name>.bind.ws: claim one with your key, pay in zaps past the free allowance

check: the console script must start against a stub document

Dami committed 98b870a
node --check passed the day the page came up empty. This runs the script's
synchronous start with every element stubbed, so a handler wired above the
helper it calls fails typecheck.
commit
98b870a662afd2b14e8d640e3cd368aa5a99afcf
parent
8771bf30cbdb0e2f7f95956dc2428cd46582702c
author
Dami <dami.etoile@gmail.com>
2 files changed, +47 -1
Mpackage.json2+-
Ascripts/check-console.mjs46++++++++++++++++++++++++++++++
M package.json +1 -1
A scripts/check-console.mjs +46 -0
@@ -0,0 +1,46 @@
1+// Runs the console script's synchronous start against a stub document, so a2+// handler wired above the helper it calls, or any other error before the3+// first await, fails typecheck instead of a live page. Syntax alone is not4+// enough: `node --check` passed the day the page came up empty.5+import { readFileSync } from "node:fs";6+7+const src = readFileSync(new URL("../src/dashboard.ts", import.meta.url), "utf8");8+const js = JSON.parse(src.match(/^const JS = (".*");$/m)[1]);9+10+// Every element lookup answers with an object that accepts any property and11+// any method call, so the script gets through its wiring.12+const element = () => new Proxy(function () {}, {13+ get: (t, k) => (k === Symbol.toPrimitive ? () => "" : k === "classList" ? { toggle() {}, add() {}, remove() {}, contains: () => false } : k === "style" ? { setProperty() {} } : element()),14+ set: () => true,15+ apply: () => element(),16+});17+const doc = element();18+const g = globalThis;19+g.document = doc;20+g.window = g;21+g.location = { host: "check.localhost", hostname: "check.localhost", origin: "http://check.localhost", protocol: "http:", hash: "", pathname: "/", search: "" };22+g.localStorage = { getItem: () => null, setItem() {}, removeItem() {} };23+g.history = { replaceState() {} };24+g.navigator = { clipboard: { writeText: async () => {} } };25+g.confirm = () => false;26+g.prompt = () => null;27+g.alert = () => {};28+g.fetch = async () => ({ ok: true, json: async () => ({}), text: async () => "" });29+g.WebSocket = class { close() {} send() {} };30+g.SIGNER_URL = "/signer.js";31+g.addEventListener = () => {};32+33+let failed = null;34+process.on("unhandledRejection", (e) => { failed = e; });35+try {36+ new Function(js)();37+ // The script is an async IIFE; its synchronous part runs before this resolves.38+ await new Promise((r) => setTimeout(r, 50));39+} catch (e) {40+ failed = e;41+}42+if (failed && !/fetch|json|network/i.test(String(failed))) {43+ console.error("console script fails before it boots:", failed);44+ process.exit(1);45+}46+console.log("console script starts");