Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
3.7 kB · 135 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136export class ConfigurationError extends Error { constructor(field: string, instruction: string) { // Only schema-owned field names/instructions belong here, never input values. super(`Invalid configuration: ${field}: ${instruction}`); this.name = "ConfigurationError"; }}
export type Environment = "production" | "development";export type Bindings = Record<string, unknown>;
export function record( value: unknown, keys: readonly string[], field: string,): Bindings { if (!value || typeof value !== "object" || Array.isArray(value)) { throw new ConfigurationError(field, "expected an object"); } if ( Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null ) { throw new ConfigurationError( field, "expected a plain configuration object", ); } if (Object.keys(value).some((key) => !keys.includes(key))) { throw new ConfigurationError( field, `contains unsupported fields; allowed fields: ${keys.join(", ")}`, ); } return value as Bindings;}
export function json(value: unknown, field: string): unknown { if (typeof value !== "string") throw new ConfigurationError( field, "set a JSON object string in Worker variables", ); try { return JSON.parse(value); } catch { throw new ConfigurationError( field, "set valid JSON (parser details are withheld to protect credentials)", ); }}
export function text(value: unknown, field: string): string { if ( typeof value !== "string" || !value.trim() || value !== value.trim() || value.length > 512 || /[\u0000-\u001f\u007f]/.test(value) ) { throw new ConfigurationError( field, "set a nonempty string of at most 512 characters without surrounding whitespace or control characters", ); } return value;}
export function origin( value: unknown, field: string, environment: Environment,): string { const input = text(value, field); let url: URL; try { url = new URL(input); } catch { throw new ConfigurationError(field, "set an absolute HTTPS origin"); } const local = environment === "development" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname); if ( (url.protocol !== "https:" && !(local && url.protocol === "http:")) || url.origin !== input || url.username || url.password ) { throw new ConfigurationError( field, "set an HTTPS origin without credentials, path, query, fragment or trailing slash; HTTP loopback is allowed only in development", ); } return input;}
export function serverSettings( env: Bindings, mode: "customer-runtime" | "control-plane", allowed: readonly string[],) { if (env.FLAREBOT_MODE !== mode) throw new ConfigurationError( "FLAREBOT_MODE", `set ${mode} for this Worker entry`, ); const environment = env.FLAREBOT_ENV ?? "production"; if (environment !== "production" && environment !== "development") throw new ConfigurationError( "FLAREBOT_ENV", "set production or development", ); if ( Object.keys(env).some( (key) => key.startsWith("FLAREBOT_") && !allowed.includes(key), ) ) { throw new ConfigurationError( "Worker bindings", "unsupported FLAREBOT_ binding for this entry; check customer-runtime versus control-plane configuration", ); } if ( environment === "production" && env.FLAREBOT_DEV_OVERRIDES !== undefined ) { throw new ConfigurationError( "FLAREBOT_DEV_OVERRIDES", "remove development overrides from production", ); } return Object.freeze({ mode, environment });}