/** * modules/projects.ts — the shared project catalog as its own Papyrus-native registered module, * fronting the same ProjectRegistryStore Docs/Rules/Playbooks scope operations and Tasks' * project catalog already share underneath (see ports/project-registry-store.ts). Tasks' * own tasks.projects/tasks.resolve_project/tasks.register_project remain fully working, * documented compatibility delegates -- this module gives every other domain (and any caller * with no reason to go through tasks.*) the identical operations under a kind-neutral name. */ import { TASK_PROJECT_LIST_MAX_RESULTS } from "../constants.ts"; import type { OperationDefinition } from "../module-registry.ts"; import { assertRegisterProjectInputBounds, resolveProjectReference } from "../project-registry/project-registry.ts"; import type { ProjectRegistryStore } from "../project-registry/project-registry-store.ts"; import { normalizeProjectRoot } from "../project-registry/scope-source.ts"; import { type OperationInput, optionalNumber, optionalString, optionalStringArray, string } from "./operation-input.ts"; const MODULE_ID = "projects"; export const PROJECTS_OPERATION_NAMES = ["projects.list", "projects.resolve", "projects.register"] as const; export function projectsOperations(registry: ProjectRegistryStore): OperationDefinition[] { const define = (name: string, execute: (input: Input) => Output): OperationDefinition => ({ name, moduleId: MODULE_ID, execute, }); return [ define("projects.list", (input: OperationInput) => { const limit = optionalNumber(input, "limit") ?? 20; if (!Number.isInteger(limit) || limit < 1 || limit > TASK_PROJECT_LIST_MAX_RESULTS) { throw new Error(`project list limit must be between 1 and ${TASK_PROJECT_LIST_MAX_RESULTS}`); } return registry.projects(optionalString(input, "query"), limit); }), define("projects.resolve", (input: OperationInput) => resolveProjectReference(registry, string(input, "reference"))), define("projects.register", (input: OperationInput) => { const name = optionalString(input, "name"); const aliases = optionalStringArray(input, "aliases"); assertRegisterProjectInputBounds(name, aliases); return registry.registerProject({ projectRoot: normalizeProjectRoot(string(input, "project_root")), name, aliases, existingId: optionalString(input, "existing_id"), }); }), ]; }