import { expect, test } from "bun:test"; import { generateTargets, parse } from "../src/index.ts"; const ast = parse(`component ConfirmDialog { props { title: string open: boolean = false } state { loading: boolean = false } server state { internalId: string = "secret" } outputs { confirm(payload: string) close() } functions { client async function confirm(value: string): Promise { const saved = await server.confirm(value) output.confirm(saved) } server async function confirm(value: string): Promise { return value } shared function normalize(value: string): string { return value.trim() } } view { } }`); test("emits separate browser and server function targets", () => { const targets = generateTargets(ast); expect(targets.browser).toContain('"confirm": async function'); expect(targets.browser).toContain('"normalize": function'); expect(targets.server).toContain("async function confirm"); expect(targets.rpc).toEqual([ expect.objectContaining({ component: "ConfirmDialog", function: "confirm" }), ]); expect(targets.declarations).toContain("interface ConfirmDialogOutputs"); expect(targets.declarations).toContain("interface ConfirmDialogServerCalls"); }); test("component contracts retain declared union options", () => { const targets = generateTargets( parse(`component SizeBox { props { size: "small" | "medium" | "large" = "medium" } view {
} }`), ); expect(targets.contract.props[0]?.options).toEqual(["small", "medium", "large"]); }); test("generates a standalone browser module for imported stores", () => { const targets = generateTargets( parse(`page store SearchStore { state { query: string = "" } client state { focused: boolean = false } server state { secret: string = "hidden" } computed { empty: boolean = query.length === 0 } functions { client function setQuery(value: string): void { query = value } } persist { storage = "session" include = ["query"] version = 1 } }`), ); expect(targets.browser).toContain("__wrnexusStoreRegistry"); expect(targets.browser).toContain('name: "SearchStore"'); expect(targets.browser).toContain('"query"'); expect(targets.browser).not.toContain('"secret": ("hidden")'); expect(targets.declarations).toContain("interface SearchStoreInstance"); }); test("browser codegen avoids reserved prop bindings and parameter collisions", () => { const targets = generateTargets( parse(`component ReservedBindings { props { class: string = "" output: string = "" } state { value: string = "" } outputs { change(payload: { value: string }) } functions { client function update(output: string): void { value = output } client function notify(): void { output.change({ value: value }) } } view { } }`), ); expect(targets.browser).not.toContain("const { class }"); expect(targets.browser).not.toContain("const output = context.output;\n const output"); expect(() => new Function(targets.browser.replace(/^export\s+/gm, ""))).not.toThrow(); }); test("RPC manifests expose only server functions referenced through server.name", () => { const targets = generateTargets( parse(`component SecureActions { functions { client async function saveClient(): Promise { await server.save("ok") } server async function save(value: string): Promise { return value } server function internalSecret(): string { return "secret" } } view { } }`), ); expect(targets.rpc.map((entry) => entry.function)).toEqual(["save"]); expect(targets.server).toContain("internalSecret"); }); test("generated browser stores bind typed RPC, persistence validation, and HMR", () => { const targets = generateTargets( parse(`global store UserStore { state { user: string | null = null count: number = 0 } functions { client async function refresh(): Promise { user = await server.loadCurrentUser() } server async function loadCurrentUser(): Promise { return "Ajay" } server function internalOnly(): string { return "secret" } } persist { storage = "local" include = ["count"] version = 2 migrations { function migrate(value, fromVersion, toVersion) { return value } } validate { function validate(value) { return value && typeof value === "object" ? value : null } } } }`), ); expect(targets.browser).toContain("const server = context.server"); expect(targets.browser).toContain("currentDefinition.persist.migrate"); expect(targets.browser).toContain("currentDefinition.persist.validate"); expect(targets.browser).toContain("__wrnexusApplyStoreHotUpdate"); expect(targets.rpc.map((entry) => entry.function)).toEqual(["loadCurrentUser"]); }); test("browser codegen binds peer client functions through the scoped function table", () => { const targets = generateTargets( parse(`component PeerCalls { state { value: number = 0 } functions { client function increment(): void { value += 1 } client function run(): void { increment() } } view { } }`), ); expect(targets.browser).toContain("context.functions"); const executable = new Function( `${targets.browser.replace(/^export\s+/gm, "")}\nreturn { bindClientScope };`, )() as { bindClientScope: (context: Record) => Record void> }; const state = { value: 0 }; const functions = executable.bindClientScope({ state, props: {}, output: {}, server: {}, refs: {}, }); functions.run?.(); expect(state.value).toBe(1); });