@emdash-cms/admin 0.0.2 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"plugins-XhZqfegd.js","names":[],"sources":["../src/lib/api/client.ts","../src/lib/api/plugins.ts"],"sourcesContent":["/**\n * Base API client configuration and shared types\n */\n\nimport type { Element } from \"@emdash-cms/blocks\";\n\nexport const API_BASE = \"/_emdash/api\";\n\n/**\n * Fetch wrapper that adds the X-EmDash-Request CSRF protection header\n * to all requests. All API calls should use this instead of raw fetch().\n */\nexport function apiFetch(input: string | URL | Request, init?: RequestInit): Promise<Response> {\n\tconst headers = new Headers(init?.headers);\n\theaders.set(\"X-EmDash-Request\", \"1\");\n\treturn fetch(input, { ...init, headers });\n}\n\n/**\n * Throw an error with the message from the API response body if available,\n * falling back to a generic message. All API error responses use the shape\n * `{ error: { code, message } }`.\n */\nexport async function throwResponseError(res: Response, fallback: string): Promise<never> {\n\tconst body: unknown = await res.json().catch(() => ({}));\n\tlet message: string | undefined;\n\tif (typeof body === \"object\" && body !== null && \"error\" in body) {\n\t\tconst { error } = body;\n\t\tif (typeof error === \"object\" && error !== null && \"message\" in error) {\n\t\t\tconst { message: msg } = error;\n\t\t\tif (typeof msg === \"string\") message = msg;\n\t\t}\n\t}\n\tthrow new Error(message || `${fallback}: ${res.statusText}`);\n}\n\n/**\n * Generic paginated result\n */\nexport interface FindManyResult<T> {\n\titems: T[];\n\tnextCursor?: string;\n}\n\n/**\n * Admin manifest describing available collections and plugins\n */\nexport interface AdminManifest {\n\tversion: string;\n\thash: string;\n\tcollections: Record<\n\t\tstring,\n\t\t{\n\t\t\tlabel: string;\n\t\t\tlabelSingular: string;\n\t\t\tsupports: string[];\n\t\t\thasSeo: boolean;\n\t\t\tfields: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\tkind: string;\n\t\t\t\t\tlabel?: string;\n\t\t\t\t\trequired?: boolean;\n\t\t\t\t\twidget?: string;\n\t\t\t\t\toptions?: Array<{ value: string; label: string }>;\n\t\t\t\t}\n\t\t\t>;\n\t\t}\n\t>;\n\tplugins: Record<\n\t\tstring,\n\t\t{\n\t\t\tname?: string;\n\t\t\tversion?: string;\n\t\t\t/** Package name for dynamic import (e.g., \"@emdash-cms/plugin-audit-log\") */\n\t\t\tpackage?: string;\n\t\t\t/** Whether the plugin is enabled */\n\t\t\tenabled?: boolean;\n\t\t\t/**\n\t\t\t * How this plugin renders its admin UI:\n\t\t\t * - \"react\": Trusted plugin with React components\n\t\t\t * - \"blocks\": Declarative Block Kit UI via admin route handler\n\t\t\t * - \"none\": No admin UI\n\t\t\t */\n\t\t\tadminMode?: \"react\" | \"blocks\" | \"none\";\n\t\t\tadminPages?: Array<{\n\t\t\t\tpath: string;\n\t\t\t\tlabel?: string;\n\t\t\t\ticon?: string;\n\t\t\t}>;\n\t\t\tdashboardWidgets?: Array<{\n\t\t\t\tid: string;\n\t\t\t\ttitle?: string;\n\t\t\t\tsize?: \"full\" | \"half\" | \"third\";\n\t\t\t}>;\n\t\t\tfieldWidgets?: Array<{\n\t\t\t\tname: string;\n\t\t\t\tlabel: string;\n\t\t\t\tfieldTypes: string[];\n\t\t\t\telements?: import(\"@emdash-cms/blocks\").Element[];\n\t\t\t}>;\n\t\t\t/** Block types for Portable Text editor */\n\t\t\tportableTextBlocks?: Array<{\n\t\t\t\ttype: string;\n\t\t\t\tlabel: string;\n\t\t\t\ticon?: string;\n\t\t\t\tdescription?: string;\n\t\t\t\tplaceholder?: string;\n\t\t\t\tfields?: Element[];\n\t\t\t}>;\n\t\t}\n\t>;\n\t/**\n\t * Auth mode for the admin UI. When \"passkey\", the security settings\n\t * (passkey management, self-signup domains) are shown. When using\n\t * external auth (e.g., \"cloudflare-access\"), these are hidden since\n\t * authentication is handled externally.\n\t */\n\tauthMode: string;\n\t/**\n\t * Whether self-signup is enabled (at least one allowed domain is active).\n\t * Used by the login page to conditionally show the \"Sign up\" link.\n\t */\n\tsignupEnabled?: boolean;\n\t/**\n\t * i18n configuration. Present when multiple locales are configured.\n\t */\n\ti18n?: {\n\t\tdefaultLocale: string;\n\t\tlocales: string[];\n\t};\n\t/**\n\t * Marketplace registry URL. Present when `marketplace` is configured\n\t * in the EmDash integration. Enables marketplace features in the UI.\n\t */\n\tmarketplace?: string;\n}\n\n/**\n * Parse an API response with the { data: T } envelope.\n *\n * Handles error responses via throwResponseError, then unwraps the data envelope.\n * Replaces both bare `response.json()` and field-unwrap patterns.\n */\nexport async function parseApiResponse<T>(\n\tresponse: Response,\n\tfallbackMessage = \"Request failed\",\n): Promise<T> {\n\tif (!response.ok) await throwResponseError(response, fallbackMessage);\n\tconst body: { data: T } = await response.json();\n\treturn body.data;\n}\n\n/**\n * Fetch admin manifest\n */\nexport async function fetchManifest(): Promise<AdminManifest> {\n\tconst response = await apiFetch(`${API_BASE}/manifest`);\n\treturn parseApiResponse<AdminManifest>(response, \"Failed to fetch manifest\");\n}\n","/**\n * Plugin management APIs\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\nexport interface PluginInfo {\n\tid: string;\n\tname: string;\n\tversion: string;\n\tpackage?: string;\n\tenabled: boolean;\n\tstatus: \"installed\" | \"active\" | \"inactive\";\n\tcapabilities: string[];\n\thasAdminPages: boolean;\n\thasDashboardWidgets: boolean;\n\thasHooks: boolean;\n\tinstalledAt?: string;\n\tactivatedAt?: string;\n\tdeactivatedAt?: string;\n\t/** Plugin source: 'config' (declared in astro.config) or 'marketplace' */\n\tsource?: \"config\" | \"marketplace\";\n\t/** Installed marketplace version (set when source = 'marketplace') */\n\tmarketplaceVersion?: string;\n\t/** Description of what the plugin does */\n\tdescription?: string;\n\t/** URL to the plugin icon (marketplace plugins use the icon proxy) */\n\ticonUrl?: string;\n}\n\n/**\n * Fetch all plugins\n */\nexport async function fetchPlugins(): Promise<PluginInfo[]> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins`);\n\tconst result = await parseApiResponse<{ items: PluginInfo[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch plugins\",\n\t);\n\treturn result.items;\n}\n\n/**\n * Fetch a single plugin\n */\nexport async function fetchPlugin(pluginId: string): Promise<PluginInfo> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins/${pluginId}`);\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" not found`);\n\t\t}\n\t\tawait throwResponseError(response, \"Failed to fetch plugin\");\n\t}\n\tconst result = await parseApiResponse<{ item: PluginInfo }>(response, \"Failed to fetch plugin\");\n\treturn result.item;\n}\n\n/**\n * Enable a plugin\n */\nexport async function enablePlugin(pluginId: string): Promise<PluginInfo> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins/${pluginId}/enable`, {\n\t\tmethod: \"POST\",\n\t});\n\tconst result = await parseApiResponse<{ item: PluginInfo }>(response, \"Failed to enable plugin\");\n\treturn result.item;\n}\n\n/**\n * Disable a plugin\n */\nexport async function disablePlugin(pluginId: string): Promise<PluginInfo> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins/${pluginId}/disable`, {\n\t\tmethod: \"POST\",\n\t});\n\tconst result = await parseApiResponse<{ item: PluginInfo }>(response, \"Failed to disable plugin\");\n\treturn result.item;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAMA,MAAa,WAAW;;;;;AAMxB,SAAgB,SAAS,OAA+B,MAAuC;CAC9F,MAAM,UAAU,IAAI,QAAQ,MAAM,QAAQ;AAC1C,SAAQ,IAAI,oBAAoB,IAAI;AACpC,QAAO,MAAM,OAAO;EAAE,GAAG;EAAM;EAAS,CAAC;;;;;;;AAQ1C,eAAsB,mBAAmB,KAAe,UAAkC;CACzF,MAAM,OAAgB,MAAM,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE;CACxD,IAAI;AACJ,KAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,WAAW,MAAM;EACjE,MAAM,EAAE,UAAU;AAClB,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,aAAa,OAAO;GACtE,MAAM,EAAE,SAAS,QAAQ;AACzB,OAAI,OAAO,QAAQ,SAAU,WAAU;;;AAGzC,OAAM,IAAI,MAAM,WAAW,GAAG,SAAS,IAAI,IAAI,aAAa;;;;;;;;AA+G7D,eAAsB,iBACrB,UACA,kBAAkB,kBACL;AACb,KAAI,CAAC,SAAS,GAAI,OAAM,mBAAmB,UAAU,gBAAgB;AAErE,SAD0B,MAAM,SAAS,MAAM,EACnC;;;;;AAMb,eAAsB,gBAAwC;AAE7D,QAAO,iBADU,MAAM,SAAS,GAAG,SAAS,WAAW,EACN,2BAA2B;;;;;;;;;;;;;;;;;AC7H7E,eAAsB,eAAsC;AAM3D,SAJe,MAAM,iBADJ,MAAM,SAAS,GAAG,SAAS,gBAAgB,EAG3D,0BACA,EACa;;;;;AAMf,eAAsB,YAAY,UAAuC;CACxE,MAAM,WAAW,MAAM,SAAS,GAAG,SAAS,iBAAiB,WAAW;AACxE,KAAI,CAAC,SAAS,IAAI;AACjB,MAAI,SAAS,WAAW,IACvB,OAAM,IAAI,MAAM,WAAW,SAAS,aAAa;AAElD,QAAM,mBAAmB,UAAU,yBAAyB;;AAG7D,SADe,MAAM,iBAAuC,UAAU,yBAAyB,EACjF;;;;;AAMf,eAAsB,aAAa,UAAuC;AAKzE,SADe,MAAM,iBAHJ,MAAM,SAAS,GAAG,SAAS,iBAAiB,SAAS,UAAU,EAC/E,QAAQ,QACR,CAAC,EACoE,0BAA0B,EAClF;;;;;AAMf,eAAsB,cAAc,UAAuC;AAK1E,SADe,MAAM,iBAHJ,MAAM,SAAS,GAAG,SAAS,iBAAiB,SAAS,WAAW,EAChF,QAAQ,QACR,CAAC,EACoE,2BAA2B,EACnF"}
1
+ {"version":3,"file":"plugins-XhZqfegd.js","names":[],"sources":["../src/lib/api/client.ts","../src/lib/api/plugins.ts"],"sourcesContent":["/**\n * Base API client configuration and shared types\n */\n\nimport type { Element } from \"@emdash-cms/blocks\";\n\nexport const API_BASE = \"/_emdash/api\";\n\n/**\n * Fetch wrapper that adds the X-EmDash-Request CSRF protection header\n * to all requests. All API calls should use this instead of raw fetch().\n */\nexport function apiFetch(input: string | URL | Request, init?: RequestInit): Promise<Response> {\n\tconst headers = new Headers(init?.headers);\n\theaders.set(\"X-EmDash-Request\", \"1\");\n\treturn fetch(input, { ...init, headers });\n}\n\n/**\n * Throw an error with the message from the API response body if available,\n * falling back to a generic message. All API error responses use the shape\n * `{ error: { code, message } }`.\n */\nexport async function throwResponseError(res: Response, fallback: string): Promise<never> {\n\tconst body: unknown = await res.json().catch(() => ({}));\n\tlet message: string | undefined;\n\tif (typeof body === \"object\" && body !== null && \"error\" in body) {\n\t\tconst { error } = body;\n\t\tif (typeof error === \"object\" && error !== null && \"message\" in error) {\n\t\t\tconst { message: msg } = error;\n\t\t\tif (typeof msg === \"string\") message = msg;\n\t\t}\n\t}\n\tthrow new Error(message || `${fallback}: ${res.statusText}`);\n}\n\n/**\n * Generic paginated result\n */\nexport interface FindManyResult<T> {\n\titems: T[];\n\tnextCursor?: string;\n}\n\n/**\n * Admin manifest describing available collections and plugins\n */\nexport interface AdminManifest {\n\tversion: string;\n\thash: string;\n\tcollections: Record<\n\t\tstring,\n\t\t{\n\t\t\tlabel: string;\n\t\t\tlabelSingular: string;\n\t\t\tsupports: string[];\n\t\t\thasSeo: boolean;\n\t\t\turlPattern?: string;\n\t\t\tfields: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\tkind: string;\n\t\t\t\t\tlabel?: string;\n\t\t\t\t\trequired?: boolean;\n\t\t\t\t\twidget?: string;\n\t\t\t\t\toptions?: Array<{ value: string; label: string }>;\n\t\t\t\t\tvalidation?: Record<string, unknown>;\n\t\t\t\t}\n\t\t\t>;\n\t\t}\n\t>;\n\tplugins: Record<\n\t\tstring,\n\t\t{\n\t\t\tname?: string;\n\t\t\tversion?: string;\n\t\t\t/** Package name for dynamic import (e.g., \"@emdash-cms/plugin-audit-log\") */\n\t\t\tpackage?: string;\n\t\t\t/** Whether the plugin is enabled */\n\t\t\tenabled?: boolean;\n\t\t\t/**\n\t\t\t * How this plugin renders its admin UI:\n\t\t\t * - \"react\": Trusted plugin with React components\n\t\t\t * - \"blocks\": Declarative Block Kit UI via admin route handler\n\t\t\t * - \"none\": No admin UI\n\t\t\t */\n\t\t\tadminMode?: \"react\" | \"blocks\" | \"none\";\n\t\t\tadminPages?: Array<{\n\t\t\t\tpath: string;\n\t\t\t\tlabel?: string;\n\t\t\t\ticon?: string;\n\t\t\t}>;\n\t\t\tdashboardWidgets?: Array<{\n\t\t\t\tid: string;\n\t\t\t\ttitle?: string;\n\t\t\t\tsize?: \"full\" | \"half\" | \"third\";\n\t\t\t}>;\n\t\t\tfieldWidgets?: Array<{\n\t\t\t\tname: string;\n\t\t\t\tlabel: string;\n\t\t\t\tfieldTypes: string[];\n\t\t\t\telements?: import(\"@emdash-cms/blocks\").Element[];\n\t\t\t}>;\n\t\t\t/** Block types for Portable Text editor */\n\t\t\tportableTextBlocks?: Array<{\n\t\t\t\ttype: string;\n\t\t\t\tlabel: string;\n\t\t\t\ticon?: string;\n\t\t\t\tdescription?: string;\n\t\t\t\tplaceholder?: string;\n\t\t\t\tfields?: Element[];\n\t\t\t}>;\n\t\t}\n\t>;\n\t/**\n\t * Auth mode for the admin UI. When \"passkey\", the security settings\n\t * (passkey management, self-signup domains) are shown. When using\n\t * external auth (e.g., \"cloudflare-access\"), these are hidden since\n\t * authentication is handled externally.\n\t */\n\tauthMode: string;\n\t/**\n\t * Whether self-signup is enabled (at least one allowed domain is active).\n\t * Used by the login page to conditionally show the \"Sign up\" link.\n\t */\n\tsignupEnabled?: boolean;\n\t/**\n\t * i18n configuration. Present when multiple locales are configured.\n\t */\n\ti18n?: {\n\t\tdefaultLocale: string;\n\t\tlocales: string[];\n\t};\n\t/**\n\t * Marketplace registry URL. Present when `marketplace` is configured\n\t * in the EmDash integration. Enables marketplace features in the UI.\n\t */\n\tmarketplace?: string;\n}\n\n/**\n * Parse an API response with the { data: T } envelope.\n *\n * Handles error responses via throwResponseError, then unwraps the data envelope.\n * Replaces both bare `response.json()` and field-unwrap patterns.\n */\nexport async function parseApiResponse<T>(\n\tresponse: Response,\n\tfallbackMessage = \"Request failed\",\n): Promise<T> {\n\tif (!response.ok) await throwResponseError(response, fallbackMessage);\n\tconst body: { data: T } = await response.json();\n\treturn body.data;\n}\n\n/**\n * Fetch admin manifest\n */\nexport async function fetchManifest(): Promise<AdminManifest> {\n\tconst response = await apiFetch(`${API_BASE}/manifest`);\n\treturn parseApiResponse<AdminManifest>(response, \"Failed to fetch manifest\");\n}\n","/**\n * Plugin management APIs\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\nexport interface PluginInfo {\n\tid: string;\n\tname: string;\n\tversion: string;\n\tpackage?: string;\n\tenabled: boolean;\n\tstatus: \"installed\" | \"active\" | \"inactive\";\n\tcapabilities: string[];\n\thasAdminPages: boolean;\n\thasDashboardWidgets: boolean;\n\thasHooks: boolean;\n\tinstalledAt?: string;\n\tactivatedAt?: string;\n\tdeactivatedAt?: string;\n\t/** Plugin source: 'config' (declared in astro.config) or 'marketplace' */\n\tsource?: \"config\" | \"marketplace\";\n\t/** Installed marketplace version (set when source = 'marketplace') */\n\tmarketplaceVersion?: string;\n\t/** Description of what the plugin does */\n\tdescription?: string;\n\t/** URL to the plugin icon (marketplace plugins use the icon proxy) */\n\ticonUrl?: string;\n}\n\n/**\n * Fetch all plugins\n */\nexport async function fetchPlugins(): Promise<PluginInfo[]> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins`);\n\tconst result = await parseApiResponse<{ items: PluginInfo[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch plugins\",\n\t);\n\treturn result.items;\n}\n\n/**\n * Fetch a single plugin\n */\nexport async function fetchPlugin(pluginId: string): Promise<PluginInfo> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins/${pluginId}`);\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" not found`);\n\t\t}\n\t\tawait throwResponseError(response, \"Failed to fetch plugin\");\n\t}\n\tconst result = await parseApiResponse<{ item: PluginInfo }>(response, \"Failed to fetch plugin\");\n\treturn result.item;\n}\n\n/**\n * Enable a plugin\n */\nexport async function enablePlugin(pluginId: string): Promise<PluginInfo> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins/${pluginId}/enable`, {\n\t\tmethod: \"POST\",\n\t});\n\tconst result = await parseApiResponse<{ item: PluginInfo }>(response, \"Failed to enable plugin\");\n\treturn result.item;\n}\n\n/**\n * Disable a plugin\n */\nexport async function disablePlugin(pluginId: string): Promise<PluginInfo> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins/${pluginId}/disable`, {\n\t\tmethod: \"POST\",\n\t});\n\tconst result = await parseApiResponse<{ item: PluginInfo }>(response, \"Failed to disable plugin\");\n\treturn result.item;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAMA,MAAa,WAAW;;;;;AAMxB,SAAgB,SAAS,OAA+B,MAAuC;CAC9F,MAAM,UAAU,IAAI,QAAQ,MAAM,QAAQ;AAC1C,SAAQ,IAAI,oBAAoB,IAAI;AACpC,QAAO,MAAM,OAAO;EAAE,GAAG;EAAM;EAAS,CAAC;;;;;;;AAQ1C,eAAsB,mBAAmB,KAAe,UAAkC;CACzF,MAAM,OAAgB,MAAM,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE;CACxD,IAAI;AACJ,KAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,WAAW,MAAM;EACjE,MAAM,EAAE,UAAU;AAClB,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,aAAa,OAAO;GACtE,MAAM,EAAE,SAAS,QAAQ;AACzB,OAAI,OAAO,QAAQ,SAAU,WAAU;;;AAGzC,OAAM,IAAI,MAAM,WAAW,GAAG,SAAS,IAAI,IAAI,aAAa;;;;;;;;AAiH7D,eAAsB,iBACrB,UACA,kBAAkB,kBACL;AACb,KAAI,CAAC,SAAS,GAAI,OAAM,mBAAmB,UAAU,gBAAgB;AAErE,SAD0B,MAAM,SAAS,MAAM,EACnC;;;;;AAMb,eAAsB,gBAAwC;AAE7D,QAAO,iBADU,MAAM,SAAS,GAAG,SAAS,WAAW,EACN,2BAA2B;;;;;;;;;;;;;;;;;AC/H7E,eAAsB,eAAsC;AAM3D,SAJe,MAAM,iBADJ,MAAM,SAAS,GAAG,SAAS,gBAAgB,EAG3D,0BACA,EACa;;;;;AAMf,eAAsB,YAAY,UAAuC;CACxE,MAAM,WAAW,MAAM,SAAS,GAAG,SAAS,iBAAiB,WAAW;AACxE,KAAI,CAAC,SAAS,IAAI;AACjB,MAAI,SAAS,WAAW,IACvB,OAAM,IAAI,MAAM,WAAW,SAAS,aAAa;AAElD,QAAM,mBAAmB,UAAU,yBAAyB;;AAG7D,SADe,MAAM,iBAAuC,UAAU,yBAAyB,EACjF;;;;;AAMf,eAAsB,aAAa,UAAuC;AAKzE,SADe,MAAM,iBAHJ,MAAM,SAAS,GAAG,SAAS,iBAAiB,SAAS,UAAU,EAC/E,QAAQ,QACR,CAAC,EACoE,0BAA0B,EAClF;;;;;AAMf,eAAsB,cAAc,UAAuC;AAK1E,SADe,MAAM,iBAHJ,MAAM,SAAS,GAAG,SAAS,iBAAiB,SAAS,WAAW,EAChF,QAAQ,QACR,CAAC,EACoE,2BAA2B,EACnF"}