import * as fs from "node:fs"; import * as path from "node:path"; import type { CapabilityType } from "../manifest.js"; /** * A capability type the build tool knows how to discover: which directory * under `src/` its files live in, and the `_tag` its default exports must * carry. This SDK registers workflows and syncs. */ export type CapabilityTypeDefinition = { type: CapabilityType; directory: string; tag: string; runtime: "worker" | "build"; }; export const CAPABILITY_TYPES: readonly CapabilityTypeDefinition[] = [ { type: "workflow", directory: "workflows", tag: "workflow", runtime: "worker" }, { type: "sync", directory: "syncs", tag: "sync", runtime: "worker" }, { type: "custom_block", directory: "customBlocks", tag: "custom_block", runtime: "build" }, ]; /** * A capability file found during discovery. The key is the file's basename; * combined with the type it forms the capability's identity (`workflow/onPageCreated`). */ export type DiscoveredCapability = { type: CapabilityType; tag: string; runtime: "worker" | "build"; key: string; /** Path to the source file, relative to the project root. */ sourcePath: string; }; /** * Discover capability files by convention: every `.ts` file directly under * `src//` for each registered capability type. Subdirectories are * not searched — they hold plain modules, not capabilities. * * Workflow and sync files are discovered from their matching directories. */ export async function discoverCapabilities(projectRoot: string): Promise { const discovered: DiscoveredCapability[] = []; for (const definition of CAPABILITY_TYPES) { const directory = path.join(projectRoot, "src", definition.directory); let entries: fs.Dirent[]; try { entries = await fs.promises.readdir(directory, { withFileTypes: true }); } catch (error) { if (error instanceof Error && "code" in error && error.code === "ENOENT") { continue; } throw error; } entries.sort((a, b) => a.name.localeCompare(b.name)); for (const entry of entries) { if (!entry.isFile() || !entry.name.endsWith(".ts") || entry.name.endsWith(".d.ts")) { continue; } discovered.push({ type: definition.type, tag: definition.tag, runtime: definition.runtime, key: entry.name.slice(0, -".ts".length), sourcePath: path.join("src", definition.directory, entry.name), }); } } return discovered; }