Files
template-app/scripts/convex-gen.mjs
T

55 lines
3.1 KiB
JavaScript

// 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/<module>.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(", ")})`,
)