@ashdev/codex-plugin-metadata-mangabaka 1.8.2 → 1.8.3

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.
package/dist/index.js CHANGED
@@ -889,7 +889,7 @@ async function handleSearch(params, client2) {
889
889
  // package.json
890
890
  var package_default = {
891
891
  name: "@ashdev/codex-plugin-metadata-mangabaka",
892
- version: "1.8.2",
892
+ version: "1.8.3",
893
893
  description: "MangaBaka metadata provider plugin for Codex",
894
894
  main: "dist/index.js",
895
895
  bin: "dist/index.js",
@@ -928,7 +928,7 @@ var package_default = {
928
928
  node: ">=22.0.0"
929
929
  },
930
930
  dependencies: {
931
- "@ashdev/codex-plugin-sdk": "^1.8.2"
931
+ "@ashdev/codex-plugin-sdk": "^1.8.3"
932
932
  },
933
933
  devDependencies: {
934
934
  "@biomejs/biome": "^2.3.13",
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../node_modules/@ashdev/codex-plugin-sdk/src/types/rpc.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/errors.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/logger.ts", "../node_modules/@ashdev/codex-plugin-sdk/src/server.ts", "../src/api.ts", "../src/mappers.ts", "../src/handlers/get.ts", "../src/handlers/match.ts", "../src/handlers/search.ts", "../package.json", "../src/manifest.ts", "../src/index.ts"],
4
- "sourcesContent": [null, null, null, null, "/**\n * MangaBaka API client\n * API docs: https://mangabaka.org/api\n */\n\nimport {\n ApiError,\n AuthError,\n createLogger,\n NotFoundError,\n RateLimitError,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MbGetSeriesResponse, MbSearchResponse, MbSeries } from \"./types.js\";\n\nconst BASE_URL = \"https://api.mangabaka.dev\";\nconst DEFAULT_TIMEOUT_SECONDS = 60;\nconst logger = createLogger({ name: \"mangabaka-api\", level: \"debug\" });\n\nexport interface MangaBakaClientOptions {\n /** Request timeout in seconds (default: 15) */\n timeout?: number;\n}\n\nexport class MangaBakaClient {\n private readonly apiKey: string;\n private readonly timeoutMs: number;\n\n constructor(apiKey: string, options?: MangaBakaClientOptions) {\n if (!apiKey) {\n throw new AuthError(\"API key is required\");\n }\n this.apiKey = apiKey;\n this.timeoutMs = (options?.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;\n logger.debug(`MangaBakaClient initialized with timeout: ${this.timeoutMs}ms`);\n }\n\n /**\n * Search for series by query\n */\n async search(\n query: string,\n page = 1,\n perPage = 20,\n ): Promise<{ data: MbSeries[]; total: number; page: number; totalPages: number }> {\n logger.debug(`Searching for: \"${query}\" (page ${page})`);\n\n const params = new URLSearchParams({\n q: query,\n page: String(page),\n limit: String(perPage),\n });\n\n const response = await this.request<MbSearchResponse>(`/v1/series/search?${params.toString()}`);\n\n return {\n data: response.data,\n total: response.pagination?.total ?? response.data.length,\n page: response.pagination?.page ?? page,\n totalPages: response.pagination?.total_pages ?? 1,\n };\n }\n\n /**\n * Get full series details by ID\n */\n async getSeries(id: number): Promise<MbSeries> {\n logger.debug(`Getting series: ${id}`);\n\n const response = await this.request<MbGetSeriesResponse>(`/v1/series/${id}`);\n\n return response.data;\n }\n\n /**\n * Make an authenticated request to the MangaBaka API\n */\n private async request<T>(path: string): Promise<T> {\n const url = `${BASE_URL}${path}`;\n const headers: Record<string, string> = {\n \"x-api-key\": this.apiKey,\n Accept: \"application/json\",\n };\n\n // Set up timeout using AbortController\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n logger.debug(`Request: ${path} (timeout: ${this.timeoutMs}ms)`);\n const response = await fetch(url, {\n method: \"GET\",\n headers,\n signal: controller.signal,\n });\n\n // Handle rate limiting\n if (response.status === 429) {\n const retryAfter = response.headers.get(\"Retry-After\");\n const seconds = retryAfter ? Number.parseInt(retryAfter, 10) : 60;\n throw new RateLimitError(seconds);\n }\n\n // Handle auth errors\n if (response.status === 401 || response.status === 403) {\n throw new AuthError(\"Invalid API key\");\n }\n\n // Handle not found\n if (response.status === 404) {\n throw new NotFoundError(`Resource not found: ${path}`);\n }\n\n // Handle other errors\n if (!response.ok) {\n const text = await response.text();\n logger.error(`API error: ${response.status}`, { body: text });\n throw new ApiError(`API error: ${response.status} ${response.statusText}`, response.status);\n }\n\n return response.json() as Promise<T>;\n } catch (error) {\n // Handle timeout (abort)\n if (error instanceof Error && error.name === \"AbortError\") {\n logger.error(`Request timed out after ${this.timeoutMs}ms: ${path}`);\n throw new ApiError(`Request timed out after ${this.timeoutMs / 1000}s`);\n }\n\n // Re-throw plugin errors\n if (\n error instanceof RateLimitError ||\n error instanceof AuthError ||\n error instanceof NotFoundError ||\n error instanceof ApiError\n ) {\n throw error;\n }\n\n // Wrap other errors\n const message = error instanceof Error ? error.message : \"Unknown error\";\n logger.error(\"Request failed\", error);\n throw new ApiError(`Request failed: ${message}`);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n}\n", "/**\n * Mappers to convert MangaBaka API responses to Codex plugin protocol types\n */\n\nimport type {\n AlternateTitle,\n ExternalLink,\n ExternalRating,\n PluginSeriesMetadata,\n ReadingDirection,\n SearchResult,\n SeriesStatus,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MbContentRating, MbSeries, MbSeriesType, MbStatus } from \"./types.js\";\n\n/**\n * Strip HTML tags from text, converting <br> to newlines\n */\nfunction stripHtml(html: string | undefined | null): string | undefined {\n if (!html) return undefined;\n return html\n .replace(/<br\\s*\\/?>/gi, \"\\n\") // Convert <br> to newlines\n .replace(/<[^>]*>/g, \"\") // Strip remaining HTML tags\n .trim();\n}\n\n/**\n * Map MangaBaka status to protocol SeriesStatus\n * MangaBaka uses: cancelled, completed, hiatus, releasing, unknown, upcoming\n * Codex uses: ongoing, ended, hiatus, abandoned, unknown\n */\nfunction mapStatus(mbStatus: MbStatus): SeriesStatus {\n switch (mbStatus) {\n case \"completed\":\n return \"ended\";\n case \"releasing\":\n case \"upcoming\":\n return \"ongoing\";\n case \"hiatus\":\n return \"hiatus\";\n case \"cancelled\":\n return \"abandoned\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Format genre from snake_case to Title Case\n */\nfunction formatGenre(genre: string): string {\n return genre\n .split(\"_\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\n/**\n * Detect language code from country of origin\n */\nfunction detectLanguageFromCountry(country: string | null | undefined): string | undefined {\n if (!country) return undefined;\n\n const countryLower = country.toLowerCase();\n if (countryLower === \"jp\" || countryLower === \"japan\") return \"ja\";\n if (countryLower === \"kr\" || countryLower === \"korea\" || countryLower === \"south korea\")\n return \"ko\";\n if (countryLower === \"cn\" || countryLower === \"china\") return \"zh\";\n if (countryLower === \"tw\" || countryLower === \"taiwan\") return \"zh-TW\";\n\n return undefined;\n}\n\n/**\n * Map MangaBaka content rating to numeric age rating\n */\nfunction mapContentRating(rating: MbContentRating | null | undefined): number | undefined {\n if (!rating) return undefined;\n\n switch (rating) {\n case \"safe\":\n return 0; // All ages\n case \"suggestive\":\n return 13; // Teen\n case \"erotica\":\n return 16; // Mature\n case \"pornographic\":\n return 18; // Adults only\n default:\n return undefined;\n }\n}\n\n/**\n * Extract rating value from either a number or an object with bayesian/average\n */\nfunction extractRating(\n rating: number | { bayesian?: number | null; average?: number | null } | null | undefined,\n): number | undefined {\n if (rating == null) return undefined;\n if (typeof rating === \"number\") return rating;\n return rating.bayesian ?? rating.average ?? undefined;\n}\n\n/**\n * Infer reading direction from series type and country\n */\nfunction inferReadingDirection(\n seriesType: MbSeriesType,\n country: string | null | undefined,\n): ReadingDirection | undefined {\n // Manhwa (Korean) and Manhua (Chinese) are typically left-to-right\n if (seriesType === \"manhwa\" || seriesType === \"manhua\") {\n return \"ltr\";\n }\n\n // Manga (Japanese) is typically right-to-left\n if (seriesType === \"manga\") {\n return \"rtl\";\n }\n\n // OEL (Original English Language) is left-to-right\n if (seriesType === \"oel\") {\n return \"ltr\";\n }\n\n // Fall back to country-based detection\n if (country) {\n const countryLower = country.toLowerCase();\n if (countryLower === \"jp\" || countryLower === \"japan\") return \"rtl\";\n if (countryLower === \"kr\" || countryLower === \"korea\" || countryLower === \"south korea\")\n return \"ltr\";\n if (countryLower === \"cn\" || countryLower === \"china\") return \"ltr\";\n if (countryLower === \"tw\" || countryLower === \"taiwan\") return \"ltr\";\n }\n\n return undefined;\n}\n\n/**\n * Map a MangaBaka series to a protocol SearchResult\n */\nexport function mapSearchResult(series: MbSeries): SearchResult {\n // Get cover URL - prefer x250 for search results\n const coverUrl = series.cover?.x250?.x1 ?? series.cover?.raw?.url ?? undefined;\n\n // Build alternate titles array\n const alternateTitles: string[] = [];\n if (series.native_title && series.native_title !== series.title) {\n alternateTitles.push(series.native_title);\n }\n if (series.romanized_title && series.romanized_title !== series.title) {\n alternateTitles.push(series.romanized_title);\n }\n\n // Note: relevanceScore is omitted - the API already returns results in relevance order\n return {\n externalId: String(series.id),\n title: series.title,\n alternateTitles,\n year: series.year ?? undefined,\n coverUrl: coverUrl ?? undefined,\n preview: {\n status: mapStatus(series.status),\n genres: (series.genres ?? []).slice(0, 3).map(formatGenre),\n rating: extractRating(series.rating),\n description: stripHtml(series.description)?.slice(0, 200) ?? undefined,\n bookCount: series.final_volume ? Number.parseInt(series.final_volume, 10) : undefined,\n },\n };\n}\n\n/**\n * Map full series response to protocol PluginSeriesMetadata\n */\nexport function mapSeriesMetadata(series: MbSeries): PluginSeriesMetadata {\n // Build alternate titles array with language info\n const alternateTitles: AlternateTitle[] = [];\n\n // Add native title\n if (series.native_title && series.native_title !== series.title) {\n alternateTitles.push({\n title: series.native_title,\n language: detectLanguageFromCountry(series.country_of_origin),\n titleType: \"native\",\n });\n }\n\n // Add romanized title\n if (series.romanized_title && series.romanized_title !== series.title) {\n alternateTitles.push({\n title: series.romanized_title,\n language: \"en\",\n titleType: \"romaji\",\n });\n }\n\n // Add secondary titles from all languages\n if (series.secondary_titles) {\n for (const [langCode, titleList] of Object.entries(series.secondary_titles)) {\n if (titleList) {\n for (const titleEntry of titleList) {\n if (titleEntry.title !== series.title) {\n alternateTitles.push({\n title: titleEntry.title,\n language: langCode,\n });\n }\n }\n }\n }\n }\n\n // Extract authors and artists as string arrays\n const authors = series.authors ?? [];\n const artists = series.artists ?? [];\n\n // Format genres\n const genres = (series.genres ?? []).map(formatGenre);\n\n // Get cover URL - prefer raw for full metadata\n const coverUrl = series.cover?.raw?.url ?? series.cover?.x350?.x1 ?? undefined;\n\n // Build external links from sources\n // Always include MangaBaka link first\n const externalLinks: ExternalLink[] = [\n {\n url: `https://mangabaka.org/${series.id}`,\n label: \"MangaBaka\",\n linkType: \"provider\",\n },\n ];\n\n // Source configuration: display name, rating key, and URL pattern\n // URL pattern uses {id} as placeholder for the source ID\n const sourceConfig: Record<string, { label: string; ratingKey: string; urlPattern?: string }> = {\n anilist: {\n label: \"AniList\",\n ratingKey: \"anilist\",\n urlPattern: \"https://anilist.co/manga/{id}\",\n },\n my_anime_list: {\n label: \"MyAnimeList\",\n ratingKey: \"myanimelist\",\n urlPattern: \"https://myanimelist.net/manga/{id}\",\n },\n mangadex: {\n label: \"MangaDex\",\n ratingKey: \"mangadex\",\n urlPattern: \"https://mangadex.org/title/{id}\",\n },\n manga_updates: {\n label: \"MangaUpdates\",\n ratingKey: \"mangaupdates\",\n urlPattern: \"https://www.mangaupdates.com/series/{id}\",\n },\n kitsu: { label: \"Kitsu\", ratingKey: \"kitsu\", urlPattern: \"https://kitsu.app/manga/{id}\" },\n anime_planet: {\n label: \"Anime-Planet\",\n ratingKey: \"animeplanet\",\n urlPattern: \"https://www.anime-planet.com/manga/{id}\",\n },\n anime_news_network: { label: \"Anime News Network\", ratingKey: \"animenewsnetwork\" },\n shikimori: {\n label: \"Shikimori\",\n ratingKey: \"shikimori\",\n urlPattern: \"https://shikimori.one/mangas/{id}\",\n },\n };\n\n // Build external links and ratings from sources in a single pass\n const externalRatings: ExternalRating[] = [];\n\n if (series.source) {\n for (const [key, info] of Object.entries(series.source)) {\n if (!info) continue;\n\n const config = sourceConfig[key];\n // Use config if available, otherwise generate defaults from key\n const ratingKey = config?.ratingKey ?? key.replace(/_/g, \"\");\n\n // Add external link if source has an ID and URL pattern\n if (info.id != null && config?.urlPattern) {\n externalLinks.push({\n url: config.urlPattern.replace(\"{id}\", String(info.id)),\n label: config.label,\n linkType: \"provider\",\n });\n }\n\n // Add external rating if source has a normalized rating\n if (info.rating_normalized != null) {\n externalRatings.push({ score: info.rating_normalized, source: ratingKey });\n }\n }\n }\n\n // Get publisher name (pick first one if available)\n const publisher = series.publishers?.[0]?.name ?? undefined;\n\n return {\n externalId: String(series.id),\n externalUrl: `https://mangabaka.org/${series.id}`,\n title: series.title,\n alternateTitles,\n summary: stripHtml(series.description),\n status: mapStatus(series.status),\n year: series.year ?? undefined,\n // Extended metadata\n publisher,\n totalBookCount: series.final_volume ? Number.parseInt(series.final_volume, 10) : undefined,\n ageRating: mapContentRating(series.content_rating),\n readingDirection: inferReadingDirection(series.type, series.country_of_origin),\n // Taxonomy\n genres,\n tags: series.tags ?? [],\n authors,\n artists,\n coverUrl: coverUrl ?? undefined,\n rating: (() => {\n const r = extractRating(series.rating);\n return r != null ? { score: r, source: \"mangabaka\" } : undefined;\n })(),\n externalRatings: externalRatings.length > 0 ? externalRatings : undefined,\n externalLinks,\n };\n}\n", "import {\n type MetadataGetParams,\n NotFoundError,\n type PluginSeriesMetadata,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MangaBakaClient } from \"../api.js\";\nimport { mapSeriesMetadata } from \"../mappers.js\";\n\nexport async function handleGet(\n params: MetadataGetParams,\n client: MangaBakaClient,\n): Promise<PluginSeriesMetadata> {\n const seriesId = Number.parseInt(params.externalId, 10);\n\n if (Number.isNaN(seriesId)) {\n throw new NotFoundError(`Invalid external ID: ${params.externalId}`);\n }\n\n const response = await client.getSeries(seriesId);\n\n return mapSeriesMetadata(response);\n}\n", "import {\n createLogger,\n type MetadataMatchParams,\n type MetadataMatchResponse,\n type SearchResult,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MangaBakaClient } from \"../api.js\";\nimport { mapSearchResult } from \"../mappers.js\";\n\nconst logger = createLogger({ name: \"mangabaka-match\", level: \"info\" });\n\n/**\n * Calculate string similarity using word overlap\n * Returns a value between 0 and 1\n */\nfunction similarity(a: string, b: string): number {\n const aLower = a.toLowerCase().trim();\n const bLower = b.toLowerCase().trim();\n\n if (aLower === bLower) return 1.0;\n if (aLower.length === 0 || bLower.length === 0) return 0;\n\n // Check if one contains the other\n if (aLower.includes(bLower) || bLower.includes(aLower)) {\n return 0.8;\n }\n\n // Simple word overlap scoring\n const aWords = new Set(aLower.split(/\\s+/));\n const bWords = new Set(bLower.split(/\\s+/));\n const intersection = [...aWords].filter((w) => bWords.has(w));\n const union = new Set([...aWords, ...bWords]);\n\n return intersection.length / union.size;\n}\n\n/**\n * Score a search result against the match parameters\n * Returns a value between 0 and 1\n */\nfunction scoreResult(result: SearchResult, params: MetadataMatchParams): number {\n let score = 0;\n\n // Title similarity (up to 0.6)\n const titleScore = similarity(result.title, params.title);\n score += titleScore * 0.6;\n\n // Year match (up to 0.2)\n if (params.year && result.year) {\n if (result.year === params.year) {\n score += 0.2;\n } else if (Math.abs(result.year - params.year) <= 1) {\n score += 0.1;\n }\n }\n\n // Boost for exact title match (up to 0.2)\n if (result.title.toLowerCase() === params.title.toLowerCase()) {\n score += 0.2;\n }\n\n return Math.min(1.0, score);\n}\n\nexport async function handleMatch(\n params: MetadataMatchParams,\n client: MangaBakaClient,\n): Promise<MetadataMatchResponse> {\n logger.debug(`Matching: \"${params.title}\"`);\n\n // Search for the title\n const response = await client.search(params.title, 1, 10);\n\n if (response.data.length === 0) {\n return {\n match: null,\n confidence: 0,\n };\n }\n\n // Map and score results\n const scoredResults = response.data\n .map((series) => {\n const result = mapSearchResult(series);\n const score = scoreResult(result, params);\n return { result, score };\n })\n .sort((a, b) => b.score - a.score);\n\n const best = scoredResults[0];\n\n if (!best) {\n return {\n match: null,\n confidence: 0,\n };\n }\n\n // If confidence is low, include alternatives\n const alternatives =\n best.score < 0.8\n ? scoredResults.slice(1, 4).map((s) => ({\n ...s.result,\n relevanceScore: s.score,\n }))\n : undefined;\n\n return {\n match: {\n ...best.result,\n relevanceScore: best.score,\n },\n confidence: best.score,\n alternatives,\n };\n}\n", "import {\n createLogger,\n type MetadataSearchParams,\n type MetadataSearchResponse,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MangaBakaClient } from \"../api.js\";\nimport { mapSearchResult } from \"../mappers.js\";\n\nconst logger = createLogger({ name: \"mangabaka-search\", level: \"debug\" });\n\nexport async function handleSearch(\n params: MetadataSearchParams,\n client: MangaBakaClient,\n): Promise<MetadataSearchResponse> {\n logger.debug(\"Search params received:\", params);\n\n const limit = params.limit ?? 20;\n\n // Parse cursor as page number (default to 1)\n const page = params.cursor ? Number.parseInt(params.cursor, 10) : 1;\n\n logger.debug(`Searching for: \"${params.query}\" (page ${page}, limit ${limit})`);\n\n const response = await client.search(params.query, page, limit);\n\n // Map results - API already returns them sorted by relevance\n const results = response.data.map(mapSearchResult);\n\n // Calculate next cursor (next page number) if there are more results\n const hasNextPage = response.page < response.totalPages;\n const nextCursor = hasNextPage ? String(response.page + 1) : undefined;\n\n return {\n results,\n nextCursor,\n };\n}\n", "{\n \"name\": \"@ashdev/codex-plugin-metadata-mangabaka\",\n \"version\": \"1.8.2\",\n \"description\": \"MangaBaka metadata provider plugin for Codex\",\n \"main\": \"dist/index.js\",\n \"bin\": \"dist/index.js\",\n \"type\": \"module\",\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AshDevFr/codex.git\",\n \"directory\": \"plugins/metadata-mangabaka\"\n },\n \"scripts\": {\n \"build\": \"esbuild src/index.ts --bundle --platform=node --target=node22 --format=esm --outfile=dist/index.js --sourcemap --banner:js='#!/usr/bin/env node'\",\n \"dev\": \"npm run build -- --watch\",\n \"clean\": \"rm -rf dist\",\n \"start\": \"node dist/index.js\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"npm run lint && npm run build\"\n },\n \"keywords\": [\n \"codex\",\n \"plugin\",\n \"mangabaka\",\n \"manga\",\n \"metadata\"\n ],\n \"author\": \"Codex\",\n \"license\": \"MIT\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"dependencies\": {\n \"@ashdev/codex-plugin-sdk\": \"^1.8.2\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.3.13\",\n \"@types/node\": \"^22.0.0\",\n \"esbuild\": \"^0.24.0\",\n \"typescript\": \"^5.7.0\",\n \"vitest\": \"^3.0.0\"\n }\n}\n", "import type { MetadataContentType, PluginManifest } from \"@ashdev/codex-plugin-sdk\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\nexport const manifest = {\n name: \"metadata-mangabaka\",\n displayName: \"MangaBaka Metadata\",\n version: packageJson.version,\n description: \"Fetch manga metadata from MangaBaka - aggregated data from multiple sources\",\n author: \"Codex\",\n homepage: \"https://mangabaka.org\",\n protocolVersion: \"1.0\",\n capabilities: {\n metadataProvider: [\"series\"] as MetadataContentType[],\n },\n requiredCredentials: [\n {\n key: \"api_key\",\n label: \"API Key\",\n description: \"Get your API key at https://mangabaka.org/settings/api (requires account)\",\n required: true,\n sensitive: true,\n type: \"password\",\n placeholder: \"mb-...\",\n },\n ],\n configSchema: {\n description: \"Optional configuration for the MangaBaka plugin\",\n fields: [\n {\n key: \"timeout\",\n label: \"Request Timeout\",\n description: \"HTTP request timeout in seconds for API calls to MangaBaka\",\n type: \"number\",\n required: false,\n default: 60,\n example: 30,\n },\n ],\n },\n} as const satisfies PluginManifest & {\n capabilities: { metadataProvider: MetadataContentType[] };\n};\n", "/**\n * MangaBaka Plugin - Fetch manga metadata from MangaBaka\n *\n * MangaBaka aggregates metadata from multiple sources (AniList, MAL, MangaDex, etc.)\n * and provides a unified API for manga/novel metadata.\n *\n * API docs: https://mangabaka.org/api\n *\n * Credentials are provided by Codex via the initialize message.\n * Required credential: api_key (get one at https://mangabaka.org/settings/api)\n */\n\nimport {\n ConfigError,\n createLogger,\n createMetadataPlugin,\n type InitializeParams,\n type MetadataProvider,\n} from \"@ashdev/codex-plugin-sdk\";\nimport { MangaBakaClient } from \"./api.js\";\nimport { handleGet } from \"./handlers/get.js\";\nimport { handleMatch } from \"./handlers/match.js\";\nimport { handleSearch } from \"./handlers/search.js\";\nimport { manifest } from \"./manifest.js\";\n\nconst logger = createLogger({ name: \"mangabaka\", level: \"info\" });\n\n// Client is initialized when we receive credentials from Codex\nlet client: MangaBakaClient | null = null;\n\nfunction getClient(): MangaBakaClient {\n if (!client) {\n throw new ConfigError(\"Plugin not initialized - missing API key\");\n }\n return client;\n}\n\n// Create the MetadataProvider implementation\nconst provider: MetadataProvider = {\n async search(params) {\n return handleSearch(params, getClient());\n },\n\n async get(params) {\n return handleGet(params, getClient());\n },\n\n async match(params) {\n return handleMatch(params, getClient());\n },\n};\n\n// Start the plugin server\ncreateMetadataPlugin({\n manifest,\n provider,\n logLevel: \"info\",\n onInitialize(params: InitializeParams) {\n const apiKey = params.credentials?.api_key;\n if (!apiKey) {\n throw new ConfigError(\"api_key credential is required\");\n }\n\n // Get optional timeout from config (in seconds)\n const timeout = params.config?.timeout as number | undefined;\n\n client = new MangaBakaClient(apiKey, { timeout });\n logger.info(`MangaBaka client initialized (timeout: ${timeout ?? \"default\"}s)`);\n },\n});\n\nlogger.info(\"MangaBaka plugin started\");\n"],
4
+ "sourcesContent": [null, null, null, null, "/**\n * MangaBaka API client\n * API docs: https://mangabaka.org/api\n */\n\nimport {\n ApiError,\n AuthError,\n createLogger,\n NotFoundError,\n RateLimitError,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MbGetSeriesResponse, MbSearchResponse, MbSeries } from \"./types.js\";\n\nconst BASE_URL = \"https://api.mangabaka.dev\";\nconst DEFAULT_TIMEOUT_SECONDS = 60;\nconst logger = createLogger({ name: \"mangabaka-api\", level: \"debug\" });\n\nexport interface MangaBakaClientOptions {\n /** Request timeout in seconds (default: 15) */\n timeout?: number;\n}\n\nexport class MangaBakaClient {\n private readonly apiKey: string;\n private readonly timeoutMs: number;\n\n constructor(apiKey: string, options?: MangaBakaClientOptions) {\n if (!apiKey) {\n throw new AuthError(\"API key is required\");\n }\n this.apiKey = apiKey;\n this.timeoutMs = (options?.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000;\n logger.debug(`MangaBakaClient initialized with timeout: ${this.timeoutMs}ms`);\n }\n\n /**\n * Search for series by query\n */\n async search(\n query: string,\n page = 1,\n perPage = 20,\n ): Promise<{ data: MbSeries[]; total: number; page: number; totalPages: number }> {\n logger.debug(`Searching for: \"${query}\" (page ${page})`);\n\n const params = new URLSearchParams({\n q: query,\n page: String(page),\n limit: String(perPage),\n });\n\n const response = await this.request<MbSearchResponse>(`/v1/series/search?${params.toString()}`);\n\n return {\n data: response.data,\n total: response.pagination?.total ?? response.data.length,\n page: response.pagination?.page ?? page,\n totalPages: response.pagination?.total_pages ?? 1,\n };\n }\n\n /**\n * Get full series details by ID\n */\n async getSeries(id: number): Promise<MbSeries> {\n logger.debug(`Getting series: ${id}`);\n\n const response = await this.request<MbGetSeriesResponse>(`/v1/series/${id}`);\n\n return response.data;\n }\n\n /**\n * Make an authenticated request to the MangaBaka API\n */\n private async request<T>(path: string): Promise<T> {\n const url = `${BASE_URL}${path}`;\n const headers: Record<string, string> = {\n \"x-api-key\": this.apiKey,\n Accept: \"application/json\",\n };\n\n // Set up timeout using AbortController\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n logger.debug(`Request: ${path} (timeout: ${this.timeoutMs}ms)`);\n const response = await fetch(url, {\n method: \"GET\",\n headers,\n signal: controller.signal,\n });\n\n // Handle rate limiting\n if (response.status === 429) {\n const retryAfter = response.headers.get(\"Retry-After\");\n const seconds = retryAfter ? Number.parseInt(retryAfter, 10) : 60;\n throw new RateLimitError(seconds);\n }\n\n // Handle auth errors\n if (response.status === 401 || response.status === 403) {\n throw new AuthError(\"Invalid API key\");\n }\n\n // Handle not found\n if (response.status === 404) {\n throw new NotFoundError(`Resource not found: ${path}`);\n }\n\n // Handle other errors\n if (!response.ok) {\n const text = await response.text();\n logger.error(`API error: ${response.status}`, { body: text });\n throw new ApiError(`API error: ${response.status} ${response.statusText}`, response.status);\n }\n\n return response.json() as Promise<T>;\n } catch (error) {\n // Handle timeout (abort)\n if (error instanceof Error && error.name === \"AbortError\") {\n logger.error(`Request timed out after ${this.timeoutMs}ms: ${path}`);\n throw new ApiError(`Request timed out after ${this.timeoutMs / 1000}s`);\n }\n\n // Re-throw plugin errors\n if (\n error instanceof RateLimitError ||\n error instanceof AuthError ||\n error instanceof NotFoundError ||\n error instanceof ApiError\n ) {\n throw error;\n }\n\n // Wrap other errors\n const message = error instanceof Error ? error.message : \"Unknown error\";\n logger.error(\"Request failed\", error);\n throw new ApiError(`Request failed: ${message}`);\n } finally {\n clearTimeout(timeoutId);\n }\n }\n}\n", "/**\n * Mappers to convert MangaBaka API responses to Codex plugin protocol types\n */\n\nimport type {\n AlternateTitle,\n ExternalLink,\n ExternalRating,\n PluginSeriesMetadata,\n ReadingDirection,\n SearchResult,\n SeriesStatus,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MbContentRating, MbSeries, MbSeriesType, MbStatus } from \"./types.js\";\n\n/**\n * Strip HTML tags from text, converting <br> to newlines\n */\nfunction stripHtml(html: string | undefined | null): string | undefined {\n if (!html) return undefined;\n return html\n .replace(/<br\\s*\\/?>/gi, \"\\n\") // Convert <br> to newlines\n .replace(/<[^>]*>/g, \"\") // Strip remaining HTML tags\n .trim();\n}\n\n/**\n * Map MangaBaka status to protocol SeriesStatus\n * MangaBaka uses: cancelled, completed, hiatus, releasing, unknown, upcoming\n * Codex uses: ongoing, ended, hiatus, abandoned, unknown\n */\nfunction mapStatus(mbStatus: MbStatus): SeriesStatus {\n switch (mbStatus) {\n case \"completed\":\n return \"ended\";\n case \"releasing\":\n case \"upcoming\":\n return \"ongoing\";\n case \"hiatus\":\n return \"hiatus\";\n case \"cancelled\":\n return \"abandoned\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Format genre from snake_case to Title Case\n */\nfunction formatGenre(genre: string): string {\n return genre\n .split(\"_\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\n/**\n * Detect language code from country of origin\n */\nfunction detectLanguageFromCountry(country: string | null | undefined): string | undefined {\n if (!country) return undefined;\n\n const countryLower = country.toLowerCase();\n if (countryLower === \"jp\" || countryLower === \"japan\") return \"ja\";\n if (countryLower === \"kr\" || countryLower === \"korea\" || countryLower === \"south korea\")\n return \"ko\";\n if (countryLower === \"cn\" || countryLower === \"china\") return \"zh\";\n if (countryLower === \"tw\" || countryLower === \"taiwan\") return \"zh-TW\";\n\n return undefined;\n}\n\n/**\n * Map MangaBaka content rating to numeric age rating\n */\nfunction mapContentRating(rating: MbContentRating | null | undefined): number | undefined {\n if (!rating) return undefined;\n\n switch (rating) {\n case \"safe\":\n return 0; // All ages\n case \"suggestive\":\n return 13; // Teen\n case \"erotica\":\n return 16; // Mature\n case \"pornographic\":\n return 18; // Adults only\n default:\n return undefined;\n }\n}\n\n/**\n * Extract rating value from either a number or an object with bayesian/average\n */\nfunction extractRating(\n rating: number | { bayesian?: number | null; average?: number | null } | null | undefined,\n): number | undefined {\n if (rating == null) return undefined;\n if (typeof rating === \"number\") return rating;\n return rating.bayesian ?? rating.average ?? undefined;\n}\n\n/**\n * Infer reading direction from series type and country\n */\nfunction inferReadingDirection(\n seriesType: MbSeriesType,\n country: string | null | undefined,\n): ReadingDirection | undefined {\n // Manhwa (Korean) and Manhua (Chinese) are typically left-to-right\n if (seriesType === \"manhwa\" || seriesType === \"manhua\") {\n return \"ltr\";\n }\n\n // Manga (Japanese) is typically right-to-left\n if (seriesType === \"manga\") {\n return \"rtl\";\n }\n\n // OEL (Original English Language) is left-to-right\n if (seriesType === \"oel\") {\n return \"ltr\";\n }\n\n // Fall back to country-based detection\n if (country) {\n const countryLower = country.toLowerCase();\n if (countryLower === \"jp\" || countryLower === \"japan\") return \"rtl\";\n if (countryLower === \"kr\" || countryLower === \"korea\" || countryLower === \"south korea\")\n return \"ltr\";\n if (countryLower === \"cn\" || countryLower === \"china\") return \"ltr\";\n if (countryLower === \"tw\" || countryLower === \"taiwan\") return \"ltr\";\n }\n\n return undefined;\n}\n\n/**\n * Map a MangaBaka series to a protocol SearchResult\n */\nexport function mapSearchResult(series: MbSeries): SearchResult {\n // Get cover URL - prefer x250 for search results\n const coverUrl = series.cover?.x250?.x1 ?? series.cover?.raw?.url ?? undefined;\n\n // Build alternate titles array\n const alternateTitles: string[] = [];\n if (series.native_title && series.native_title !== series.title) {\n alternateTitles.push(series.native_title);\n }\n if (series.romanized_title && series.romanized_title !== series.title) {\n alternateTitles.push(series.romanized_title);\n }\n\n // Note: relevanceScore is omitted - the API already returns results in relevance order\n return {\n externalId: String(series.id),\n title: series.title,\n alternateTitles,\n year: series.year ?? undefined,\n coverUrl: coverUrl ?? undefined,\n preview: {\n status: mapStatus(series.status),\n genres: (series.genres ?? []).slice(0, 3).map(formatGenre),\n rating: extractRating(series.rating),\n description: stripHtml(series.description)?.slice(0, 200) ?? undefined,\n bookCount: series.final_volume ? Number.parseInt(series.final_volume, 10) : undefined,\n },\n };\n}\n\n/**\n * Map full series response to protocol PluginSeriesMetadata\n */\nexport function mapSeriesMetadata(series: MbSeries): PluginSeriesMetadata {\n // Build alternate titles array with language info\n const alternateTitles: AlternateTitle[] = [];\n\n // Add native title\n if (series.native_title && series.native_title !== series.title) {\n alternateTitles.push({\n title: series.native_title,\n language: detectLanguageFromCountry(series.country_of_origin),\n titleType: \"native\",\n });\n }\n\n // Add romanized title\n if (series.romanized_title && series.romanized_title !== series.title) {\n alternateTitles.push({\n title: series.romanized_title,\n language: \"en\",\n titleType: \"romaji\",\n });\n }\n\n // Add secondary titles from all languages\n if (series.secondary_titles) {\n for (const [langCode, titleList] of Object.entries(series.secondary_titles)) {\n if (titleList) {\n for (const titleEntry of titleList) {\n if (titleEntry.title !== series.title) {\n alternateTitles.push({\n title: titleEntry.title,\n language: langCode,\n });\n }\n }\n }\n }\n }\n\n // Extract authors and artists as string arrays\n const authors = series.authors ?? [];\n const artists = series.artists ?? [];\n\n // Format genres\n const genres = (series.genres ?? []).map(formatGenre);\n\n // Get cover URL - prefer raw for full metadata\n const coverUrl = series.cover?.raw?.url ?? series.cover?.x350?.x1 ?? undefined;\n\n // Build external links from sources\n // Always include MangaBaka link first\n const externalLinks: ExternalLink[] = [\n {\n url: `https://mangabaka.org/${series.id}`,\n label: \"MangaBaka\",\n linkType: \"provider\",\n },\n ];\n\n // Source configuration: display name, rating key, and URL pattern\n // URL pattern uses {id} as placeholder for the source ID\n const sourceConfig: Record<string, { label: string; ratingKey: string; urlPattern?: string }> = {\n anilist: {\n label: \"AniList\",\n ratingKey: \"anilist\",\n urlPattern: \"https://anilist.co/manga/{id}\",\n },\n my_anime_list: {\n label: \"MyAnimeList\",\n ratingKey: \"myanimelist\",\n urlPattern: \"https://myanimelist.net/manga/{id}\",\n },\n mangadex: {\n label: \"MangaDex\",\n ratingKey: \"mangadex\",\n urlPattern: \"https://mangadex.org/title/{id}\",\n },\n manga_updates: {\n label: \"MangaUpdates\",\n ratingKey: \"mangaupdates\",\n urlPattern: \"https://www.mangaupdates.com/series/{id}\",\n },\n kitsu: { label: \"Kitsu\", ratingKey: \"kitsu\", urlPattern: \"https://kitsu.app/manga/{id}\" },\n anime_planet: {\n label: \"Anime-Planet\",\n ratingKey: \"animeplanet\",\n urlPattern: \"https://www.anime-planet.com/manga/{id}\",\n },\n anime_news_network: { label: \"Anime News Network\", ratingKey: \"animenewsnetwork\" },\n shikimori: {\n label: \"Shikimori\",\n ratingKey: \"shikimori\",\n urlPattern: \"https://shikimori.one/mangas/{id}\",\n },\n };\n\n // Build external links and ratings from sources in a single pass\n const externalRatings: ExternalRating[] = [];\n\n if (series.source) {\n for (const [key, info] of Object.entries(series.source)) {\n if (!info) continue;\n\n const config = sourceConfig[key];\n // Use config if available, otherwise generate defaults from key\n const ratingKey = config?.ratingKey ?? key.replace(/_/g, \"\");\n\n // Add external link if source has an ID and URL pattern\n if (info.id != null && config?.urlPattern) {\n externalLinks.push({\n url: config.urlPattern.replace(\"{id}\", String(info.id)),\n label: config.label,\n linkType: \"provider\",\n });\n }\n\n // Add external rating if source has a normalized rating\n if (info.rating_normalized != null) {\n externalRatings.push({ score: info.rating_normalized, source: ratingKey });\n }\n }\n }\n\n // Get publisher name (pick first one if available)\n const publisher = series.publishers?.[0]?.name ?? undefined;\n\n return {\n externalId: String(series.id),\n externalUrl: `https://mangabaka.org/${series.id}`,\n title: series.title,\n alternateTitles,\n summary: stripHtml(series.description),\n status: mapStatus(series.status),\n year: series.year ?? undefined,\n // Extended metadata\n publisher,\n totalBookCount: series.final_volume ? Number.parseInt(series.final_volume, 10) : undefined,\n ageRating: mapContentRating(series.content_rating),\n readingDirection: inferReadingDirection(series.type, series.country_of_origin),\n // Taxonomy\n genres,\n tags: series.tags ?? [],\n authors,\n artists,\n coverUrl: coverUrl ?? undefined,\n rating: (() => {\n const r = extractRating(series.rating);\n return r != null ? { score: r, source: \"mangabaka\" } : undefined;\n })(),\n externalRatings: externalRatings.length > 0 ? externalRatings : undefined,\n externalLinks,\n };\n}\n", "import {\n type MetadataGetParams,\n NotFoundError,\n type PluginSeriesMetadata,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MangaBakaClient } from \"../api.js\";\nimport { mapSeriesMetadata } from \"../mappers.js\";\n\nexport async function handleGet(\n params: MetadataGetParams,\n client: MangaBakaClient,\n): Promise<PluginSeriesMetadata> {\n const seriesId = Number.parseInt(params.externalId, 10);\n\n if (Number.isNaN(seriesId)) {\n throw new NotFoundError(`Invalid external ID: ${params.externalId}`);\n }\n\n const response = await client.getSeries(seriesId);\n\n return mapSeriesMetadata(response);\n}\n", "import {\n createLogger,\n type MetadataMatchParams,\n type MetadataMatchResponse,\n type SearchResult,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MangaBakaClient } from \"../api.js\";\nimport { mapSearchResult } from \"../mappers.js\";\n\nconst logger = createLogger({ name: \"mangabaka-match\", level: \"info\" });\n\n/**\n * Calculate string similarity using word overlap\n * Returns a value between 0 and 1\n */\nfunction similarity(a: string, b: string): number {\n const aLower = a.toLowerCase().trim();\n const bLower = b.toLowerCase().trim();\n\n if (aLower === bLower) return 1.0;\n if (aLower.length === 0 || bLower.length === 0) return 0;\n\n // Check if one contains the other\n if (aLower.includes(bLower) || bLower.includes(aLower)) {\n return 0.8;\n }\n\n // Simple word overlap scoring\n const aWords = new Set(aLower.split(/\\s+/));\n const bWords = new Set(bLower.split(/\\s+/));\n const intersection = [...aWords].filter((w) => bWords.has(w));\n const union = new Set([...aWords, ...bWords]);\n\n return intersection.length / union.size;\n}\n\n/**\n * Score a search result against the match parameters\n * Returns a value between 0 and 1\n */\nfunction scoreResult(result: SearchResult, params: MetadataMatchParams): number {\n let score = 0;\n\n // Title similarity (up to 0.6)\n const titleScore = similarity(result.title, params.title);\n score += titleScore * 0.6;\n\n // Year match (up to 0.2)\n if (params.year && result.year) {\n if (result.year === params.year) {\n score += 0.2;\n } else if (Math.abs(result.year - params.year) <= 1) {\n score += 0.1;\n }\n }\n\n // Boost for exact title match (up to 0.2)\n if (result.title.toLowerCase() === params.title.toLowerCase()) {\n score += 0.2;\n }\n\n return Math.min(1.0, score);\n}\n\nexport async function handleMatch(\n params: MetadataMatchParams,\n client: MangaBakaClient,\n): Promise<MetadataMatchResponse> {\n logger.debug(`Matching: \"${params.title}\"`);\n\n // Search for the title\n const response = await client.search(params.title, 1, 10);\n\n if (response.data.length === 0) {\n return {\n match: null,\n confidence: 0,\n };\n }\n\n // Map and score results\n const scoredResults = response.data\n .map((series) => {\n const result = mapSearchResult(series);\n const score = scoreResult(result, params);\n return { result, score };\n })\n .sort((a, b) => b.score - a.score);\n\n const best = scoredResults[0];\n\n if (!best) {\n return {\n match: null,\n confidence: 0,\n };\n }\n\n // If confidence is low, include alternatives\n const alternatives =\n best.score < 0.8\n ? scoredResults.slice(1, 4).map((s) => ({\n ...s.result,\n relevanceScore: s.score,\n }))\n : undefined;\n\n return {\n match: {\n ...best.result,\n relevanceScore: best.score,\n },\n confidence: best.score,\n alternatives,\n };\n}\n", "import {\n createLogger,\n type MetadataSearchParams,\n type MetadataSearchResponse,\n} from \"@ashdev/codex-plugin-sdk\";\nimport type { MangaBakaClient } from \"../api.js\";\nimport { mapSearchResult } from \"../mappers.js\";\n\nconst logger = createLogger({ name: \"mangabaka-search\", level: \"debug\" });\n\nexport async function handleSearch(\n params: MetadataSearchParams,\n client: MangaBakaClient,\n): Promise<MetadataSearchResponse> {\n logger.debug(\"Search params received:\", params);\n\n const limit = params.limit ?? 20;\n\n // Parse cursor as page number (default to 1)\n const page = params.cursor ? Number.parseInt(params.cursor, 10) : 1;\n\n logger.debug(`Searching for: \"${params.query}\" (page ${page}, limit ${limit})`);\n\n const response = await client.search(params.query, page, limit);\n\n // Map results - API already returns them sorted by relevance\n const results = response.data.map(mapSearchResult);\n\n // Calculate next cursor (next page number) if there are more results\n const hasNextPage = response.page < response.totalPages;\n const nextCursor = hasNextPage ? String(response.page + 1) : undefined;\n\n return {\n results,\n nextCursor,\n };\n}\n", "{\n \"name\": \"@ashdev/codex-plugin-metadata-mangabaka\",\n \"version\": \"1.8.3\",\n \"description\": \"MangaBaka metadata provider plugin for Codex\",\n \"main\": \"dist/index.js\",\n \"bin\": \"dist/index.js\",\n \"type\": \"module\",\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AshDevFr/codex.git\",\n \"directory\": \"plugins/metadata-mangabaka\"\n },\n \"scripts\": {\n \"build\": \"esbuild src/index.ts --bundle --platform=node --target=node22 --format=esm --outfile=dist/index.js --sourcemap --banner:js='#!/usr/bin/env node'\",\n \"dev\": \"npm run build -- --watch\",\n \"clean\": \"rm -rf dist\",\n \"start\": \"node dist/index.js\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"npm run lint && npm run build\"\n },\n \"keywords\": [\n \"codex\",\n \"plugin\",\n \"mangabaka\",\n \"manga\",\n \"metadata\"\n ],\n \"author\": \"Codex\",\n \"license\": \"MIT\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"dependencies\": {\n \"@ashdev/codex-plugin-sdk\": \"^1.8.3\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.3.13\",\n \"@types/node\": \"^22.0.0\",\n \"esbuild\": \"^0.24.0\",\n \"typescript\": \"^5.7.0\",\n \"vitest\": \"^3.0.0\"\n }\n}\n", "import type { MetadataContentType, PluginManifest } from \"@ashdev/codex-plugin-sdk\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\nexport const manifest = {\n name: \"metadata-mangabaka\",\n displayName: \"MangaBaka Metadata\",\n version: packageJson.version,\n description: \"Fetch manga metadata from MangaBaka - aggregated data from multiple sources\",\n author: \"Codex\",\n homepage: \"https://mangabaka.org\",\n protocolVersion: \"1.0\",\n capabilities: {\n metadataProvider: [\"series\"] as MetadataContentType[],\n },\n requiredCredentials: [\n {\n key: \"api_key\",\n label: \"API Key\",\n description: \"Get your API key at https://mangabaka.org/settings/api (requires account)\",\n required: true,\n sensitive: true,\n type: \"password\",\n placeholder: \"mb-...\",\n },\n ],\n configSchema: {\n description: \"Optional configuration for the MangaBaka plugin\",\n fields: [\n {\n key: \"timeout\",\n label: \"Request Timeout\",\n description: \"HTTP request timeout in seconds for API calls to MangaBaka\",\n type: \"number\",\n required: false,\n default: 60,\n example: 30,\n },\n ],\n },\n} as const satisfies PluginManifest & {\n capabilities: { metadataProvider: MetadataContentType[] };\n};\n", "/**\n * MangaBaka Plugin - Fetch manga metadata from MangaBaka\n *\n * MangaBaka aggregates metadata from multiple sources (AniList, MAL, MangaDex, etc.)\n * and provides a unified API for manga/novel metadata.\n *\n * API docs: https://mangabaka.org/api\n *\n * Credentials are provided by Codex via the initialize message.\n * Required credential: api_key (get one at https://mangabaka.org/settings/api)\n */\n\nimport {\n ConfigError,\n createLogger,\n createMetadataPlugin,\n type InitializeParams,\n type MetadataProvider,\n} from \"@ashdev/codex-plugin-sdk\";\nimport { MangaBakaClient } from \"./api.js\";\nimport { handleGet } from \"./handlers/get.js\";\nimport { handleMatch } from \"./handlers/match.js\";\nimport { handleSearch } from \"./handlers/search.js\";\nimport { manifest } from \"./manifest.js\";\n\nconst logger = createLogger({ name: \"mangabaka\", level: \"info\" });\n\n// Client is initialized when we receive credentials from Codex\nlet client: MangaBakaClient | null = null;\n\nfunction getClient(): MangaBakaClient {\n if (!client) {\n throw new ConfigError(\"Plugin not initialized - missing API key\");\n }\n return client;\n}\n\n// Create the MetadataProvider implementation\nconst provider: MetadataProvider = {\n async search(params) {\n return handleSearch(params, getClient());\n },\n\n async get(params) {\n return handleGet(params, getClient());\n },\n\n async match(params) {\n return handleMatch(params, getClient());\n },\n};\n\n// Start the plugin server\ncreateMetadataPlugin({\n manifest,\n provider,\n logLevel: \"info\",\n onInitialize(params: InitializeParams) {\n const apiKey = params.credentials?.api_key;\n if (!apiKey) {\n throw new ConfigError(\"api_key credential is required\");\n }\n\n // Get optional timeout from config (in seconds)\n const timeout = params.config?.timeout as number | undefined;\n\n client = new MangaBakaClient(apiKey, { timeout });\n logger.info(`MangaBaka client initialized (timeout: ${timeout ?? \"default\"}s)`);\n },\n});\n\nlogger.info(\"MangaBaka plugin started\");\n"],
5
5
  "mappings": "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",
6
6
  "names": ["manifest", "provider", "logger", "manifest", "provider", "logger", "client", "logger", "client", "logger", "client", "logger"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ashdev/codex-plugin-metadata-mangabaka",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "MangaBaka metadata provider plugin for Codex",
5
5
  "main": "dist/index.js",
6
6
  "bin": "dist/index.js",
@@ -39,7 +39,7 @@
39
39
  "node": ">=22.0.0"
40
40
  },
41
41
  "dependencies": {
42
- "@ashdev/codex-plugin-sdk": "^1.8.2"
42
+ "@ashdev/codex-plugin-sdk": "^1.8.3"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@biomejs/biome": "^2.3.13",