@emdash-cms/admin 0.13.0 → 0.14.0
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/{LocaleDirectionProvider-rC0KuZjM.js → LocaleDirectionProvider-D32XwRVp.js} +17 -17
- package/dist/{LocaleDirectionProvider-rC0KuZjM.js.map → LocaleDirectionProvider-D32XwRVp.js.map} +1 -1
- package/dist/index.js +167 -16
- package/dist/index.js.map +1 -1
- package/dist/locales/ar/messages.mjs +1 -1
- package/dist/locales/de/messages.mjs +1 -1
- package/dist/locales/en/messages.mjs +1 -1
- package/dist/locales/es-419/messages.mjs +1 -1
- package/dist/locales/es-ES/messages.mjs +1 -1
- package/dist/locales/eu/messages.mjs +1 -1
- package/dist/locales/fa/messages.mjs +1 -1
- package/dist/locales/fr/messages.mjs +1 -1
- package/dist/locales/id/messages.mjs +1 -1
- package/dist/locales/index.js +1 -1
- package/dist/locales/ja/messages.mjs +1 -1
- package/dist/locales/ko/messages.mjs +1 -1
- package/dist/locales/pl/messages.mjs +1 -1
- package/dist/locales/pseudo/messages.mjs +1 -1
- package/dist/locales/pt-BR/messages.mjs +1 -1
- package/dist/locales/zh-CN/messages.mjs +1 -1
- package/dist/locales/zh-TW/messages.mjs +1 -1
- package/dist/messages-35lkat0c.js +6 -0
- package/dist/messages-35lkat0c.js.map +1 -0
- package/dist/messages-BSSaBU23.js +6 -0
- package/dist/messages-BSSaBU23.js.map +1 -0
- package/dist/messages-Boy6iMLn.js +6 -0
- package/dist/messages-Boy6iMLn.js.map +1 -0
- package/dist/messages-BwIV9n3i.js +6 -0
- package/dist/messages-BwIV9n3i.js.map +1 -0
- package/dist/messages-CGrAqIHP.js +6 -0
- package/dist/messages-CGrAqIHP.js.map +1 -0
- package/dist/messages-CgLmZ-Xe.js +6 -0
- package/dist/messages-CgLmZ-Xe.js.map +1 -0
- package/dist/messages-Ckz9OJjP.js +6 -0
- package/dist/messages-Ckz9OJjP.js.map +1 -0
- package/dist/messages-CrY9gw3p.js +6 -0
- package/dist/messages-CrY9gw3p.js.map +1 -0
- package/dist/messages-CtrOZaXa.js +6 -0
- package/dist/messages-CtrOZaXa.js.map +1 -0
- package/dist/messages-CxW3nwfb.js +6 -0
- package/dist/messages-CxW3nwfb.js.map +1 -0
- package/dist/messages-D7CyjcVC.js +6 -0
- package/dist/messages-D7CyjcVC.js.map +1 -0
- package/dist/messages-Dhy7Stmm.js +6 -0
- package/dist/messages-Dhy7Stmm.js.map +1 -0
- package/dist/messages-FyJi9vss.js +6 -0
- package/dist/messages-FyJi9vss.js.map +1 -0
- package/dist/messages-JMBMiRyI.js +6 -0
- package/dist/messages-JMBMiRyI.js.map +1 -0
- package/dist/messages-pPTbyAIO.js +6 -0
- package/dist/messages-pPTbyAIO.js.map +1 -0
- package/dist/messages-zl21KaEb.js +6 -0
- package/dist/messages-zl21KaEb.js.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +3 -3
- package/dist/messages-BB7Ta5GU.js +0 -6
- package/dist/messages-BB7Ta5GU.js.map +0 -1
- package/dist/messages-BMyaLK4h.js +0 -6
- package/dist/messages-BMyaLK4h.js.map +0 -1
- package/dist/messages-BRIi_T5q.js +0 -6
- package/dist/messages-BRIi_T5q.js.map +0 -1
- package/dist/messages-Be0FUVz_.js +0 -6
- package/dist/messages-Be0FUVz_.js.map +0 -1
- package/dist/messages-BvE8RBIJ.js +0 -6
- package/dist/messages-BvE8RBIJ.js.map +0 -1
- package/dist/messages-CBHhMxhB.js +0 -6
- package/dist/messages-CBHhMxhB.js.map +0 -1
- package/dist/messages-CQJpnG4Q.js +0 -6
- package/dist/messages-CQJpnG4Q.js.map +0 -1
- package/dist/messages-CZpDTTb_.js +0 -6
- package/dist/messages-CZpDTTb_.js.map +0 -1
- package/dist/messages-CemjGhIt.js +0 -6
- package/dist/messages-CemjGhIt.js.map +0 -1
- package/dist/messages-Czya1QvU.js +0 -6
- package/dist/messages-Czya1QvU.js.map +0 -1
- package/dist/messages-DR7ouL8H.js +0 -6
- package/dist/messages-DR7ouL8H.js.map +0 -1
- package/dist/messages-DyiZEfUj.js +0 -6
- package/dist/messages-DyiZEfUj.js.map +0 -1
- package/dist/messages-JMuy3Tpu.js +0 -6
- package/dist/messages-JMuy3Tpu.js.map +0 -1
- package/dist/messages-h9IORlKh.js +0 -6
- package/dist/messages-h9IORlKh.js.map +0 -1
- package/dist/messages-iu8SG58F.js +0 -6
- package/dist/messages-iu8SG58F.js.map +0 -1
- package/dist/messages-uD16X789.js +0 -6
- package/dist/messages-uD16X789.js.map +0 -1
|
@@ -193,22 +193,22 @@ function resolveLocale(request) {
|
|
|
193
193
|
//#endregion
|
|
194
194
|
//#region src/locales/loadMessages.ts
|
|
195
195
|
const LOCALE_LOADERS = /* @__PURE__ */ Object.assign({
|
|
196
|
-
"./ar/messages.mjs": () => import("./messages-
|
|
197
|
-
"./de/messages.mjs": () => import("./messages-
|
|
198
|
-
"./en/messages.mjs": () => import("./messages-
|
|
199
|
-
"./es-419/messages.mjs": () => import("./messages-
|
|
200
|
-
"./es-ES/messages.mjs": () => import("./messages-
|
|
201
|
-
"./eu/messages.mjs": () => import("./messages-
|
|
202
|
-
"./fa/messages.mjs": () => import("./messages-
|
|
203
|
-
"./fr/messages.mjs": () => import("./messages-
|
|
204
|
-
"./id/messages.mjs": () => import("./messages-
|
|
205
|
-
"./ja/messages.mjs": () => import("./messages-
|
|
206
|
-
"./ko/messages.mjs": () => import("./messages-
|
|
207
|
-
"./pl/messages.mjs": () => import("./messages-
|
|
208
|
-
"./pseudo/messages.mjs": () => import("./messages-
|
|
209
|
-
"./pt-BR/messages.mjs": () => import("./messages-
|
|
210
|
-
"./zh-CN/messages.mjs": () => import("./messages-
|
|
211
|
-
"./zh-TW/messages.mjs": () => import("./messages-
|
|
196
|
+
"./ar/messages.mjs": () => import("./messages-35lkat0c.js"),
|
|
197
|
+
"./de/messages.mjs": () => import("./messages-zl21KaEb.js"),
|
|
198
|
+
"./en/messages.mjs": () => import("./messages-FyJi9vss.js"),
|
|
199
|
+
"./es-419/messages.mjs": () => import("./messages-D7CyjcVC.js"),
|
|
200
|
+
"./es-ES/messages.mjs": () => import("./messages-pPTbyAIO.js"),
|
|
201
|
+
"./eu/messages.mjs": () => import("./messages-CtrOZaXa.js"),
|
|
202
|
+
"./fa/messages.mjs": () => import("./messages-Ckz9OJjP.js"),
|
|
203
|
+
"./fr/messages.mjs": () => import("./messages-Boy6iMLn.js"),
|
|
204
|
+
"./id/messages.mjs": () => import("./messages-CGrAqIHP.js"),
|
|
205
|
+
"./ja/messages.mjs": () => import("./messages-CrY9gw3p.js"),
|
|
206
|
+
"./ko/messages.mjs": () => import("./messages-CxW3nwfb.js"),
|
|
207
|
+
"./pl/messages.mjs": () => import("./messages-BSSaBU23.js"),
|
|
208
|
+
"./pseudo/messages.mjs": () => import("./messages-Dhy7Stmm.js"),
|
|
209
|
+
"./pt-BR/messages.mjs": () => import("./messages-JMBMiRyI.js"),
|
|
210
|
+
"./zh-CN/messages.mjs": () => import("./messages-CgLmZ-Xe.js"),
|
|
211
|
+
"./zh-TW/messages.mjs": () => import("./messages-BwIV9n3i.js")
|
|
212
212
|
});
|
|
213
213
|
async function loadMessages(locale) {
|
|
214
214
|
const key = `./${locale}/messages.mjs`;
|
|
@@ -275,4 +275,4 @@ function LocaleDirectionProvider({ children }) {
|
|
|
275
275
|
|
|
276
276
|
//#endregion
|
|
277
277
|
export { SUPPORTED_LOCALES as a, getLocaleLabel as c, DEFAULT_LOCALE as i, resolveLocale as l, useLocale as n, SUPPORTED_LOCALE_CODES as o, loadMessages as r, getLocaleDir as s, LocaleDirectionProvider as t };
|
|
278
|
-
//# sourceMappingURL=LocaleDirectionProvider-
|
|
278
|
+
//# sourceMappingURL=LocaleDirectionProvider-D32XwRVp.js.map
|
package/dist/{LocaleDirectionProvider-rC0KuZjM.js.map → LocaleDirectionProvider-D32XwRVp.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocaleDirectionProvider-rC0KuZjM.js","names":["React"],"sources":["../src/locales/locales.ts","../src/locales/config.ts","../src/locales/loadMessages.ts","../src/locales/useLocale.ts","../src/locales/LocaleDirectionProvider.tsx"],"sourcesContent":["/**\n * Canonical locale definitions -- the single source of truth.\n *\n * This file is intentionally free of Vite/Astro APIs (`import.meta.env` etc.)\n * so it can be imported from CLI tools (Lingui, Lunaria) running in plain Node.\n *\n * To add a new locale:\n * 1. Add an entry here (with `enabled: false`).\n * 2. Run `pnpm locale:extract` to generate the PO file.\n * 3. Translate the strings in the PO file.\n * 4. Set `enabled: true` once coverage is sufficient.\n *\n * Lingui and Lunaria use all locales (for extraction and tracking).\n * The admin runtime only exposes locales with `enabled: true`.\n */\n\nexport interface LocaleDefinition {\n\t/** BCP 47 locale code (e.g. \"en\", \"pt-BR\"). */\n\tcode: string;\n\t/** Human-readable label in the locale's own language. */\n\tlabel: string;\n\t/** Whether this locale is selectable in the admin UI. */\n\tenabled: boolean;\n\t/** Text direction for this locale. Defaults to \"ltr\" if not specified. */\n\tdir?: \"rtl\" | \"ltr\";\n}\n\n/**\n * All locales that have (or should have) a PO catalog.\n * First entry is the source/default locale.\n */\nexport const LOCALES: LocaleDefinition[] = [\n\t// Source locale first, then alphabetical by English name.\n\t{ code: \"en\", label: \"English\", enabled: true },\n\t{ code: \"ar\", label: \"العربية\", enabled: true, dir: \"rtl\" }, // Arabic\n\t{ code: \"eu\", label: \"Euskara\", enabled: true }, // Basque\n\t{ code: \"zh-CN\", label: \"简体中文\", enabled: true }, // Chinese (Simplified)\n\t{ code: \"zh-TW\", label: \"繁體中文\", enabled: true }, // Chinese (Traditional)\n\t{ code: \"fa\", label: \"فارسی\", enabled: true, dir: \"rtl\" }, // Farsi (also known as Persian)\n\t{ code: \"fr\", label: \"Français\", enabled: true }, // French\n\t{ code: \"de\", label: \"Deutsch\", enabled: true }, // German\n\t{ code: \"id\", label: \"Bahasa Indonesia\", enabled: true }, // Indonesian\n\t{ code: \"ja\", label: \"日本語\", enabled: true }, // Japanese\n\t{ code: \"ko\", label: \"한국어\", enabled: false }, // Korean\n\t{ code: \"pl\", label: \"Polski\", enabled: true }, // Polish\n\t{ code: \"pt-BR\", label: \"Português (Brasil)\", enabled: true }, // Portuguese (Brazil)\n\t{ code: \"es-419\", label: \"Español (Latinoamérica)\", enabled: true }, // Spanish (Latin America)\n\t{ code: \"es-ES\", label: \"Español (España)\", enabled: true }, // Spanish (Spain) - BCP 47\n\t// Pseudo-locale for i18n testing - never enabled in the admin UI by default.\n\t// Set EMDASH_PSEUDO_LOCALE=1 in .env to expose it in the locale switcher (dev only).\n\t{ code: \"pseudo\", label: \"Pseudo\", enabled: false },\n];\n\n/** The source locale (first entry). */\nexport const SOURCE_LOCALE = LOCALES[0]!;\n\n/** All locale codes (for Lingui extraction / Lunaria tracking). */\nexport const LOCALE_CODES = LOCALES.map((l) => l.code);\n\n/** Target locales -- everything except the source (for Lunaria). */\nexport const TARGET_LOCALES = LOCALES.slice(1);\n\n/** Locales enabled in the admin UI. */\nexport const ENABLED_LOCALES = LOCALES.filter((l) => l.enabled);\n","/**\n * Locale configuration for the admin UI runtime.\n *\n * Locale definitions are in `./locales.ts` -- the single source of truth\n * shared by this file, lingui.config.ts and lunaria.config.ts.\n */\n\nimport { ENABLED_LOCALES, LOCALES, SOURCE_LOCALE } from \"./locales.js\";\n\nexport type { LocaleDefinition as SupportedLocale } from \"./locales.js\";\n\nfunction isValidLocale(code: string): boolean {\n\ttry {\n\t\tconst locale = new Intl.Locale(code);\n\t\treturn locale.baseName !== \"\";\n\t} catch {\n\t\tif (import.meta.env.DEV) {\n\t\t\tthrow new Error(`Invalid locale code: \"${code}\"`);\n\t\t}\n\t\treturn false;\n\t}\n}\n\n// Injected by the EmDash Vite integration from process.env.EMDASH_PSEUDO_LOCALE.\n// Only true in dev when EMDASH_PSEUDO_LOCALE=1 is set.\ndeclare const __EMDASH_PSEUDO_LOCALE__: boolean;\n\n/**\n * The pseudo locale, injected into the supported list only when\n * EMDASH_PSEUDO_LOCALE=1 is set. Never available in production.\n */\nconst PSEUDO_LOCALE =\n\ttypeof __EMDASH_PSEUDO_LOCALE__ !== \"undefined\" && __EMDASH_PSEUDO_LOCALE__\n\t\t? LOCALES.find((l) => l.code === \"pseudo\")\n\t\t: undefined;\n\n/** Available locales at runtime, validated against BCP 47. */\nexport const SUPPORTED_LOCALES = [\n\t...ENABLED_LOCALES.filter((l) => isValidLocale(l.code)),\n\t...(PSEUDO_LOCALE ? [PSEUDO_LOCALE] : []),\n];\n\nexport const SUPPORTED_LOCALE_CODES = new Set(SUPPORTED_LOCALES.map((l) => l.code));\n\nexport const DEFAULT_LOCALE = SOURCE_LOCALE.code;\n\n/** Maps base language codes to supported locales (e.g. \"pt\" -> \"pt-BR\"). */\nconst BASE_LANGUAGE_MAP = new Map<string, string>();\n/** Maps script codes to supported locales (e.g. \"zh-Hant\" -> \"zh-TW\", \"zh-Hans\" -> \"zh-CN\"). */\nconst SCRIPT_LANGUAGE_MAP = new Map<string, string>();\n\nfor (const l of SUPPORTED_LOCALES) {\n\tconst base = l.code.split(\"-\")[0]!.toLowerCase();\n\t// First match wins -- if we have both \"pt\" and \"pt-BR\", exact wins via direct lookup.\n\tif (!BASE_LANGUAGE_MAP.has(base)) {\n\t\tBASE_LANGUAGE_MAP.set(base, l.code);\n\t}\n\n\tconst maximized = new Intl.Locale(l.code).maximize();\n\tif (maximized.script) {\n\t\tconst scriptKey = `${maximized.language}-${maximized.script}`.toLowerCase();\n\t\tif (!SCRIPT_LANGUAGE_MAP.has(scriptKey)) {\n\t\t\tSCRIPT_LANGUAGE_MAP.set(scriptKey, l.code);\n\t\t}\n\t}\n}\n\n/**\n * Find the best matching supported locale for a BCP 47 tag.\n * Canonicalizes via Intl.Locale so case differences (e.g. \"pt-br\" vs \"pt-BR\")\n * don't prevent matching. Supports script codes (zh-Hant -> zh-TW, zh-Hans -> zh-CN)\n * and falls back to base language (pt-PT -> pt-BR).\n */\nfunction matchLocale(tag: string): string | undefined {\n\tconst trimmed = tag.trim();\n\tif (!trimmed) return undefined;\n\tlet canonical: string;\n\ttry {\n\t\tcanonical = new Intl.Locale(trimmed).baseName;\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\t// Exact match (case-insensitive via Intl.Locale)\n\tif (SUPPORTED_LOCALE_CODES.has(canonical)) return canonical;\n\n\t// Try script-based matching (zh-Hant -> zh-TW, zh-Hans -> zh-CN)\n\tconst locale = new Intl.Locale(trimmed);\n\tif (locale.script) {\n\t\tconst scriptKey = `${locale.language}-${locale.script}`.toLowerCase();\n\t\tconst scriptMatch = SCRIPT_LANGUAGE_MAP.get(scriptKey);\n\t\tif (scriptMatch) return scriptMatch;\n\t}\n\n\t// Fallback to base language (pt-PT -> pt-BR)\n\tconst base = canonical.split(\"-\")[0]!.toLowerCase();\n\treturn BASE_LANGUAGE_MAP.get(base);\n}\n\nconst LOCALE_LABELS = new Map(SUPPORTED_LOCALES.map((l) => [l.code, l.label]));\n\n/** Get a display label for a locale code, falling back to uppercase code. */\nexport function getLocaleLabel(code: string): string {\n\treturn LOCALE_LABELS.get(code) ?? code.toUpperCase();\n}\n\nconst LOCALE_DIRS = new Map(SUPPORTED_LOCALES.map((l) => [l.code, l.dir]));\n\n/** Get the text direction for a locale code. Defaults to \"ltr\" if not specified. */\nexport function getLocaleDir(code: string): \"ltr\" | \"rtl\" {\n\treturn LOCALE_DIRS.get(code) ?? \"ltr\";\n}\n\nconst LOCALE_COOKIE_RE = /(?:^|;\\s*)emdash-locale=([^;]+)/;\n\n/**\n * Resolve the admin locale from a Request.\n * Priority: emdash-locale cookie -> Accept-Language -> DEFAULT_LOCALE.\n */\nexport function resolveLocale(request: Request): string {\n\tconst cookieHeader = request.headers.get(\"cookie\") ?? \"\";\n\tconst cookieMatch = cookieHeader.match(LOCALE_COOKIE_RE);\n\tconst cookieLocale = cookieMatch?.[1]?.trim() ?? \"\";\n\n\tif (SUPPORTED_LOCALE_CODES.has(cookieLocale)) return cookieLocale;\n\n\tconst acceptLang = request.headers.get(\"accept-language\") ?? \"\";\n\tfor (const entry of acceptLang.split(\",\")) {\n\t\tconst tag = entry.split(\";\")[0]!.trim();\n\t\tconst matched = matchLocale(tag);\n\t\tif (matched) return matched;\n\t}\n\n\treturn DEFAULT_LOCALE;\n}\n","import type { Messages } from \"@lingui/core\";\n\nimport { DEFAULT_LOCALE } from \"./config.js\";\n\nconst LOCALE_LOADERS = import.meta.glob<{ messages: Messages }>(\"./**/messages.mjs\");\n\nexport async function loadMessages(locale: string): Promise<Messages> {\n\tconst key = `./${locale}/messages.mjs`;\n\tconst fallbackKey = `./${DEFAULT_LOCALE}/messages.mjs`;\n\tconst loader = LOCALE_LOADERS[key] ?? LOCALE_LOADERS[fallbackKey];\n\tif (!loader) {\n\t\tthrow new Error(\n\t\t\t`No locale catalog found for \"${locale}\" or \"${DEFAULT_LOCALE}\". Run \\`pnpm locale:compile\\` to generate catalogs.`,\n\t\t);\n\t}\n\tconst { messages } = await loader();\n\treturn messages;\n}\n","import { useLingui } from \"@lingui/react\";\nimport * as React from \"react\";\n\nimport { SUPPORTED_LOCALE_CODES } from \"./config.js\";\nimport { loadMessages } from \"./loadMessages.js\";\n\nfunction setCookie(code: string) {\n\tconst secure = window.location.protocol === \"https:\" ? \"; Secure\" : \"\";\n\tdocument.cookie = `emdash-locale=${code}; Path=/_emdash; SameSite=Lax; Max-Age=31536000${secure}`;\n}\n\n/**\n * Get the current locale and a function to switch locales.\n * Loads the new catalog dynamically and sets a cookie for server-side persistence.\n */\nexport function useLocale() {\n\tconst { i18n } = useLingui();\n\tconst [locale, setLocaleState] = React.useState(i18n.locale);\n\n\tconst setLocale = React.useCallback(\n\t\t(code: string) => {\n\t\t\tif (code === i18n.locale || !SUPPORTED_LOCALE_CODES.has(code)) return;\n\t\t\tsetCookie(code);\n\t\t\tvoid loadMessages(code)\n\t\t\t\t.then((messages) => i18n.loadAndActivate({ locale: code, messages }))\n\t\t\t\t.catch(() => {\n\t\t\t\t\tsetCookie(i18n.locale);\n\t\t\t\t});\n\t\t},\n\t\t[i18n],\n\t);\n\n\t// Subscribe to i18n change events to trigger re-renders\n\tReact.useEffect(() => {\n\t\tconst unsubscribe = i18n.on(\"change\", () => {\n\t\t\tsetLocaleState(i18n.locale);\n\t\t});\n\t\treturn unsubscribe;\n\t}, [i18n]);\n\n\treturn { locale, setLocale };\n}\n","import { DirectionProvider } from \"@cloudflare/kumo/primitives\";\nimport * as React from \"react\";\n\nimport { getLocaleDir } from \"./config.js\";\nimport { useLocale } from \"./useLocale.js\";\n\ninterface LocaleDirectionProviderProps {\n\tchildren: React.ReactNode;\n}\n\n/**\n * Wraps the app with DirectionProvider and keeps it in sync with the current locale.\n * Automatically updates direction when locale changes via useLocale.\n */\nexport function LocaleDirectionProvider({ children }: LocaleDirectionProviderProps) {\n\tconst { locale } = useLocale();\n\tconst dir = React.useMemo(() => getLocaleDir(locale), [locale]);\n\n\t/** Sync the direction and lang attributes with the current locale. */\n\tReact.useEffect(() => {\n\t\tdocument.documentElement.setAttribute(\"lang\", locale);\n\t\tdocument.documentElement.setAttribute(\"dir\", dir);\n\t}, [dir, locale]);\n\n\treturn <DirectionProvider direction={dir}>{children}</DirectionProvider>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LocaleDirectionProvider-D32XwRVp.js","names":["React"],"sources":["../src/locales/locales.ts","../src/locales/config.ts","../src/locales/loadMessages.ts","../src/locales/useLocale.ts","../src/locales/LocaleDirectionProvider.tsx"],"sourcesContent":["/**\n * Canonical locale definitions -- the single source of truth.\n *\n * This file is intentionally free of Vite/Astro APIs (`import.meta.env` etc.)\n * so it can be imported from CLI tools (Lingui, Lunaria) running in plain Node.\n *\n * To add a new locale:\n * 1. Add an entry here (with `enabled: false`).\n * 2. Run `pnpm locale:extract` to generate the PO file.\n * 3. Translate the strings in the PO file.\n * 4. Set `enabled: true` once coverage is sufficient.\n *\n * Lingui and Lunaria use all locales (for extraction and tracking).\n * The admin runtime only exposes locales with `enabled: true`.\n */\n\nexport interface LocaleDefinition {\n\t/** BCP 47 locale code (e.g. \"en\", \"pt-BR\"). */\n\tcode: string;\n\t/** Human-readable label in the locale's own language. */\n\tlabel: string;\n\t/** Whether this locale is selectable in the admin UI. */\n\tenabled: boolean;\n\t/** Text direction for this locale. Defaults to \"ltr\" if not specified. */\n\tdir?: \"rtl\" | \"ltr\";\n}\n\n/**\n * All locales that have (or should have) a PO catalog.\n * First entry is the source/default locale.\n */\nexport const LOCALES: LocaleDefinition[] = [\n\t// Source locale first, then alphabetical by English name.\n\t{ code: \"en\", label: \"English\", enabled: true },\n\t{ code: \"ar\", label: \"العربية\", enabled: true, dir: \"rtl\" }, // Arabic\n\t{ code: \"eu\", label: \"Euskara\", enabled: true }, // Basque\n\t{ code: \"zh-CN\", label: \"简体中文\", enabled: true }, // Chinese (Simplified)\n\t{ code: \"zh-TW\", label: \"繁體中文\", enabled: true }, // Chinese (Traditional)\n\t{ code: \"fa\", label: \"فارسی\", enabled: true, dir: \"rtl\" }, // Farsi (also known as Persian)\n\t{ code: \"fr\", label: \"Français\", enabled: true }, // French\n\t{ code: \"de\", label: \"Deutsch\", enabled: true }, // German\n\t{ code: \"id\", label: \"Bahasa Indonesia\", enabled: true }, // Indonesian\n\t{ code: \"ja\", label: \"日本語\", enabled: true }, // Japanese\n\t{ code: \"ko\", label: \"한국어\", enabled: false }, // Korean\n\t{ code: \"pl\", label: \"Polski\", enabled: true }, // Polish\n\t{ code: \"pt-BR\", label: \"Português (Brasil)\", enabled: true }, // Portuguese (Brazil)\n\t{ code: \"es-419\", label: \"Español (Latinoamérica)\", enabled: true }, // Spanish (Latin America)\n\t{ code: \"es-ES\", label: \"Español (España)\", enabled: true }, // Spanish (Spain) - BCP 47\n\t// Pseudo-locale for i18n testing - never enabled in the admin UI by default.\n\t// Set EMDASH_PSEUDO_LOCALE=1 in .env to expose it in the locale switcher (dev only).\n\t{ code: \"pseudo\", label: \"Pseudo\", enabled: false },\n];\n\n/** The source locale (first entry). */\nexport const SOURCE_LOCALE = LOCALES[0]!;\n\n/** All locale codes (for Lingui extraction / Lunaria tracking). */\nexport const LOCALE_CODES = LOCALES.map((l) => l.code);\n\n/** Target locales -- everything except the source (for Lunaria). */\nexport const TARGET_LOCALES = LOCALES.slice(1);\n\n/** Locales enabled in the admin UI. */\nexport const ENABLED_LOCALES = LOCALES.filter((l) => l.enabled);\n","/**\n * Locale configuration for the admin UI runtime.\n *\n * Locale definitions are in `./locales.ts` -- the single source of truth\n * shared by this file, lingui.config.ts and lunaria.config.ts.\n */\n\nimport { ENABLED_LOCALES, LOCALES, SOURCE_LOCALE } from \"./locales.js\";\n\nexport type { LocaleDefinition as SupportedLocale } from \"./locales.js\";\n\nfunction isValidLocale(code: string): boolean {\n\ttry {\n\t\tconst locale = new Intl.Locale(code);\n\t\treturn locale.baseName !== \"\";\n\t} catch {\n\t\tif (import.meta.env.DEV) {\n\t\t\tthrow new Error(`Invalid locale code: \"${code}\"`);\n\t\t}\n\t\treturn false;\n\t}\n}\n\n// Injected by the EmDash Vite integration from process.env.EMDASH_PSEUDO_LOCALE.\n// Only true in dev when EMDASH_PSEUDO_LOCALE=1 is set.\ndeclare const __EMDASH_PSEUDO_LOCALE__: boolean;\n\n/**\n * The pseudo locale, injected into the supported list only when\n * EMDASH_PSEUDO_LOCALE=1 is set. Never available in production.\n */\nconst PSEUDO_LOCALE =\n\ttypeof __EMDASH_PSEUDO_LOCALE__ !== \"undefined\" && __EMDASH_PSEUDO_LOCALE__\n\t\t? LOCALES.find((l) => l.code === \"pseudo\")\n\t\t: undefined;\n\n/** Available locales at runtime, validated against BCP 47. */\nexport const SUPPORTED_LOCALES = [\n\t...ENABLED_LOCALES.filter((l) => isValidLocale(l.code)),\n\t...(PSEUDO_LOCALE ? [PSEUDO_LOCALE] : []),\n];\n\nexport const SUPPORTED_LOCALE_CODES = new Set(SUPPORTED_LOCALES.map((l) => l.code));\n\nexport const DEFAULT_LOCALE = SOURCE_LOCALE.code;\n\n/** Maps base language codes to supported locales (e.g. \"pt\" -> \"pt-BR\"). */\nconst BASE_LANGUAGE_MAP = new Map<string, string>();\n/** Maps script codes to supported locales (e.g. \"zh-Hant\" -> \"zh-TW\", \"zh-Hans\" -> \"zh-CN\"). */\nconst SCRIPT_LANGUAGE_MAP = new Map<string, string>();\n\nfor (const l of SUPPORTED_LOCALES) {\n\tconst base = l.code.split(\"-\")[0]!.toLowerCase();\n\t// First match wins -- if we have both \"pt\" and \"pt-BR\", exact wins via direct lookup.\n\tif (!BASE_LANGUAGE_MAP.has(base)) {\n\t\tBASE_LANGUAGE_MAP.set(base, l.code);\n\t}\n\n\tconst maximized = new Intl.Locale(l.code).maximize();\n\tif (maximized.script) {\n\t\tconst scriptKey = `${maximized.language}-${maximized.script}`.toLowerCase();\n\t\tif (!SCRIPT_LANGUAGE_MAP.has(scriptKey)) {\n\t\t\tSCRIPT_LANGUAGE_MAP.set(scriptKey, l.code);\n\t\t}\n\t}\n}\n\n/**\n * Find the best matching supported locale for a BCP 47 tag.\n * Canonicalizes via Intl.Locale so case differences (e.g. \"pt-br\" vs \"pt-BR\")\n * don't prevent matching. Supports script codes (zh-Hant -> zh-TW, zh-Hans -> zh-CN)\n * and falls back to base language (pt-PT -> pt-BR).\n */\nfunction matchLocale(tag: string): string | undefined {\n\tconst trimmed = tag.trim();\n\tif (!trimmed) return undefined;\n\tlet canonical: string;\n\ttry {\n\t\tcanonical = new Intl.Locale(trimmed).baseName;\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\t// Exact match (case-insensitive via Intl.Locale)\n\tif (SUPPORTED_LOCALE_CODES.has(canonical)) return canonical;\n\n\t// Try script-based matching (zh-Hant -> zh-TW, zh-Hans -> zh-CN)\n\tconst locale = new Intl.Locale(trimmed);\n\tif (locale.script) {\n\t\tconst scriptKey = `${locale.language}-${locale.script}`.toLowerCase();\n\t\tconst scriptMatch = SCRIPT_LANGUAGE_MAP.get(scriptKey);\n\t\tif (scriptMatch) return scriptMatch;\n\t}\n\n\t// Fallback to base language (pt-PT -> pt-BR)\n\tconst base = canonical.split(\"-\")[0]!.toLowerCase();\n\treturn BASE_LANGUAGE_MAP.get(base);\n}\n\nconst LOCALE_LABELS = new Map(SUPPORTED_LOCALES.map((l) => [l.code, l.label]));\n\n/** Get a display label for a locale code, falling back to uppercase code. */\nexport function getLocaleLabel(code: string): string {\n\treturn LOCALE_LABELS.get(code) ?? code.toUpperCase();\n}\n\nconst LOCALE_DIRS = new Map(SUPPORTED_LOCALES.map((l) => [l.code, l.dir]));\n\n/** Get the text direction for a locale code. Defaults to \"ltr\" if not specified. */\nexport function getLocaleDir(code: string): \"ltr\" | \"rtl\" {\n\treturn LOCALE_DIRS.get(code) ?? \"ltr\";\n}\n\nconst LOCALE_COOKIE_RE = /(?:^|;\\s*)emdash-locale=([^;]+)/;\n\n/**\n * Resolve the admin locale from a Request.\n * Priority: emdash-locale cookie -> Accept-Language -> DEFAULT_LOCALE.\n */\nexport function resolveLocale(request: Request): string {\n\tconst cookieHeader = request.headers.get(\"cookie\") ?? \"\";\n\tconst cookieMatch = cookieHeader.match(LOCALE_COOKIE_RE);\n\tconst cookieLocale = cookieMatch?.[1]?.trim() ?? \"\";\n\n\tif (SUPPORTED_LOCALE_CODES.has(cookieLocale)) return cookieLocale;\n\n\tconst acceptLang = request.headers.get(\"accept-language\") ?? \"\";\n\tfor (const entry of acceptLang.split(\",\")) {\n\t\tconst tag = entry.split(\";\")[0]!.trim();\n\t\tconst matched = matchLocale(tag);\n\t\tif (matched) return matched;\n\t}\n\n\treturn DEFAULT_LOCALE;\n}\n","import type { Messages } from \"@lingui/core\";\n\nimport { DEFAULT_LOCALE } from \"./config.js\";\n\nconst LOCALE_LOADERS = import.meta.glob<{ messages: Messages }>(\"./**/messages.mjs\");\n\nexport async function loadMessages(locale: string): Promise<Messages> {\n\tconst key = `./${locale}/messages.mjs`;\n\tconst fallbackKey = `./${DEFAULT_LOCALE}/messages.mjs`;\n\tconst loader = LOCALE_LOADERS[key] ?? LOCALE_LOADERS[fallbackKey];\n\tif (!loader) {\n\t\tthrow new Error(\n\t\t\t`No locale catalog found for \"${locale}\" or \"${DEFAULT_LOCALE}\". Run \\`pnpm locale:compile\\` to generate catalogs.`,\n\t\t);\n\t}\n\tconst { messages } = await loader();\n\treturn messages;\n}\n","import { useLingui } from \"@lingui/react\";\nimport * as React from \"react\";\n\nimport { SUPPORTED_LOCALE_CODES } from \"./config.js\";\nimport { loadMessages } from \"./loadMessages.js\";\n\nfunction setCookie(code: string) {\n\tconst secure = window.location.protocol === \"https:\" ? \"; Secure\" : \"\";\n\tdocument.cookie = `emdash-locale=${code}; Path=/_emdash; SameSite=Lax; Max-Age=31536000${secure}`;\n}\n\n/**\n * Get the current locale and a function to switch locales.\n * Loads the new catalog dynamically and sets a cookie for server-side persistence.\n */\nexport function useLocale() {\n\tconst { i18n } = useLingui();\n\tconst [locale, setLocaleState] = React.useState(i18n.locale);\n\n\tconst setLocale = React.useCallback(\n\t\t(code: string) => {\n\t\t\tif (code === i18n.locale || !SUPPORTED_LOCALE_CODES.has(code)) return;\n\t\t\tsetCookie(code);\n\t\t\tvoid loadMessages(code)\n\t\t\t\t.then((messages) => i18n.loadAndActivate({ locale: code, messages }))\n\t\t\t\t.catch(() => {\n\t\t\t\t\tsetCookie(i18n.locale);\n\t\t\t\t});\n\t\t},\n\t\t[i18n],\n\t);\n\n\t// Subscribe to i18n change events to trigger re-renders\n\tReact.useEffect(() => {\n\t\tconst unsubscribe = i18n.on(\"change\", () => {\n\t\t\tsetLocaleState(i18n.locale);\n\t\t});\n\t\treturn unsubscribe;\n\t}, [i18n]);\n\n\treturn { locale, setLocale };\n}\n","import { DirectionProvider } from \"@cloudflare/kumo/primitives\";\nimport * as React from \"react\";\n\nimport { getLocaleDir } from \"./config.js\";\nimport { useLocale } from \"./useLocale.js\";\n\ninterface LocaleDirectionProviderProps {\n\tchildren: React.ReactNode;\n}\n\n/**\n * Wraps the app with DirectionProvider and keeps it in sync with the current locale.\n * Automatically updates direction when locale changes via useLocale.\n */\nexport function LocaleDirectionProvider({ children }: LocaleDirectionProviderProps) {\n\tconst { locale } = useLocale();\n\tconst dir = React.useMemo(() => getLocaleDir(locale), [locale]);\n\n\t/** Sync the direction and lang attributes with the current locale. */\n\tReact.useEffect(() => {\n\t\tdocument.documentElement.setAttribute(\"lang\", locale);\n\t\tdocument.documentElement.setAttribute(\"dir\", dir);\n\t}, [dir, locale]);\n\n\treturn <DirectionProvider direction={dir}>{children}</DirectionProvider>;\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as fetchAuthMode, d as throwResponseError, i as fetchPlugins, l as fetchManifest, n as enablePlugin, o as API_BASE, r as fetchPlugin, s as apiFetch, t as disablePlugin, u as parseApiResponse } from "./plugins-qrjvPK4g.js";
|
|
2
|
-
import { a as SUPPORTED_LOCALES, i as DEFAULT_LOCALE, l as resolveLocale, n as useLocale, o as SUPPORTED_LOCALE_CODES, t as LocaleDirectionProvider } from "./LocaleDirectionProvider-
|
|
2
|
+
import { a as SUPPORTED_LOCALES, i as DEFAULT_LOCALE, l as resolveLocale, n as useLocale, o as SUPPORTED_LOCALE_CODES, t as LocaleDirectionProvider } from "./LocaleDirectionProvider-D32XwRVp.js";
|
|
3
3
|
import "./locales/index.js";
|
|
4
4
|
import { Badge, Button, Checkbox, CommandPalette, Dialog, Input, InputArea, Label, LinkButton, Loader, Popover, Select, Sidebar as KumoSidebar, Switch, Tabs, Toast, Toasty, Tooltip, buttonVariants, useSidebar } from "@cloudflare/kumo";
|
|
5
5
|
import { i18n } from "@lingui/core";
|
|
@@ -21963,32 +21963,32 @@ async function getDiscoveryClient(config) {
|
|
|
21963
21963
|
});
|
|
21964
21964
|
const wrapped = {
|
|
21965
21965
|
async searchPackages(opts) {
|
|
21966
|
-
return
|
|
21966
|
+
return discovery.searchPackages({
|
|
21967
21967
|
q: opts.q,
|
|
21968
21968
|
cursor: opts.cursor,
|
|
21969
21969
|
limit: opts.limit
|
|
21970
21970
|
});
|
|
21971
21971
|
},
|
|
21972
21972
|
async resolvePackage(handle, slug) {
|
|
21973
|
-
return
|
|
21973
|
+
return discovery.resolvePackage({
|
|
21974
21974
|
handle,
|
|
21975
21975
|
slug
|
|
21976
21976
|
});
|
|
21977
21977
|
},
|
|
21978
21978
|
async getPackage(did, slug) {
|
|
21979
|
-
return
|
|
21979
|
+
return discovery.getPackage({
|
|
21980
21980
|
did,
|
|
21981
21981
|
slug
|
|
21982
21982
|
});
|
|
21983
21983
|
},
|
|
21984
21984
|
async getLatestRelease(did, slug) {
|
|
21985
|
-
return
|
|
21985
|
+
return discovery.getLatestRelease({
|
|
21986
21986
|
did,
|
|
21987
21987
|
package: slug
|
|
21988
21988
|
});
|
|
21989
21989
|
},
|
|
21990
21990
|
async listReleases(did, slug, cursor) {
|
|
21991
|
-
return
|
|
21991
|
+
return discovery.listReleases({
|
|
21992
21992
|
did,
|
|
21993
21993
|
package: slug,
|
|
21994
21994
|
cursor
|
|
@@ -22392,7 +22392,9 @@ function RegistryBrowse({ config, installedRegistryUris = /* @__PURE__ */ new Se
|
|
|
22392
22392
|
function RegistryPackageCard({ pkg, installed }) {
|
|
22393
22393
|
const { _: _t2 } = useLingui();
|
|
22394
22394
|
const linkSegment = usePublisherHandle(pkg.did, pkg.handle).handle ?? pkg.did;
|
|
22395
|
-
const
|
|
22395
|
+
const name = pkg.profile?.name;
|
|
22396
|
+
const description = pkg.profile?.description;
|
|
22397
|
+
const license = pkg.profile?.license;
|
|
22396
22398
|
const verified = (pkg.labels ?? []).some((l) => l.val === "verified");
|
|
22397
22399
|
return /* @__PURE__ */ jsx(Link$1, {
|
|
22398
22400
|
to: "/plugins/marketplace/$pluginId",
|
|
@@ -22410,7 +22412,7 @@ function RegistryPackageCard({ pkg, installed }) {
|
|
|
22410
22412
|
className: "flex items-center gap-2",
|
|
22411
22413
|
children: [/* @__PURE__ */ jsx("h2", {
|
|
22412
22414
|
className: "truncate font-semibold",
|
|
22413
|
-
children:
|
|
22415
|
+
children: name ?? pkg.slug
|
|
22414
22416
|
}), verified ? /* @__PURE__ */ jsx(ShieldCheck, {
|
|
22415
22417
|
className: "h-4 w-4 shrink-0 text-kumo-brand",
|
|
22416
22418
|
"aria-label": _t2({
|
|
@@ -22424,9 +22426,13 @@ function RegistryPackageCard({ pkg, installed }) {
|
|
|
22424
22426
|
aggregatorHandle: pkg.handle,
|
|
22425
22427
|
variant: "card"
|
|
22426
22428
|
}),
|
|
22427
|
-
|
|
22429
|
+
description ? /* @__PURE__ */ jsx("p", {
|
|
22428
22430
|
className: "mt-2 line-clamp-2 text-sm text-kumo-default",
|
|
22429
|
-
children:
|
|
22431
|
+
children: description
|
|
22432
|
+
}) : null,
|
|
22433
|
+
license ? /* @__PURE__ */ jsx("p", {
|
|
22434
|
+
className: "mt-2 text-xs text-kumo-subtle",
|
|
22435
|
+
children: license
|
|
22430
22436
|
}) : null,
|
|
22431
22437
|
installed ? /* @__PURE__ */ jsx("div", {
|
|
22432
22438
|
className: "mt-3",
|
|
@@ -22499,9 +22505,27 @@ function RegistryPluginDetail({ pluginId, config }) {
|
|
|
22499
22505
|
queryFn: () => getLatestRegistryRelease(config, pkg.did, slug),
|
|
22500
22506
|
enabled: Boolean(pkg?.did && slug)
|
|
22501
22507
|
});
|
|
22502
|
-
const ext = (release?.release
|
|
22508
|
+
const ext = (release?.release?.extensions)?.["com.emdashcms.experimental.package.releaseExtension"];
|
|
22503
22509
|
const capabilities = Array.isArray(ext?.capabilities) ? canonicalCapabilitiesForDriftCheck(ext?.capabilities) : canonicalCapabilitiesForDriftCheck(declaredAccessToCapabilityList(ext?.declaredAccess));
|
|
22504
|
-
const
|
|
22510
|
+
const pkgProfile = pkg?.profile ?? null;
|
|
22511
|
+
const displayName = pkgProfile?.name;
|
|
22512
|
+
const description = pkgProfile?.description;
|
|
22513
|
+
const licenseText = pkgProfile?.license;
|
|
22514
|
+
const keywordList = pkgProfile?.keywords ?? [];
|
|
22515
|
+
const authorList = (pkgProfile?.authors ?? []).map((a) => ({
|
|
22516
|
+
name: a.name,
|
|
22517
|
+
url: safeExternalHref(a.url),
|
|
22518
|
+
email: safeEmail(a.email)
|
|
22519
|
+
}));
|
|
22520
|
+
const securityList = (pkgProfile?.security ?? []).flatMap((c) => {
|
|
22521
|
+
const url = safeExternalHref(c.url);
|
|
22522
|
+
const email = safeEmail(c.email);
|
|
22523
|
+
return url || email ? [{
|
|
22524
|
+
url,
|
|
22525
|
+
email
|
|
22526
|
+
}] : [];
|
|
22527
|
+
});
|
|
22528
|
+
const repoHref = safeExternalHref(release?.release?.repo);
|
|
22505
22529
|
const verified = (pkg?.labels ?? []).some((l) => l.val === "verified");
|
|
22506
22530
|
const policyOk = release && pkg ? releasePassesPolicy(release, {
|
|
22507
22531
|
did: pkg.did,
|
|
@@ -22582,7 +22606,7 @@ function RegistryPluginDetail({ pluginId, config }) {
|
|
|
22582
22606
|
className: "flex items-center gap-2",
|
|
22583
22607
|
children: [/* @__PURE__ */ jsx("h1", {
|
|
22584
22608
|
className: "truncate text-3xl font-bold",
|
|
22585
|
-
children:
|
|
22609
|
+
children: displayName ?? slug
|
|
22586
22610
|
}), verified ? /* @__PURE__ */ jsx(ShieldCheck, {
|
|
22587
22611
|
className: "h-5 w-5 shrink-0 text-kumo-brand",
|
|
22588
22612
|
"aria-label": _t({
|
|
@@ -22677,10 +22701,82 @@ function RegistryPluginDetail({ pluginId, config }) {
|
|
|
22677
22701
|
})
|
|
22678
22702
|
})] })]
|
|
22679
22703
|
}) : null,
|
|
22680
|
-
|
|
22704
|
+
description ? /* @__PURE__ */ jsx("p", {
|
|
22681
22705
|
className: "text-base text-kumo-default",
|
|
22682
|
-
children:
|
|
22706
|
+
children: description
|
|
22707
|
+
}) : null,
|
|
22708
|
+
licenseText || repoHref || keywordList.length > 0 ? /* @__PURE__ */ jsxs("section", {
|
|
22709
|
+
className: "flex flex-wrap items-center gap-2",
|
|
22710
|
+
children: [
|
|
22711
|
+
licenseText ? /* @__PURE__ */ jsx(LicenseBadge, { license: licenseText }) : null,
|
|
22712
|
+
keywordList.map((k) => /* @__PURE__ */ jsx(Badge, { children: k }, k)),
|
|
22713
|
+
repoHref ? /* @__PURE__ */ jsx(LinkButton, {
|
|
22714
|
+
href: repoHref,
|
|
22715
|
+
external: true,
|
|
22716
|
+
variant: "secondary",
|
|
22717
|
+
children: _t({
|
|
22718
|
+
id: "n/I+da",
|
|
22719
|
+
message: "View source"
|
|
22720
|
+
})
|
|
22721
|
+
}) : null
|
|
22722
|
+
]
|
|
22683
22723
|
}) : null,
|
|
22724
|
+
authorList.length > 0 ? /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
|
|
22725
|
+
className: "text-sm font-semibold text-kumo-subtle",
|
|
22726
|
+
children: _t({
|
|
22727
|
+
id: "dpqPBg",
|
|
22728
|
+
message: "Authors"
|
|
22729
|
+
})
|
|
22730
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
22731
|
+
className: "mt-2 space-y-1",
|
|
22732
|
+
children: authorList.map((a, i) => /* @__PURE__ */ jsxs("li", {
|
|
22733
|
+
className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-sm",
|
|
22734
|
+
children: [
|
|
22735
|
+
/* @__PURE__ */ jsx("span", {
|
|
22736
|
+
className: "font-medium text-kumo-default",
|
|
22737
|
+
children: a.name
|
|
22738
|
+
}),
|
|
22739
|
+
a.url ? /* @__PURE__ */ jsx("a", {
|
|
22740
|
+
href: a.url,
|
|
22741
|
+
target: "_blank",
|
|
22742
|
+
rel: "noreferrer",
|
|
22743
|
+
className: "text-kumo-brand hover:underline",
|
|
22744
|
+
children: _t({
|
|
22745
|
+
id: "On0aF2",
|
|
22746
|
+
message: "Website"
|
|
22747
|
+
})
|
|
22748
|
+
}) : null,
|
|
22749
|
+
a.email ? /* @__PURE__ */ jsx("a", {
|
|
22750
|
+
href: `mailto:${a.email}`,
|
|
22751
|
+
className: "text-kumo-brand hover:underline",
|
|
22752
|
+
children: a.email
|
|
22753
|
+
}) : null
|
|
22754
|
+
]
|
|
22755
|
+
}, `${a.name}-${i}`))
|
|
22756
|
+
})] }) : null,
|
|
22757
|
+
securityList.length > 0 ? /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
|
|
22758
|
+
className: "text-sm font-semibold text-kumo-subtle",
|
|
22759
|
+
children: _t({
|
|
22760
|
+
id: "FDZXmz",
|
|
22761
|
+
message: "Security contacts"
|
|
22762
|
+
})
|
|
22763
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
22764
|
+
className: "mt-2 space-y-1",
|
|
22765
|
+
children: securityList.map((c, i) => /* @__PURE__ */ jsxs("li", {
|
|
22766
|
+
className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-sm",
|
|
22767
|
+
children: [c.email ? /* @__PURE__ */ jsx("a", {
|
|
22768
|
+
href: `mailto:${c.email}`,
|
|
22769
|
+
className: "text-kumo-brand hover:underline",
|
|
22770
|
+
children: c.email
|
|
22771
|
+
}) : null, c.url ? /* @__PURE__ */ jsx("a", {
|
|
22772
|
+
href: c.url,
|
|
22773
|
+
target: "_blank",
|
|
22774
|
+
rel: "noreferrer",
|
|
22775
|
+
className: "text-kumo-brand hover:underline",
|
|
22776
|
+
children: c.url
|
|
22777
|
+
}) : null]
|
|
22778
|
+
}, `${c.email ?? c.url ?? "contact"}-${i}`))
|
|
22779
|
+
})] }) : null,
|
|
22684
22780
|
capabilities.length > 0 ? /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h2", {
|
|
22685
22781
|
className: "text-sm font-semibold text-kumo-subtle",
|
|
22686
22782
|
children: _t({
|
|
@@ -22693,7 +22789,7 @@ function RegistryPluginDetail({ pluginId, config }) {
|
|
|
22693
22789
|
})] }) : null,
|
|
22694
22790
|
showConsent && release ? /* @__PURE__ */ jsx(CapabilityConsentDialog, {
|
|
22695
22791
|
mode: "install",
|
|
22696
|
-
pluginName:
|
|
22792
|
+
pluginName: displayName ?? slug,
|
|
22697
22793
|
capabilities,
|
|
22698
22794
|
isPending: installMutation.isPending,
|
|
22699
22795
|
error: getMutationError(installMutation.error),
|
|
@@ -22744,6 +22840,61 @@ function formatDate(iso) {
|
|
|
22744
22840
|
return iso;
|
|
22745
22841
|
}
|
|
22746
22842
|
}
|
|
22843
|
+
/**
|
|
22844
|
+
* SPDX page URL for a license, or `null` when the value isn't a single
|
|
22845
|
+
* SPDX identifier (compound expressions like "MIT OR Apache-2.0" and the
|
|
22846
|
+
* literal "proprietary" have no canonical spdx.org page).
|
|
22847
|
+
*/
|
|
22848
|
+
/**
|
|
22849
|
+
* Validate an untrusted aggregator-supplied URL for use in an `href`.
|
|
22850
|
+
* Returns the normalised URL only when it is an absolute `http(s)` URL;
|
|
22851
|
+
* everything else (relative, `javascript:`, `data:`, garbage, non-string)
|
|
22852
|
+
* returns `null`. The profile/release records are pass-throughs from a
|
|
22853
|
+
* remote service, so an unsanitised `href` is stored XSS in the
|
|
22854
|
+
* authenticated admin origin.
|
|
22855
|
+
*/
|
|
22856
|
+
function safeExternalHref(value) {
|
|
22857
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
22858
|
+
let parsed;
|
|
22859
|
+
try {
|
|
22860
|
+
parsed = new URL(value);
|
|
22861
|
+
} catch {
|
|
22862
|
+
return null;
|
|
22863
|
+
}
|
|
22864
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
22865
|
+
return parsed.href;
|
|
22866
|
+
}
|
|
22867
|
+
const EMAIL_RE = /^[^\s@<>()[\]\\,;:"?&=%/]+@[^\s@<>()[\]\\,;:"?&=%/]+\.[^\s@<>()[\]\\,;:"?&=%/]+$/;
|
|
22868
|
+
function safeEmail(value) {
|
|
22869
|
+
if (typeof value !== "string") return null;
|
|
22870
|
+
const email = value.trim();
|
|
22871
|
+
if (email.length === 0 || email.length > 320) return null;
|
|
22872
|
+
return EMAIL_RE.test(email) ? email : null;
|
|
22873
|
+
}
|
|
22874
|
+
const SPDX_SINGLE_ID_RE = /^[A-Za-z0-9.+-]+$/;
|
|
22875
|
+
function spdxLicenseHref(license) {
|
|
22876
|
+
const id = license.trim();
|
|
22877
|
+
if (!SPDX_SINGLE_ID_RE.test(id)) return null;
|
|
22878
|
+
if (id.toLowerCase() === "proprietary") return null;
|
|
22879
|
+
return `https://spdx.org/licenses/${id}.html`;
|
|
22880
|
+
}
|
|
22881
|
+
function LicenseBadge({ license }) {
|
|
22882
|
+
const { _: _t3 } = useLingui();
|
|
22883
|
+
const href = spdxLicenseHref(license);
|
|
22884
|
+
if (!href) return /* @__PURE__ */ jsx(Badge, { children: license });
|
|
22885
|
+
return /* @__PURE__ */ jsx("a", {
|
|
22886
|
+
href,
|
|
22887
|
+
target: "_blank",
|
|
22888
|
+
rel: "noreferrer",
|
|
22889
|
+
"aria-label": _t3({
|
|
22890
|
+
id: "4Q1BOX",
|
|
22891
|
+
message: "View the {license} license on spdx.org",
|
|
22892
|
+
values: { license }
|
|
22893
|
+
}),
|
|
22894
|
+
className: "hover:opacity-80",
|
|
22895
|
+
children: /* @__PURE__ */ jsx(Badge, { children: license })
|
|
22896
|
+
});
|
|
22897
|
+
}
|
|
22747
22898
|
function formatHoldback(seconds) {
|
|
22748
22899
|
if (seconds <= 0) return "0s";
|
|
22749
22900
|
if (seconds < 3600) return `${Math.round(seconds / 60)} min`;
|