Files
template-app/convex/lib/convexModules.mjs
T

67 lines
4.4 KiB
JavaScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The modules that deploy to the Convex backend on this profile, derived from the app's one
// module list (modules/index.ts) -- named exclusions, not a second list.
//
// Why a module is excluded: its callable procedure graph needs a Node-only runtime or a workflow
// that has not been composed here. These are explicit gaps, not inferred from the module
// importing a package which has unrelated Node-only exports. Auth is included: its procedure
// handlers use Web Crypto; the OIDC redirects and sealed session storage remain in the trusted
// Next BFF, which invokes Convex functions with server-only admin auth and a verified identity.
//
// Convex actions have no ctx.db, so the runtime cannot run an effect procedure that changes the
// app's own rows in an action. File upload and delete have explicit action→mutation compositions
// in files.ts; other effects remain omitted until they have an equivalent state-machine workflow.
export const CONVEX_EXCLUDED_MODULES = new Map([])
// The modules whose effect procedures are generated as Convex actions (convex/lib/effects.ts):
// each effect is one call to a platform service through the module's own port and owns no app
// row, so it needs no transaction -- except the account module's, whose rows are reached through
// its bound store, one transaction per resumable step. The kernel still resolves the tenant scope and checks the
// permission, against ctx.db in an internal query (convex/effectScope.ts), and the handler's
// nested reads and writes run as the generated queries and mutations they always are.
export const CONVEX_ACTION_EFFECT_MODULES = new Map()
CONVEX_ACTION_EFFECT_MODULES.set("guides", "Guides runtime calls through GUIDES_PLATFORM; no app rows") // kit:guides
CONVEX_ACTION_EFFECT_MODULES.set("support", "Support session/contact calls through SUPPORT_PLATFORM") // kit:support
// The account module's export and deletion compose every module's share (platform calls) and own the
// deletion ledger, which they reach through ACCOUNT_STORE: one internal query or mutation per step.
CONVEX_ACTION_EFFECT_MODULES.set("account", "account export/deletion; ledger steps through convex/account.ts")
export const CONVEX_EXCLUDED_PROCEDURES = new Map([
["branding.logo.set", "logo decoding and Blob writes require an audited action workflow"],
["branding.logo.clear", "logo record updates and concurrent Blob cleanup require an action workflow"],
["branding.logo.get", "logo bytes require an authorized Blob action workflow"],
["files.file.delete", "the BFF composes the object delete action with internal mutations"],
])
// The account page's platform analytics erasure and export (ADR-049) call the platform with the
// deployment token through ACCOUNT_PRIVACY, which only the web's composition root binds; no Convex
// action binds it yet, so on this profile the panel answers "unavailable".
for (const name of ["account.analytics.request", "account.analytics.status", "account.analytics.export"]) {
CONVEX_EXCLUDED_PROCEDURES.set(name, "ACCOUNT_PRIVACY is bound by the web's composition root only")
}
// kit:guides:begin -- a survey file's bytes are the streamed body of a PUT, which cannot be a Convex
// action's argument, and no BFF composition streams it to the platform on this profile yet.
CONVEX_EXCLUDED_PROCEDURES.set(
"guides.runtime.uploadFile",
"a streamed request body cannot be a Convex action argument",
)
// kit:guides:end
const modulesFor = (appModules, includeBffFileEffects) =>
appModules
.filter((module) => !CONVEX_EXCLUDED_MODULES.has(module.id))
.map((module) => ({
...module,
procedures: (module.procedures ?? []).filter((procedure) => {
const composedBffFileProcedure =
includeBffFileEffects && module.id === "files" && procedure.name.startsWith("files.file.")
const runnable = procedure.kind !== "effect" || CONVEX_ACTION_EFFECT_MODULES.has(module.id)
return composedBffFileProcedure || (runnable && !CONVEX_EXCLUDED_PROCEDURES.has(procedure.name))
}),
}))
// The backend generator and deployment registry omit procedures that are composed as actions by
// the BFF. The web HTTP registry keeps those file contracts so their routes still validate input,
// tenancy, permissions, rate limits and CSRF before the caller invokes the action workflow.
export const convexModules = (appModules) => modulesFor(appModules, false)
export const convexBffModules = (appModules) => modulesFor(appModules, true)