Expand kitchen sink plugin demos
This commit is contained in:
@@ -101,6 +101,14 @@ export function createPluginDevWatcher(
|
||||
);
|
||||
});
|
||||
|
||||
watcher.on("error", (err) => {
|
||||
log.warn(
|
||||
{ pluginId, packagePath: absPath, err: err instanceof Error ? err.message : String(err) },
|
||||
"plugin-dev-watcher: watcher error, stopping watch for this plugin",
|
||||
);
|
||||
unwatchPlugin(pluginId);
|
||||
});
|
||||
|
||||
watchers.set(pluginId, watcher);
|
||||
log.info(
|
||||
{ pluginId, packagePath: absPath },
|
||||
|
||||
@@ -465,6 +465,26 @@ export function buildHostServices(
|
||||
return companyId;
|
||||
};
|
||||
|
||||
const parseWindowValue = (value: unknown): number | null => {
|
||||
if (typeof value === "number" && Number.isFinite(value)) {
|
||||
return Math.max(0, Math.floor(value));
|
||||
}
|
||||
if (typeof value === "string" && value.trim().length > 0) {
|
||||
const parsed = Number(value);
|
||||
if (Number.isFinite(parsed)) {
|
||||
return Math.max(0, Math.floor(parsed));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const applyWindow = <T>(rows: T[], params?: { limit?: unknown; offset?: unknown }): T[] => {
|
||||
const offset = parseWindowValue(params?.offset) ?? 0;
|
||||
const limit = parseWindowValue(params?.limit);
|
||||
if (limit == null) return rows.slice(offset);
|
||||
return rows.slice(offset, offset + limit);
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugins are instance-wide in the current runtime. Company IDs are still
|
||||
* required for company-scoped data access, but there is no per-company
|
||||
@@ -648,8 +668,8 @@ export function buildHostServices(
|
||||
},
|
||||
|
||||
companies: {
|
||||
async list(_params) {
|
||||
return (await companies.list()) as Company[];
|
||||
async list(params) {
|
||||
return applyWindow((await companies.list()) as Company[], params);
|
||||
},
|
||||
async get(params) {
|
||||
await ensurePluginAvailableForCompany(params.companyId);
|
||||
@@ -661,7 +681,7 @@ export function buildHostServices(
|
||||
async list(params) {
|
||||
const companyId = ensureCompanyId(params.companyId);
|
||||
await ensurePluginAvailableForCompany(companyId);
|
||||
return (await projects.list(companyId)) as Project[];
|
||||
return applyWindow((await projects.list(companyId)) as Project[], params);
|
||||
},
|
||||
async get(params) {
|
||||
const companyId = ensureCompanyId(params.companyId);
|
||||
@@ -738,7 +758,7 @@ export function buildHostServices(
|
||||
async list(params) {
|
||||
const companyId = ensureCompanyId(params.companyId);
|
||||
await ensurePluginAvailableForCompany(companyId);
|
||||
return (await issues.list(companyId, params as any)) as Issue[];
|
||||
return applyWindow((await issues.list(companyId, params as any)) as Issue[], params);
|
||||
},
|
||||
async get(params) {
|
||||
const companyId = ensureCompanyId(params.companyId);
|
||||
@@ -780,7 +800,10 @@ export function buildHostServices(
|
||||
const companyId = ensureCompanyId(params.companyId);
|
||||
await ensurePluginAvailableForCompany(companyId);
|
||||
const rows = await agents.list(companyId);
|
||||
return rows.filter((agent) => !params.status || agent.status === params.status) as Agent[];
|
||||
return applyWindow(
|
||||
rows.filter((agent) => !params.status || agent.status === params.status) as Agent[],
|
||||
params,
|
||||
);
|
||||
},
|
||||
async get(params) {
|
||||
const companyId = ensureCompanyId(params.companyId);
|
||||
@@ -825,10 +848,13 @@ export function buildHostServices(
|
||||
const companyId = ensureCompanyId(params.companyId);
|
||||
await ensurePluginAvailableForCompany(companyId);
|
||||
const rows = await goals.list(companyId);
|
||||
return rows.filter((goal) =>
|
||||
(!params.level || goal.level === params.level) &&
|
||||
(!params.status || goal.status === params.status),
|
||||
) as Goal[];
|
||||
return applyWindow(
|
||||
rows.filter((goal) =>
|
||||
(!params.level || goal.level === params.level) &&
|
||||
(!params.status || goal.status === params.status),
|
||||
) as Goal[],
|
||||
params,
|
||||
);
|
||||
},
|
||||
async get(params) {
|
||||
const companyId = ensureCompanyId(params.companyId);
|
||||
|
||||
@@ -127,6 +127,12 @@ export interface PluginDiscoveryResult {
|
||||
sources: PluginSource[];
|
||||
}
|
||||
|
||||
function getDeclaredPageRoutePaths(manifest: PaperclipPluginManifestV1): string[] {
|
||||
return (manifest.ui?.slots ?? [])
|
||||
.filter((slot): slot is PluginUiSlotDeclaration => slot.type === "page" && typeof slot.routePath === "string" && slot.routePath.length > 0)
|
||||
.map((slot) => slot.routePath!);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Loader options
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -739,6 +745,30 @@ export function pluginLoader(
|
||||
const log = logger.child({ service: "plugin-loader" });
|
||||
const hostVersion = runtimeServices?.instanceInfo.hostVersion;
|
||||
|
||||
async function assertPageRoutePathsAvailable(manifest: PaperclipPluginManifestV1): Promise<void> {
|
||||
const requestedRoutePaths = getDeclaredPageRoutePaths(manifest);
|
||||
if (requestedRoutePaths.length === 0) return;
|
||||
|
||||
const uniqueRequested = new Set(requestedRoutePaths);
|
||||
if (uniqueRequested.size !== requestedRoutePaths.length) {
|
||||
throw new Error(`Plugin ${manifest.id} declares duplicate page routePath values`);
|
||||
}
|
||||
|
||||
const installedPlugins = await registry.listInstalled();
|
||||
for (const plugin of installedPlugins) {
|
||||
if (plugin.pluginKey === manifest.id) continue;
|
||||
const installedManifest = plugin.manifestJson as PaperclipPluginManifestV1 | null;
|
||||
if (!installedManifest) continue;
|
||||
const installedRoutePaths = new Set(getDeclaredPageRoutePaths(installedManifest));
|
||||
const conflictingRoute = requestedRoutePaths.find((routePath) => installedRoutePaths.has(routePath));
|
||||
if (conflictingRoute) {
|
||||
throw new Error(
|
||||
`Plugin ${manifest.id} routePath "${conflictingRoute}" conflicts with installed plugin ${plugin.pluginKey}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Internal helpers
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -861,6 +891,8 @@ export function pluginLoader(
|
||||
);
|
||||
}
|
||||
|
||||
await assertPageRoutePathsAvailable(manifest);
|
||||
|
||||
// Step 6: Reject plugins that require a newer host than the running server
|
||||
const minimumHostVersion = getMinimumHostVersion(manifest);
|
||||
if (minimumHostVersion && hostVersion) {
|
||||
|
||||
Reference in New Issue
Block a user