// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0 // The Convex profile's generator entry (spec §5.4): `pnpm convex:gen`. Rebuilds the module // library (the registry is read from the manifests, not from a snapshot) and rewrites // convex/schema.ts and convex/generated/.ts. The output is deterministic: regeneration // on an unchanged registry is byte-identical, and the profile gate asserts it. import { mkdir, readdir, rm, writeFile } from "node:fs/promises" import { buildRegistry } from "@conusai-kit/core" import { writeConvexFiles } from "@conusai-kit/data-convex" import { convexModules } from "../convex/lib/convexModules.mjs" // The registry is read from the modules' built library (`pnpm run build:libs` first — the // package.json script chains it), filtered to the modules the Convex runtime can deploy // (convex/lib/convexModules.mjs, one exclusion map shared with the runtime). import { appModules } from "../modules/dist/index.js" const SPDX = "// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0\n" // A module that declares its profiles and leaves out `convex` (knowledge, agent) is not deployed // here: the runtime's module list (convex/lib/runtime.ts) and test/convex-profile.test.ts apply the // same filter, so no wrapper is generated for a procedure the deployed registry does not hold. const convexBound = appModules.filter((module) => module.profiles === undefined || module.profiles.includes("convex")) const registry = buildRegistry([...convexModules(convexBound)]) const files = await writeConvexFiles( registry, new URL("../convex", import.meta.url).pathname, { mkdir, writeFile, }, { header: SPDX }, ) const functionName = (name) => name.replace(/[^A-Za-z0-9_$]/g, "_") const procedures = [...registry.procedures.values()] .map(({ module, def }) => [def.name, { kind: def.kind, reference: `generated/${module}:${functionName(def.name)}` }]) .sort(([left], [right]) => left.localeCompare(right)) const procedureMap = `${SPDX}// Generated by @conusai-kit/data-convex (pnpm convex:gen). Do not edit by hand.\nexport const CONVEX_PROCEDURES = ${JSON.stringify(Object.fromEntries(procedures), null, 2)} as const\n` await writeFile(new URL("../convex/generated/procedures.ts", import.meta.url), procedureMap) // Convex bundles every exported .ts function it finds, whether or not another file imports it. // Leaving a wrapper for an excluded module deploys a public endpoint whose runtime registry does // not contain its procedure. Remove stale wrappers deterministically so generation is also the // profile's deployment allowlist. const expected = new Set([...files.map((file) => file.path.split("/").at(-1)), "procedures.ts"]) for (const entry of await readdir(new URL("../convex/generated/", import.meta.url), { withFileTypes: true })) { if (entry.isFile() && entry.name.endsWith(".ts") && !expected.has(entry.name)) { await rm(new URL(`../convex/generated/${entry.name}`, import.meta.url)) } } console.log( `convex:gen: ${files.length + 1} file(s) written (${files .map((f) => f.path) .concat("generated/procedures.ts") .join(", ")})`, )