mirror of
https://github.com/zap-proto/web.git
synced 2026-10-30 15:03:09 +00:00
FIX 2 — the @zap-proto/web root no longer leaks server-only Node code. `serve` and the `MintCap` auth slot reach `node:module` (createRequire) / `node:http` / `node:net`; importing them from the package root pulled those into browser bundles. src/index.ts now re-exports only the browser-safe client surface (connect, browserWsTransport, Conn, WebRpcError, ZapParseError). `serve` / `nodeWsTransport` / `MintCap` move to the existing `@zap-proto/web/server` sub-path (server.ts re-exports nodeWsTransport + MintCap so /server is the complete Node API). The `.` / `./server` / `./client` exports are unchanged in shape — only the root surface shrank. Bumps @zap-proto/zap dep to ^1.2.0 (the browser-safe root + zapgen bin). Adds a happy-dom test asserting the root import resolves under a DOM environment, that serve/nodeWsTransport are not reachable from root, and that the built dist/index.js imports no Node built-in (comment-stripped scan, so JSDoc prose does not trip the guard). README documents the browser-safe root vs the Node `/server` sub-path.
68 lines
2.8 KiB
TypeScript
68 lines
2.8 KiB
TypeScript
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
/**
|
|
* @vitest-environment happy-dom
|
|
*
|
|
* Browser-safety guard for the @zap-proto/web root entry (FIX 2).
|
|
*
|
|
* The root entry must import zero Node built-ins so it bundles into a browser.
|
|
* `serve` / `nodeWsTransport` / the `MintCap` auth slot reach `node:module`
|
|
* (createRequire) / `node:http` / `node:net` and now live ONLY behind
|
|
* `@zap-proto/web/server`. We assert:
|
|
*
|
|
* 1. `await import("../src/index.js")` resolves under a DOM environment and
|
|
* exposes the browser RPC surface (connect / browserWsTransport / Conn).
|
|
* 2. The server-only symbols are NOT reachable from root.
|
|
* 3. The BUILT root bundle (dist/index.js) imports no `node:` builtin and
|
|
* never names `createRequire`.
|
|
*/
|
|
|
|
import { readFileSync, existsSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { describe, it, expect } from "vitest";
|
|
|
|
/** Strip `//` line comments and `/* *\/` block comments from JS source. */
|
|
function stripComments(src: string): string {
|
|
return src
|
|
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
.replace(/(^|[^:"'`])\/\/.*$/gm, "$1");
|
|
}
|
|
|
|
describe("@zap-proto/web root is browser-safe (FIX 2)", () => {
|
|
it("imports under a DOM environment with no resolution error", async () => {
|
|
expect(typeof (globalThis as Record<string, unknown>).window).toBe(
|
|
"object",
|
|
);
|
|
const mod = await import("../src/index.js");
|
|
// Browser RPC surface is present...
|
|
expect(typeof mod.connect).toBe("function");
|
|
expect(typeof mod.browserWsTransport).toBe("function");
|
|
expect(typeof mod.Conn).toBe("function");
|
|
expect(typeof mod.WebRpcError).toBe("function");
|
|
// ...and the Node server surface is NOT reachable from root.
|
|
const m = mod as Record<string, unknown>;
|
|
expect(m.serve).toBeUndefined();
|
|
expect(m.nodeWsTransport).toBeUndefined();
|
|
});
|
|
|
|
it("the server sub-path still exposes serve + node transport", async () => {
|
|
const server = await import("../src/server.js");
|
|
expect(typeof server.serve).toBe("function");
|
|
expect(typeof server.nodeWsTransport).toBe("function");
|
|
});
|
|
|
|
it("built dist/index.js imports no Node built-in", () => {
|
|
// Resolve from cwd: import.meta.url is an http:// URL under happy-dom.
|
|
const dist = join(process.cwd(), "dist", "index.js");
|
|
if (!existsSync(dist)) return; // dist is produced by `pnpm build`.
|
|
// Strip comments so prose mentioning `node:` / `createRequire` in JSDoc
|
|
// does not trip the guard — we are checking emitted code, not docs.
|
|
const code = stripComments(readFileSync(dist, "utf8"));
|
|
expect(/from\s*["']node:/.test(code)).toBe(false);
|
|
expect(/\bcreateRequire\b/.test(code)).toBe(false);
|
|
expect(/from\s*["'](?:net|tls|module|http)["']/.test(code)).toBe(false);
|
|
expect(/\brequire\s*\(/.test(code)).toBe(false);
|
|
});
|
|
});
|