86 lines
5.2 KiB
JavaScript
86 lines
5.2 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"],
|
|
])
|
|
// 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) => {
|
|
const 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))
|
|
})
|
|
// Requirements name callable operations. Filtering a procedure must also remove its
|
|
// requirement entry, or the projected registry refuses the now-unknown operation.
|
|
// Keep metadata for retained procedures and own jobs without changing the source manifest.
|
|
// Unknown source operations remain so registry validation still refuses invalid declarations.
|
|
const retained = new Set(procedures.map(({ name }) => name))
|
|
const removed = new Set(
|
|
(module.procedures ?? []).filter(({ name }) => !retained.has(name)).map(({ name }) => name),
|
|
)
|
|
return {
|
|
...module,
|
|
procedures,
|
|
...(module.serviceRequirements === undefined
|
|
? {}
|
|
: {
|
|
serviceRequirements: module.serviceRequirements
|
|
.map((requirement) => ({
|
|
...requirement,
|
|
operations: Object.fromEntries(
|
|
Object.entries(requirement.operations).filter(
|
|
([name]) => !removed.has(name) || Object.hasOwn(module.jobs ?? {}, name),
|
|
),
|
|
),
|
|
}))
|
|
.filter((requirement) => Object.keys(requirement.operations).length > 0),
|
|
}),
|
|
}
|
|
})
|
|
|
|
// 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)
|