Files
WRNexusJS/packages/cli/src/services.ts
T
Clintchiz 586a6db8ff
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-02 23:18:51 +05:30

266 lines
9.9 KiB
TypeScript

export interface LocalServiceRecord {
id: string;
createdAt: string;
[key: string]: unknown;
}
export interface LocalServicesState {
database: Map<string, unknown>;
cache: Map<string, { value: unknown; expiresAt?: number }>;
mail: LocalServiceRecord[];
sms: LocalServiceRecord[];
webhooks: LocalServiceRecord[];
storage: Map<string, Uint8Array>;
queue: LocalServiceRecord[];
cron: LocalServiceRecord[];
auth: Map<string, LocalServiceRecord>;
metrics: LocalServiceRecord[];
}
export function createLocalServicesState(): LocalServicesState {
return {
database: new Map(),
cache: new Map(),
mail: [],
sms: [],
webhooks: [],
storage: new Map(),
queue: [],
cron: [],
auth: new Map(),
metrics: [],
};
}
const json = (value: unknown, status = 200, origin = "https://localhost:3000") =>
Response.json(value, {
status,
headers: {
"cache-control": "no-store",
"access-control-allow-origin": origin,
"x-content-type-options": "nosniff",
},
});
async function boundedJson(
request: Request,
maximum = 256 * 1024,
): Promise<Record<string, unknown>> {
const text = await request.text();
if (new TextEncoder().encode(text).byteLength > maximum)
throw new RangeError("payload-too-large");
const value = JSON.parse(text) as unknown;
if (!value || typeof value !== "object" || Array.isArray(value))
throw new TypeError("object-required");
return value as Record<string, unknown>;
}
function record(value: Record<string, unknown>): LocalServiceRecord {
return { id: crypto.randomUUID(), createdAt: new Date().toISOString(), ...value };
}
export function createLocalServicesHandler(
state = createLocalServicesState(),
options: { origin?: string } = {},
) {
const origin = options.origin ?? "https://localhost:3000";
return async (request: Request): Promise<Response> => {
const url = new URL(request.url);
const path = url.pathname;
if (request.method === "OPTIONS")
return new Response(null, {
status: 204,
headers: {
"access-control-allow-origin": origin,
"access-control-allow-methods": "GET,POST,PUT,DELETE",
"access-control-allow-headers": "content-type",
},
});
if (path === "/healthz" || path === "/readyz")
return json({ status: "up", service: "wrnexus-local-services" }, 200, origin);
if (path === "/" || path === "/__services")
return new Response(
`<!doctype html><html lang="en"><head><meta charset="utf-8"><title>WRNexus Local Services</title></head><body><h1>WRNexus Local Services</h1><nav>${["database", "cache", "mail", "sms", "webhooks", "storage", "queue", "cron", "auth", "metrics"].map((name) => `<a href="/${name}">${name}</a> `).join("")}</nav><p>Use the JSON endpoints to inspect or inject local development events.</p></body></html>`,
{
headers: {
"content-type": "text/html; charset=utf-8",
"content-security-policy": "default-src 'none'; style-src 'unsafe-inline'",
},
},
);
try {
if (path === "/database") {
if (request.method === "GET") return json(Object.fromEntries(state.database), 200, origin);
const body = await boundedJson(request);
if (typeof body.key !== "string") return json({ error: "key is required" }, 400);
state.database.set(body.key, body.value);
return json({ key: body.key, value: body.value }, 201, origin);
}
if (path === "/cache") {
if (request.method === "GET") {
const now = Date.now();
for (const [key, value] of state.cache)
if (value.expiresAt && value.expiresAt <= now) state.cache.delete(key);
return json(Object.fromEntries(state.cache), 200, origin);
}
const body = await boundedJson(request);
if (typeof body.key !== "string") return json({ error: "key is required" }, 400);
const ttlMs = typeof body.ttlMs === "number" ? Math.max(0, body.ttlMs) : undefined;
state.cache.set(body.key, {
value: body.value,
...(ttlMs ? { expiresAt: Date.now() + ttlMs } : {}),
});
return json({ stored: true }, 201, origin);
}
for (const [name, values] of [
["mail", state.mail],
["sms", state.sms],
["webhooks", state.webhooks],
["queue", state.queue],
["cron", state.cron],
["metrics", state.metrics],
] as const)
if (path === `/${name}`) {
if (request.method === "GET") return json(values, 200, origin);
const value = record(await boundedJson(request));
values.unshift(value);
if (values.length > 500) values.length = 500;
return json(value, 202, origin);
}
if (path === "/storage") {
if (request.method === "GET")
return json(
[...state.storage.entries()].map(([key, value]) => ({ key, bytes: value.byteLength })),
200,
origin,
);
const key = url.searchParams.get("key");
if (!key || key.includes("..") || key.length > 256)
return json({ error: "safe key is required" }, 400);
const bytes = new Uint8Array(await request.arrayBuffer());
if (bytes.byteLength > 10 * 1024 * 1024) return json({ error: "object too large" }, 413);
state.storage.set(key, bytes);
return json({ key, bytes: bytes.byteLength }, 201, origin);
}
if (path.startsWith("/storage/") && request.method === "GET") {
const key = decodeURIComponent(path.slice(9));
const value = state.storage.get(key);
return value
? new Response(Uint8Array.from(value), {
headers: {
"content-type": "application/octet-stream",
"content-disposition": "attachment",
"x-content-type-options": "nosniff",
},
})
: json({ error: "not found" }, 404);
}
if (path === "/auth") {
if (request.method === "GET") return json([...state.auth.values()], 200, origin);
const value = record(await boundedJson(request));
if (typeof value.email !== "string") return json({ error: "email is required" }, 400);
state.auth.set(value.id, value);
return json({ user: value, accessToken: `local_${value.id}` }, 201, origin);
}
return json({ error: "not found" }, 404);
} catch (error) {
if (error instanceof RangeError) return json({ error: error.message }, 413);
return json({ error: error instanceof Error ? error.message : "invalid request" }, 400);
}
};
}
export interface LocalCertificate {
cert: string;
key: string;
certFile: string;
keyFile: string;
reused: boolean;
}
/** Generate and cache a localhost-only development certificate without requiring OpenSSL. */
export async function ensureLocalCertificate(appRoot: string): Promise<LocalCertificate> {
const directory = join(resolve(appRoot), ".wrnexus", "certificates");
const certFile = join(directory, "localhost.pem");
const keyFile = join(directory, "localhost-key.pem");
if (existsSync(certFile) && existsSync(keyFile)) {
try {
const cert = readFileSync(certFile, "utf8");
const key = readFileSync(keyFile, "utf8");
const certificate = new X509Certificate(cert);
if (Date.parse(certificate.validTo) > Date.now() + 7 * 24 * 60 * 60 * 1000) {
return { cert, key, certFile, keyFile, reused: true };
}
} catch {
// Replace invalid or expired development material below.
}
}
const now = new Date();
const expires = new Date(now.getTime() + 365 * 24 * 60 * 60 * 1000);
const generated = await generate([{ name: "commonName", value: "localhost" }], {
algorithm: "sha256",
keyType: "ec",
curve: "P-256",
notBeforeDate: new Date(now.getTime() - 60_000),
notAfterDate: expires,
extensions: [
{ name: "basicConstraints", cA: false, critical: true },
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true, critical: true },
{ name: "extKeyUsage", serverAuth: true },
{
name: "subjectAltName",
altNames: [
{ type: 2, value: "localhost" },
{ type: 2, value: "*.localhost" },
{ type: 7, ip: "127.0.0.1" },
{ type: 7, ip: "::1" },
],
},
],
});
mkdirSync(directory, { recursive: true });
writeFileSync(certFile, generated.cert, { encoding: "utf8", mode: 0o600 });
writeFileSync(keyFile, generated.private, { encoding: "utf8", mode: 0o600 });
try {
chmodSync(certFile, 0o600);
chmodSync(keyFile, 0o600);
} catch {
// Windows ACLs are inherited from the private workspace directory.
}
return {
cert: generated.cert,
key: generated.private,
certFile,
keyFile,
reused: false,
};
}
export async function startLocalServices(
options: {
appRoot?: string;
port?: number;
hostname?: string;
https?: boolean;
origin?: string;
certificate?: LocalCertificate;
} = {},
) {
const port = options.port ?? 3099;
const hostname = options.hostname ?? "127.0.0.1";
const secure = options.https !== false;
const tls = secure
? (options.certificate ?? (await ensureLocalCertificate(options.appRoot ?? ".")))
: undefined;
const server = Bun.serve({
port,
hostname,
fetch: createLocalServicesHandler(undefined, { origin: options.origin }),
...(tls ? { tls: { cert: tls.cert, key: tls.key } } : {}),
});
console.log(
` ▸ Local services: ${secure ? "https" : "http"}://${hostname}:${server.port}/__services`,
);
if (tls) console.log(` certificate: ${tls.certFile} (trust locally to remove warnings)`);
console.log(" database cache mail sms webhooks storage queue cron auth metrics");
return server;
}
import { X509Certificate } from "node:crypto";
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { generate } from "selfsigned";