mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-01 06:02:56 +00:00
* feat(plugins): unify source installation and simplify plugin settings * feat(plugins): install npm sources and review CLI updates Treat install selectors as one-time choices. Keep source identity separate from the installed revision, and resolve ordinary updates from the current upstream default. npm only acquires directories; plugin loading remains independent of npm. * fix(plugins): preserve CLI diagnostics and exercise reviewed updates Move new source capability coverage to real browser journeys. Update the requirements regression to exercise preview and apply, and preserve the existing duplicate-selector diagnostic across positional option scopes. * test(plugins): remove unused pending helper * test(plugins): pack registry fixtures with the Windows command runner * test(plugins): assert failed client contribution setup * docs(plugins): consolidate guides and document npm publishing * docs(plugins): introduce publishing choices before npm instructions * test(plugins): own browser journey cleanup in a fixture * feat(plugins): prepare scaffolded packages for publishing
103 lines
3.5 KiB
TypeScript
103 lines
3.5 KiB
TypeScript
import type {
|
|
PluginUpdatePreview,
|
|
PluginUpdateResult,
|
|
PluginUpdateSelection,
|
|
PluginUpdateProposal,
|
|
} from "@getpaseo/protocol/messages";
|
|
|
|
interface UpdateClient {
|
|
previewPluginUpdates(input: {
|
|
pluginId?: string;
|
|
target?: PluginUpdateSelection;
|
|
}): Promise<PluginUpdatePreview[]>;
|
|
applyPluginUpdates(proposals: PluginUpdateProposal[]): Promise<PluginUpdateResult[]>;
|
|
}
|
|
export interface UpdateOptions {
|
|
pluginId?: string;
|
|
all?: boolean;
|
|
ref?: string;
|
|
version?: string;
|
|
check?: boolean;
|
|
yes?: boolean;
|
|
}
|
|
export interface UpdateInteraction {
|
|
interactive: boolean;
|
|
structured: boolean;
|
|
write(text: string): void;
|
|
confirm(message: string): Promise<boolean>;
|
|
}
|
|
export interface UpdateOutcome {
|
|
id: string;
|
|
outcome: PluginUpdatePreview["outcome"] | PluginUpdateResult["outcome"] | "declined";
|
|
current?: PluginUpdatePreview["current"];
|
|
target?: PluginUpdatePreview["target"];
|
|
links?: string[];
|
|
proposal?: PluginUpdateProposal;
|
|
plugin?: PluginUpdateResult["plugin"];
|
|
error?: string;
|
|
warning?: string;
|
|
}
|
|
|
|
export async function reviewPluginUpdates(
|
|
client: UpdateClient,
|
|
options: UpdateOptions,
|
|
interaction: UpdateInteraction,
|
|
): Promise<UpdateOutcome[]> {
|
|
if ((options.pluginId === undefined) === (options.all !== true))
|
|
throw new Error("Choose one plugin ID or pass --all");
|
|
if (options.ref && options.version) throw new Error("Choose --ref or --version, not both");
|
|
let target: PluginUpdateSelection | undefined;
|
|
if (options.ref) target = { kind: "git", ref: options.ref };
|
|
if (options.version) target = { kind: "npm", version: options.version };
|
|
if (target && options.all)
|
|
throw new Error("An explicit target requires one plugin ID, without --all");
|
|
const preview = await client.previewPluginUpdates({ pluginId: options.pluginId, target });
|
|
if (!interaction.structured) interaction.write(formatUpdatePreview(preview));
|
|
if (options.check) return preview;
|
|
const proposals = preview.flatMap((item) => (item.proposal ? [item.proposal] : []));
|
|
if (!proposals.length) return preview;
|
|
if (!options.yes && !target) {
|
|
if (interaction.structured || !interaction.interactive)
|
|
throw new Error("Confirmation required; rerun with --yes.");
|
|
if (
|
|
!(await interaction.confirm(
|
|
options.all ? "Update these plugins? [y/N] " : `Update ${options.pluginId}? [y/N] `,
|
|
))
|
|
) {
|
|
return preview.map((item) => (item.proposal ? { id: item.id, outcome: "declined" } : item));
|
|
}
|
|
}
|
|
const applied = new Map(
|
|
(await client.applyPluginUpdates(proposals)).map((item) => [item.id, item]),
|
|
);
|
|
return preview.map((item) => applied.get(item.id) ?? item);
|
|
}
|
|
|
|
export function formatUpdatePreview(items: PluginUpdatePreview[]): string {
|
|
return items
|
|
.map((item) => {
|
|
const current = item.current?.currentRevision ?? "unknown";
|
|
const target = item.target?.kind === "git" ? item.target.commit : item.target?.version;
|
|
let text: string;
|
|
switch (item.outcome) {
|
|
case "update":
|
|
text = `${current} → ${target}`;
|
|
break;
|
|
case "current":
|
|
text = `up to date (${current})`;
|
|
break;
|
|
case "installed-newer":
|
|
text = `installed ${current} is newer than latest ${target}; keeping installed version`;
|
|
break;
|
|
case "local":
|
|
text = "local directory; use Reload after editing";
|
|
break;
|
|
case "error":
|
|
text = item.error ?? "Check failed";
|
|
break;
|
|
}
|
|
return `${item.id}: ${text}\n${item.links.map((link) => `Review: ${link}\n`).join("")}`;
|
|
})
|
|
.join("\n");
|
|
}
|