import { z } from 'zod'; /** * Plugin administration types shared between api and web (ADR 0008, issue #71). * The manifest itself lives in `@dorfteich/plugin-sdk`; these types describe an * *installed* plugin as the instance stores and surfaces it. */ /** * Instance-level activation a Site Admin sets per plugin (ADR 0008 lifecycle): * `disabled` — installed but inert; `optional` — available, Pond Admins toggle * it per pond; `required` — always on everywhere and cannot be uninstalled. */ export const PLUGIN_INSTANCE_MODES = ['disabled', 'optional', 'required'] as const; export type PluginInstanceMode = (typeof PLUGIN_INSTANCE_MODES)[number]; /** One extension point as surfaced to admins (mirrors the manifest entry). */ export interface PluginExtensionPointView { type: string; id: string; title: Record; } /** An installed plugin as returned by the admin API. */ export interface PluginView { id: string; name: string; version: string; apiVersion: string; kind: string; mode: PluginInstanceMode; /** Capabilities the manifest declared, shown to the Site Admin at install. */ permissions: string[]; extensionPoints: PluginExtensionPointView[]; /** Base path the sandbox loads assets from: `/plugins///`. */ assetBasePath: string; license: string; homepage?: string; /** SHA-256 (hex) of the installed bundle ZIP (#232); null = installed * before hash recording existed — reinstall to record it. */ bundleSha256: string | null; /** Verdict against `plugins.allowlist` (#232): `not_enforced` while the * allowlist is empty; otherwise pinned / unpinned / mismatch. */ pinning: 'not_enforced' | 'pinned' | 'unpinned' | 'mismatch'; installedAt: string; updatedAt: string; } /** * Machine-readable rejection codes for an install/uninstall attempt. Each is * also an `errors.` i18n key. `validateManifest` field issues travel in * the ApiErrorBody `details`. */ export const PLUGIN_ERROR_CODES = [ 'plugin_invalid_zip', 'plugin_bad_structure', 'plugin_invalid_manifest', 'plugin_api_incompatible', 'plugin_too_large', 'plugin_missing_bundle', 'plugin_missing_styles', 'plugin_css_unsafe', 'plugin_version_not_higher', 'plugin_required_cannot_uninstall', 'plugin_not_found', 'plugin_not_optional', 'plugin_not_pinned', 'plugin_hash_mismatch', ] as const; export type PluginErrorCode = (typeof PLUGIN_ERROR_CODES)[number]; /** * An optional plugin as shown in a pond's plugin settings (issue #72): the * installed plugin plus whether this pond has activated it. Only `optional` * plugins appear here — `required` ones are always on and `disabled` ones are * never available, so neither is a per-pond choice. */ export interface PondPluginSetting { plugin: PluginView; enabled: boolean; } /** Payload to switch a plugin's instance mode (Site Admin, issue #72). */ export const pluginModeInputSchema = z.object({ mode: z.enum(PLUGIN_INSTANCE_MODES), }); export type PluginModeInput = z.infer; /** Payload to toggle an optional plugin for one pond (Pond Admin, issue #72). */ export const pondPluginToggleInputSchema = z.object({ enabled: z.boolean(), }); export type PondPluginToggleInput = z.infer; /** * What a `plugin_block` of an inactive plugin renders instead of its sandbox * (issue #76): the manifest `fallback`, resolved server-side from the stored * manifest snapshot — which survives uninstall as a tombstone, so blocks in * documents always have something to show. An image fallback is resolved to * its served URL while the files exist and degrades to `null` (neutral * placeholder) once they are gone. */ export interface PluginFallbackView { pluginId: string; name: string; fallback: { type: 'text'; value: string } | { type: 'image'; url: string } | null; } /** * Responses of the viewer-scoped plugin API (issue #74, `/api/v1/plugin/…`). * Every call runs with the requesting user's session behind the standard * permission guards, so a plugin never sees more than its viewer could. The * heading outline reuses the editor's `OutlineEntry` (see editor-schema). */ export interface PluginPageSummary { id: string; title: string; slug: string; /** Label names, for pageTool filtering (issue #77). */ labels: string[]; } export interface PluginPageMeta { id: string; title: string; pondId: string; slug: string; } export interface PluginPageContent { markdown: string; }