import { summarizeArtifact } from "../artifact/artifact.ts";
import type { ArtifactScopeStore } from "../artifact/artifact-scope-store.ts";
import type { ArtifactStore } from "../artifact/artifact-store.ts";
import type { ArtifactTrashStore } from "../artifact/artifact-trash-store.ts";
import {
addBinderGroup,
addBinderProject,
binderScope,
binderTree,
createBinder,
fileArtifact,
listBinders,
moveBinder,
removeBinder,
removeBinderGroup,
removeBinderProject,
replaceBinderGroups,
replaceBinderProjects,
setBinderGlobal,
setBinderNone,
unfileArtifact,
updateBinder,
} from "../binder/binder-service.ts";
import type { OperationDefinition } from "../module-registry.ts";
import type { ProjectRegistryStore } from "../project-registry/project-registry-store.ts";
import type { ScopeGroupStore } from "../scope-group/scope-group-store.ts";
import { type OperationInput, optionalBoolean, optionalNumber, optionalString, string } from "./operation-input.ts";
const MODULE_ID = "binders";
const eventContext = (input: OperationInput) => ({
actor: optionalString(input, "actor"),
source: optionalString(input, "source"),
sessionId: optionalString(input, "session_id") ?? optionalString(input, "sessionId"),
});
const listFilter = (input: OperationInput) => {
const projectRoot = optionalString(input, "project_root");
const applicable = optionalBoolean(input, "applicable") === true;
if (applicable && projectRoot === undefined) throw new Error("applicable requires project_root");
return {
text: optionalString(input, "text"),
limit: optionalNumber(input, "limit"),
...(applicable ? { applicableToProjectRoot: projectRoot } : { projectRoot }),
};
};
export const BINDERS_OPERATION_NAMES = [
"binders.create",
"binders.list",
"binders.tree",
"binders.show",
"binders.update",
"binders.move",
"binders.file",
"binders.unfile",
"binders.remove",
"binders.scope",
"binders.set_global",
"binders.set_none",
"binders.add_project",
"binders.remove_project",
"binders.replace_projects",
"binders.add_group",
"binders.remove_group",
"binders.replace_groups",
] as const;
export function bindersOperations(
artifacts: ArtifactStore & ArtifactTrashStore,
scopes: ArtifactScopeStore,
registry: ProjectRegistryStore,
scopeGroups: ScopeGroupStore,
): OperationDefinition[] {
const define = (name: string, execute: (input: Input) => Output): OperationDefinition => ({
name,
moduleId: MODULE_ID,
execute,
});
return [
define("binders.create", (input: OperationInput) =>
createBinder(
artifacts,
scopes,
{
title: string(input, "title"),
labels: input.labels as string[] | undefined,
parentId: optionalString(input, "parent_id"),
projectRoot: optionalString(input, "project_root"),
projectReferences: input.projects as string[] | undefined,
},
eventContext(input),
registry,
),
),
define("binders.list", (input: OperationInput) => {
const binders = listBinders(artifacts, scopes, listFilter(input));
return optionalBoolean(input, "full") === true ? binders : binders.map(summarizeArtifact);
}),
define("binders.tree", (input: OperationInput) =>
binderTree(artifacts, scopes, {
projectRoot: optionalString(input, "project_root"),
artifactIds: input.artifact_ids as string[] | undefined,
}),
),
define("binders.show", (input: OperationInput) => {
const id = string(input, "id");
const tree = binderTree(artifacts, scopes, { projectRoot: optionalString(input, "project_root") });
const node = tree.nodes.find((candidate) => candidate.binder.id === id);
if (!node) throw new Error(`binder artifact "${id}" not found in this project context`);
return node;
}),
define("binders.update", (input: OperationInput) =>
updateBinder(
artifacts,
scopes,
string(input, "id"),
{ title: optionalString(input, "title"), labels: input.labels as string[] | undefined },
optionalString(input, "project_root"),
eventContext(input),
),
),
define("binders.move", (input: OperationInput) =>
moveBinder(
artifacts,
scopes,
string(input, "id"),
optionalString(input, "parent_id"),
optionalString(input, "project_root"),
eventContext(input),
),
),
define("binders.file", (input: OperationInput) =>
fileArtifact(
artifacts,
scopes,
string(input, "artifact_id"),
string(input, "binder_id"),
optionalString(input, "project_root"),
eventContext(input),
),
),
define("binders.unfile", (input: OperationInput) =>
unfileArtifact(artifacts, scopes, string(input, "artifact_id"), optionalString(input, "project_root"), eventContext(input)),
),
define("binders.remove", (input: OperationInput) =>
removeBinder(artifacts, string(input, "id"), eventContext(input), optionalString(input, "reason")),
),
define("binders.scope", (input: OperationInput) => binderScope(artifacts, scopes, string(input, "id"))),
define("binders.set_global", (input: OperationInput) => setBinderGlobal(artifacts, scopes, string(input, "id"))),
define("binders.set_none", (input: OperationInput) => setBinderNone(artifacts, scopes, string(input, "id"))),
define("binders.add_project", (input: OperationInput) =>
addBinderProject(artifacts, scopes, registry, string(input, "id"), string(input, "project")),
),
define("binders.remove_project", (input: OperationInput) =>
removeBinderProject(artifacts, scopes, registry, string(input, "id"), string(input, "project")),
),
define("binders.replace_projects", (input: OperationInput) =>
replaceBinderProjects(artifacts, scopes, registry, string(input, "id"), (input.projects as string[] | undefined) ?? []),
),
define("binders.add_group", (input: OperationInput) =>
addBinderGroup(artifacts, scopes, scopeGroups, string(input, "id"), string(input, "group")),
),
define("binders.remove_group", (input: OperationInput) =>
removeBinderGroup(artifacts, scopes, scopeGroups, string(input, "id"), string(input, "group")),
),
define("binders.replace_groups", (input: OperationInput) =>
replaceBinderGroups(artifacts, scopes, scopeGroups, string(input, "id"), (input.groups as string[] | undefined) ?? []),
),
];
}