clone

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

dependabot/npm_and_yarn/npm-f01a459b0f
bindws / scripts / check / check-celld.mjs
check-celld.mjs 3.7K
1// Keeps wrangler.celld.jsonc, the config for hosting without Cloudflare2// (docs/16-hosting-without-cloudflare.md), in step with wrangler.jsonc: the3// same code, bindings, migrations and prices, and only the keys celld4// accepts. Runs as part of `npm run typecheck`, so a change to one file that5// forgets the other fails the build instead of quietly dropping the host.6import { readFileSync } from "node:fs";7import { stripComments } from "./jsonc.mjs";89const load = (path) => JSON.parse(stripComments(readFileSync(new URL("../../" + path, import.meta.url), "utf8")));1011const cf = load("wrangler.jsonc");12const celld = load("wrangler.celld.jsonc");13const problems = [];14const same = (what, a, b) => {15 if (JSON.stringify(a) !== JSON.stringify(b)) problems.push(`${what} differs: wrangler.jsonc has ${JSON.stringify(a)}, wrangler.celld.jsonc has ${JSON.stringify(b)}`);16};1718// The keys celld deploy accepts (https://celld.dev/docs/cloudflare-compat);19// any other top-level key stops its deployment.20const ACCEPTED = new Set(["$schema", "name", "main", "no_bundle", "compatibility_date", "compatibility_flags", "durable_objects", "migrations", "assets", "services", "triggers", "vars", "d1_databases", "kv_namespaces", "queues", "workflows", "r2_buckets"]);21for (const k of Object.keys(celld)) if (!ACCEPTED.has(k)) problems.push(`wrangler.celld.jsonc has "${k}", which celld deploy refuses`);2223same("name", cf.name, celld.name);24same("main", cf.main, celld.main);25same("compatibility_date", cf.compatibility_date, celld.compatibility_date);26same("compatibility_flags", cf.compatibility_flags, celld.compatibility_flags);27same("durable_objects", cf.durable_objects, celld.durable_objects);28same("migrations", cf.migrations, celld.migrations);29same("r2_buckets bindings", cf.r2_buckets.map((b) => b.binding), celld.r2_buckets.map((b) => b.binding));30same("kv_namespaces bindings", cf.kv_namespaces.map((b) => b.binding), celld.kv_namespaces.map((b) => b.binding));3132// Vars: the prices and allowances match; the site-specific ones may differ;33// the Cloudflare-only ones are empty on celld; celld has the proxy header.34const SITE = new Set(["DOMAIN", "DEV_RELAY", "LIGHTNING_ADDRESS", "SERVICE_PUBKEY", "LEASE_DAYS"]);35const CLOUDFLARE_ONLY = new Set(["ZONE_ID", "CNAME_TARGET"]);36const CELLD_ONLY = new Set(["CLIENT_IP_HEADER"]);37for (const k of Object.keys(cf.vars)) {38 if (!(k in celld.vars)) problems.push(`vars.${k} is missing from wrangler.celld.jsonc`);39 else if (CLOUDFLARE_ONLY.has(k)) {40 if (celld.vars[k] !== "") problems.push(`vars.${k} is Cloudflare-only and must be empty in wrangler.celld.jsonc`);41 } else if (!SITE.has(k)) same(`vars.${k}`, cf.vars[k], celld.vars[k]);42}43for (const k of Object.keys(celld.vars)) if (!(k in cf.vars) && !CELLD_ONLY.has(k)) problems.push(`vars.${k} is in wrangler.celld.jsonc but not in wrangler.jsonc`);44for (const k of CELLD_ONLY) if (!(k in celld.vars)) problems.push(`vars.${k} is missing from wrangler.celld.jsonc`);4546// The lease door's in-memory limits (src/edge.ts) restate the rate limit47// bindings, which celld does not have; the figures must agree.48const edge = readFileSync(new URL("../../src/edge.ts", import.meta.url), "utf8");49const constant = (name) => Number(edge.match(new RegExp(`export const ${name} = (\\d+);`))?.[1]);50const binding = (name) => cf.ratelimits.find((r) => r.name === name)?.simple.limit;51same("leases per address per minute", binding("LEASE_LIMIT_IP"), constant("LEASES_PER_IP_MINUTE"));52same("leases per minute", binding("LEASE_LIMIT_ALL"), constant("LEASES_PER_MINUTE"));5354if (problems.length) {55 console.error("wrangler.celld.jsonc is out of step with wrangler.jsonc:\n " + problems.join("\n "));56 process.exit(1);57}58console.log("wrangler.celld.jsonc matches wrangler.jsonc");