@rebasepro/studio 0.17.2 → 0.17.3-canary.gdd23447
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/ApiKeysView-DiCurTEU.js +1224 -0
- package/dist/ApiKeysView-DiCurTEU.js.map +1 -0
- package/dist/components/ApiKeys/CreateApiKeyDialog.d.ts +6 -0
- package/dist/components/ApiKeys/permissions.d.ts +66 -0
- package/dist/index.es.js +1 -1
- package/package.json +9 -9
- package/src/components/ApiKeys/ApiKeysView.tsx +69 -211
- package/src/components/ApiKeys/CreateApiKeyDialog.tsx +711 -0
- package/src/components/ApiKeys/permissions.ts +145 -0
- package/dist/ApiKeysView-C_UoUPuR.js +0 -728
- package/dist/ApiKeysView-C_UoUPuR.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiKeysView-DiCurTEU.js","names":[],"sources":["../src/components/ApiKeys/permissions.ts","../src/components/ApiKeys/CreateApiKeyDialog.tsx","../src/components/ApiKeys/ApiKeysView.tsx"],"sourcesContent":["/**\n * The vocabulary of an API key's permission entries.\n *\n * A permission entry is `{ collection, operations }`, but `collection` is not\n * only a collection: the same field addresses three namespaces, and the guard\n * that enforces them lives in `@rebasepro/server`\n * (`auth/api-keys/api-key-permission-guard.ts`):\n *\n * - `\"*\"` — every collection, every custom function, and storage\n * - `\"storage\"` — the storage routes\n * - `\"functions\"` — every custom function, including the function index\n * - `\"functions/<name>\"`— one named function\n * - anything else — the collection with that slug\n *\n * The UI used to label the field \"Collection slug or *\", which made two of\n * those namespaces undiscoverable and made `\"*\"` read as \"all collections\"\n * when it also hands over storage and every function. This module is the one\n * place that knows the mapping, so the picker, the row description, the grant\n * summary and the detail panel cannot drift from each other.\n *\n * @module\n */\n\nimport type { ApiKeyPermission } from \"@rebasepro/types\";\n\n/** Grants every collection, every function, and storage. */\nexport const RESOURCE_EVERYTHING = \"*\";\n/** Grants the storage routes. */\nexport const RESOURCE_STORAGE = \"storage\";\n/** Grants every custom function. */\nexport const RESOURCE_ALL_FUNCTIONS = \"functions\";\n/** Prefix addressing a single custom function. */\nexport const FUNCTION_PREFIX = \"functions/\";\n\nexport type ResourceKind =\n | \"everything\"\n | \"storage\"\n | \"all-functions\"\n | \"function\"\n | \"collection\";\n\nexport interface ParsedResource {\n kind: ResourceKind;\n /** The function name for `\"function\"`, the slug for `\"collection\"`. */\n name: string;\n}\n\n/** Classify a raw `collection` field into the namespace it addresses. */\nexport function parseResource(collection: string): ParsedResource {\n const value = collection.trim();\n if (value === RESOURCE_EVERYTHING) return { kind: \"everything\", name: \"\" };\n if (value === RESOURCE_STORAGE) return { kind: \"storage\", name: \"\" };\n if (value === RESOURCE_ALL_FUNCTIONS) return { kind: \"all-functions\", name: \"\" };\n if (value.startsWith(FUNCTION_PREFIX)) {\n return { kind: \"function\", name: value.slice(FUNCTION_PREFIX.length) };\n }\n return { kind: \"collection\", name: value };\n}\n\n/**\n * Short label for a resource — what a picker or a chip shows.\n *\n * Deliberately not the raw value: `\"*\"` alone is the thing nobody could read.\n */\nexport function resourceLabel(collection: string): string {\n const { kind, name } = parseResource(collection);\n switch (kind) {\n case \"everything\": return \"Everything\";\n case \"storage\": return \"Storage\";\n case \"all-functions\": return \"All functions\";\n case \"function\": return name ? `${name}()` : \"Function\";\n case \"collection\": return name || \"—\";\n }\n}\n\n/**\n * The resource as a sentence fragment, for \"this key can read <fragment>\".\n *\n * `\"everything\"` spells out all three namespaces, because that is exactly the\n * fact the old `*` input hid.\n */\nexport function resourcePhrase(collection: string): string {\n const { kind, name } = parseResource(collection);\n switch (kind) {\n case \"everything\": return \"every collection, every custom function and storage\";\n case \"storage\": return \"storage\";\n case \"all-functions\": return \"every custom function\";\n case \"function\": return name ? `the ${name} function` : \"one function\";\n case \"collection\": return name ? `the ${name} collection` : \"an unnamed collection\";\n }\n}\n\n/** How each operation reads for a given namespace, so the sentence stays true. */\nfunction operationPhrase(kind: ResourceKind, operation: string): string {\n if (kind === \"all-functions\" || kind === \"function\") {\n // Functions are invoked, not written to; the HTTP method still picks\n // the operation, so a POST-only function needs `write`.\n return operation === \"read\" ? \"call (GET)\"\n : operation === \"write\" ? \"call (POST, PUT, PATCH)\"\n : \"call (DELETE)\";\n }\n if (kind === \"storage\") {\n return operation === \"read\" ? \"download from\"\n : operation === \"write\" ? \"upload to\"\n : \"delete from\";\n }\n return operation;\n}\n\n/** Join with an Oxford-less \"and\", the way the rest of the panel reads. */\nfunction joinPhrases(parts: string[]): string {\n if (parts.length <= 1) return parts[0] ?? \"\";\n return `${parts.slice(0, -1).join(\", \")} and ${parts[parts.length - 1]}`;\n}\n\n/**\n * One plain sentence per permission entry: what the key will actually be able\n * to do. An entry with no operations selected grants nothing and says so,\n * rather than being silently dropped at submit time.\n */\nexport function grantSentence(perm: ApiKeyPermission): string {\n const { kind } = parseResource(perm.collection);\n const target = resourcePhrase(perm.collection);\n if (perm.operations.length === 0) return `No access to ${target}`;\n const verbs = joinPhrases(perm.operations.map(op => operationPhrase(kind, op)));\n const verb = verbs.charAt(0).toUpperCase() + verbs.slice(1);\n return `${verb} ${target}`;\n}\n\n/**\n * Dense one-line summary for list rows and the created-key confirmation.\n *\n * The wildcard wins over everything else in the array, because the guard\n * returns on the first match — a key holding `*` is a full-access key no\n * matter what else is listed beside it.\n */\nexport function permissionSummary(perms: ApiKeyPermission[]): string {\n if (perms.length === 0) return \"No permissions\";\n const wildcard = perms.find(p => p.collection === RESOURCE_EVERYTHING);\n if (wildcard) return `Everything (${wildcard.operations.join(\", \")})`;\n if (perms.length === 1) {\n return `${resourceLabel(perms[0].collection)} (${perms[0].operations.join(\", \")})`;\n }\n return `${perms.length} resources`;\n}\n","import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n AlertTriangleIcon,\n BooleanSwitchWithLabel,\n Button,\n ChevronsUpDownIcon,\n CircularProgress,\n cls,\n DatabaseIcon,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FolderIcon,\n FunctionSquareIcon,\n GlobeIcon,\n iconSize,\n IconButton,\n PlusIcon,\n Select,\n SelectGroup,\n SelectItem,\n ShieldIcon,\n TextField,\n Tooltip,\n Trash2Icon,\n Typography\n} from \"@rebasepro/ui\";\nimport {\n useApiBase,\n useApiConfig,\n useRebaseClient,\n useSnackbarController,\n useStudioCollectionRegistry\n} from \"@rebasepro/app\";\nimport type { ApiKeyPermission, ApiKeyWithSecret, RebaseClient } from \"@rebasepro/types\";\n\nimport {\n FUNCTION_PREFIX,\n grantSentence,\n parseResource,\n RESOURCE_ALL_FUNCTIONS,\n RESOURCE_EVERYTHING,\n RESOURCE_STORAGE,\n type ResourceKind\n} from \"./permissions\";\n\n/* ═══════════════════════════════════════════════════════════════\n Row model\n ═══════════════════════════════════════════════════════════════ */\n\ninterface PermissionRow {\n /** The raw wire value for `ApiKeyPermission.collection`. */\n resource: string;\n read: boolean;\n write: boolean;\n delete: boolean;\n /**\n * The row edits its resource as free text instead of through the picker.\n * Tracked separately from the value: a slug the panel has not registered\n * is still a legitimate grant, and without this flag the row would snap\n * back to the picker on the next render as soon as it was typed.\n */\n freeText: boolean;\n}\n\nconst OPERATIONS = [\"read\", \"write\", \"delete\"] as const;\ntype Operation = (typeof OPERATIONS)[number];\n\n/** What each operation actually permits, per namespace, shown on the toggle. */\nconst OPERATION_HINT: Record<ResourceKind, Record<Operation, string>> = {\n everything: {\n read: \"GET on every resource\",\n write: \"POST, PUT and PATCH on every resource\",\n delete: \"DELETE on every resource\"\n },\n collection: {\n read: \"List and get snapshots\",\n write: \"Create and update snapshots\",\n delete: \"Delete snapshots\"\n },\n storage: {\n read: \"List and download files\",\n write: \"Upload files and create folders\",\n delete: \"Delete files\"\n },\n \"all-functions\": {\n read: \"Call any function over GET\",\n write: \"Call any function over POST, PUT or PATCH\",\n delete: \"Call any function over DELETE\"\n },\n function: {\n read: \"Call it over GET\",\n write: \"Call it over POST, PUT or PATCH\",\n delete: \"Call it over DELETE\"\n }\n};\n\n/**\n * Picker value that switches a row to free text.\n *\n * Never reaches the wire — choosing it sets `freeText` and clears the resource\n * — so it only has to be a value no collection slug would take.\n */\nconst SENTINEL_FREE_TEXT = \"__custom__\";\n\nconst rowToPermission = (row: PermissionRow): ApiKeyPermission => ({\n collection: row.resource.trim(),\n operations: OPERATIONS.filter(op => row[op])\n});\n\nconst rowGrantsNothing = (row: PermissionRow): boolean => {\n const perm = rowToPermission(row);\n return !perm.collection || perm.operations.length === 0;\n};\n\nfunction ResourceIcon({ kind, className }: { kind: ResourceKind; className?: string }) {\n const Component = kind === \"everything\" ? GlobeIcon\n : kind === \"storage\" ? FolderIcon\n : kind === \"all-functions\" || kind === \"function\" ? FunctionSquareIcon\n : DatabaseIcon;\n return <Component size={iconSize.smallest} className={className}/>;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Operation toggles\n\n Three states carried by one control each: off is neutral, on is tinted with\n the operation's own semantic colour. The previous version coloured the\n labels whether or not they were checked, so a read-only key still showed a\n red \"delete\" and the row read as more permissive than it was.\n ═══════════════════════════════════════════════════════════════ */\n\nconst OPERATION_STYLES: Record<Operation, { on: string; dot: string }> = {\n read: {\n on: \"bg-emerald-500/12 text-emerald-700 dark:text-emerald-300 ring-emerald-500/40\",\n dot: \"bg-emerald-500\"\n },\n write: {\n on: \"bg-blue-500/12 text-blue-700 dark:text-blue-300 ring-blue-500/40\",\n dot: \"bg-blue-500\"\n },\n delete: {\n on: \"bg-rose-500/12 text-rose-700 dark:text-rose-300 ring-rose-500/40\",\n dot: \"bg-rose-500\"\n }\n};\n\nfunction OperationToggles({\n row,\n onToggle\n }: {\n row: PermissionRow;\n onToggle: (operation: Operation, value: boolean) => void;\n}) {\n const kind = parseResource(row.resource).kind;\n return (\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Allowed operations\">\n {OPERATIONS.map(op => {\n const active = row[op];\n const styles = OPERATION_STYLES[op];\n return (\n <Tooltip key={op} title={OPERATION_HINT[kind][op]} delayDuration={400}>\n <button\n type=\"button\"\n aria-pressed={active}\n onClick={() => onToggle(op, !active)}\n className={cls(\n \"flex items-center gap-1.5 h-7 pl-2 pr-2.5 rounded-md text-2xs font-medium\",\n \"ring-1 transition-colors duration-150 cursor-pointer\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\",\n active\n ? styles.on\n : \"bg-transparent ring-transparent text-surface-500 dark:text-surface-400 hover:bg-surface-accent-100 dark:hover:bg-surface-800\"\n )}\n >\n <span className={cls(\n \"w-1.5 h-1.5 rounded-full transition-colors duration-150\",\n active ? styles.dot : \"bg-surface-300 dark:bg-surface-600\"\n )}/>\n {op}\n </button>\n </Tooltip>\n );\n })}\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Section heading\n ═══════════════════════════════════════════════════════════════ */\n\nfunction SectionLabel({ children, hint }: { children: React.ReactNode; hint?: React.ReactNode }) {\n return (\n <div className=\"flex items-baseline gap-2 mb-2\">\n <Typography\n variant=\"label\"\n className=\"text-2xs uppercase tracking-wider font-semibold text-surface-600 dark:text-surface-300\"\n gutterBottom={false}\n >\n {children}\n </Typography>\n {hint && (\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-2xs\" gutterBottom={false}>\n {hint}\n </Typography>\n )}\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Create API Key Dialog\n ═══════════════════════════════════════════════════════════════ */\n\nexport function CreateApiKeyDialog({\n onClose,\n onCreated\n }: {\n onClose: () => void;\n onCreated: (key: ApiKeyWithSecret) => void;\n}) {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const collectionRegistry = useStudioCollectionRegistry();\n const apiConfig = useApiConfig();\n const apiBase = useApiBase();\n\n const [name, setName] = useState(\"\");\n const [rows, setRows] = useState<PermissionRow[]>([\n { resource: RESOURCE_EVERYTHING, read: true, write: false, delete: false, freeText: false }\n ]);\n const [admin, setAdmin] = useState(false);\n const [rateLimit, setRateLimit] = useState(\"\");\n const [expiresIn, setExpiresIn] = useState(\"never\");\n const [creating, setCreating] = useState(false);\n\n /* The collections the panel already knows — the picker's main list. */\n const collections = useMemo(\n () => (collectionRegistry?.collections ?? [])\n .map(col => ({ slug: col.slug, name: col.name }))\n .filter(col => !!col.slug)\n .sort((a, b) => a.slug.localeCompare(b.slug)),\n [collectionRegistry?.collections]\n );\n\n /**\n * Deployed functions, so a single-function grant can be picked instead of\n * spelled `functions/<name>` from memory. Best-effort: this is an ordinary\n * API route, and a backend that serves no functions — or refuses the\n * request — just leaves the free-text escape to cover it.\n */\n const [functionNames, setFunctionNames] = useState<string[]>([]);\n useEffect(() => {\n if (!apiBase) return;\n let cancelled = false;\n (async () => {\n try {\n const token = await apiConfig?.getAuthToken?.();\n const res = await fetch(`${apiBase}/functions`, {\n headers: token ? { Authorization: `Bearer ${token}` } : undefined\n });\n if (!res.ok) return;\n const body = await res.json() as { functions?: { name?: string }[] };\n if (cancelled) return;\n setFunctionNames((body.functions ?? [])\n .map(fn => fn.name)\n .filter((fnName): fnName is string => !!fnName));\n } catch {\n /* No listing available; the picker falls back to free text. */\n }\n })();\n return () => { cancelled = true; };\n }, [apiBase, apiConfig]);\n\n const updateRow = useCallback((idx: number, patch: Partial<PermissionRow>) => {\n setRows(current => current.map((row, i) => i === idx ? { ...row, ...patch } : row));\n }, []);\n\n const addRow = () => setRows(current => [\n ...current,\n {\n resource: collections[0]?.slug ?? \"\",\n read: true,\n write: false,\n delete: false,\n freeText: collections.length === 0\n }\n ]);\n\n const removeRow = (idx: number) => setRows(current => current.filter((_, i) => i !== idx));\n\n /* What would actually be sent. A row with no operations grants nothing;\n it used to be dropped at submit time with no indication it had been. */\n const effectivePermissions = useMemo(\n () => rows.map(rowToPermission).filter(perm => perm.collection && perm.operations.length > 0),\n [rows]\n );\n\n const droppedRows = rows.filter(rowGrantsNothing).length;\n const hasWildcard = rows.some(row => row.resource === RESOURCE_EVERYTHING && OPERATIONS.some(op => row[op]));\n const canSubmit = !!name.trim() && effectivePermissions.length > 0 && !creating;\n\n const handleCreate = async () => {\n if (!client?.apiKeys || !canSubmit) return;\n\n let expires_at: string | null = null;\n if (expiresIn === \"7d\") expires_at = new Date(Date.now() + 7 * 86400000).toISOString();\n else if (expiresIn === \"30d\") expires_at = new Date(Date.now() + 30 * 86400000).toISOString();\n else if (expiresIn === \"90d\") expires_at = new Date(Date.now() + 90 * 86400000).toISOString();\n else if (expiresIn === \"1y\") expires_at = new Date(Date.now() + 365 * 86400000).toISOString();\n\n setCreating(true);\n try {\n const res = await client.apiKeys.createKey({\n name: name.trim(),\n permissions: effectivePermissions,\n admin,\n rate_limit: rateLimit ? parseInt(rateLimit, 10) : null,\n expires_at\n });\n onCreated(res.key);\n } catch (e: unknown) {\n snackbar.open({ type: \"error\", message: e instanceof Error ? e.message : String(e) });\n } finally {\n setCreating(false);\n }\n };\n\n const submitBlockedReason = !name.trim()\n ? \"Name the key first\"\n : effectivePermissions.length === 0\n ? \"Grant at least one operation on one resource\"\n : \"\";\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open && !creating) onClose(); }} maxWidth=\"2xl\">\n\n <DialogTitle variant=\"subtitle1\" gutterBottom={false} className=\"font-semibold\">\n Create API key\n </DialogTitle>\n\n <DialogContent includeMargin={false} className=\"px-8 pt-2 pb-4 flex flex-col gap-6\">\n\n <Typography variant=\"body2\" color=\"secondary\" gutterBottom={false} className=\"text-[13px] max-w-[62ch]\">\n A credential for scripts, cron jobs and agents. It authenticates as itself rather\n than as a user, and reaches only what you grant here.\n </Typography>\n\n <TextField\n label=\"Name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Analytics pipeline\"\n size=\"small\"\n autoFocus\n />\n\n {/* ── Access ── */}\n <div>\n <SectionLabel hint=\"What the key may call\">Access</SectionLabel>\n\n <div className={cls(\"rounded-lg border overflow-hidden\", defaultBorderMixin)}>\n {rows.map((row, idx) => {\n const parsed = parseResource(row.resource);\n return (\n <div\n key={idx}\n role=\"group\"\n aria-label={`Resource ${idx + 1}`}\n className={cls(\n \"flex flex-wrap items-center gap-x-3 gap-y-2 px-3 py-2.5\",\n idx > 0 && cls(\"border-t\", defaultBorderMixin)\n )}\n >\n <div className=\"flex items-center gap-2 flex-1 min-w-[15rem]\">\n <ResourceIcon\n kind={parsed.kind}\n className={cls(\n \"shrink-0\",\n parsed.kind === \"everything\"\n ? \"text-amber-600 dark:text-amber-400\"\n : \"text-surface-500 dark:text-surface-400\"\n )}\n />\n {row.freeText\n ? (\n <TextField\n size=\"small\"\n aria-label={`Resource ${idx + 1} name`}\n value={row.resource}\n onChange={(e) => updateRow(idx, { resource: e.target.value })}\n placeholder=\"collection slug, or functions/<name>\"\n className=\"flex-1\"\n endAdornment={\n <Tooltip title=\"Back to the list\">\n <IconButton\n size=\"smallest\"\n aria-label=\"Pick from the list instead\"\n onClick={() => updateRow(idx, {\n resource: collections[0]?.slug ?? RESOURCE_EVERYTHING,\n freeText: false\n })}\n >\n <ChevronsUpDownIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n }\n />\n )\n : (\n <Select\n size=\"small\"\n fullWidth\n className=\"flex-1\"\n aria-label={`Resource ${idx + 1}`}\n value={row.resource}\n position=\"popper\"\n onValueChange={(value) => {\n if (value === SENTINEL_FREE_TEXT) {\n updateRow(idx, { freeText: true, resource: \"\" });\n } else {\n updateRow(idx, { resource: value, freeText: false });\n }\n }}\n renderValue={(value) => (\n <span className=\"truncate\">\n {value === RESOURCE_EVERYTHING ? \"Everything\"\n : value === RESOURCE_STORAGE ? \"Storage\"\n : value === RESOURCE_ALL_FUNCTIONS ? \"All functions\"\n : String(value).startsWith(FUNCTION_PREFIX)\n ? `${String(value).slice(FUNCTION_PREFIX.length)}()`\n : String(value)}\n </span>\n )}\n >\n <SelectItem value={RESOURCE_EVERYTHING}>\n <div className=\"flex flex-col text-left\">\n <span>Everything</span>\n <span className=\"text-2xs text-text-secondary dark:text-text-secondary-dark\">\n Every collection, every function and storage\n </span>\n </div>\n </SelectItem>\n\n {collections.length > 0 && (\n <SelectGroup label=\"Collections\">\n {collections.map(col => (\n <SelectItem key={col.slug} value={col.slug}>\n <div className=\"flex flex-col text-left\">\n <span>{col.slug}</span>\n {col.name && col.name !== col.slug && (\n <span className=\"text-2xs text-text-secondary dark:text-text-secondary-dark\">\n {col.name}\n </span>\n )}\n </div>\n </SelectItem>\n ))}\n </SelectGroup>\n )}\n\n <SelectGroup label=\"Functions\">\n <SelectItem value={RESOURCE_ALL_FUNCTIONS}>All functions</SelectItem>\n {functionNames.map(fnName => (\n <SelectItem key={fnName} value={`${FUNCTION_PREFIX}${fnName}`}>\n {fnName}()\n </SelectItem>\n ))}\n </SelectGroup>\n\n <SelectGroup label=\"Storage\">\n <SelectItem value={RESOURCE_STORAGE}>Storage</SelectItem>\n </SelectGroup>\n\n <SelectGroup label=\"Other\">\n <SelectItem value={SENTINEL_FREE_TEXT}>Type a name…</SelectItem>\n </SelectGroup>\n </Select>\n )}\n </div>\n\n <OperationToggles\n row={row}\n onToggle={(operation, value) => updateRow(idx, { [operation]: value })}\n />\n\n <Tooltip title={rows.length > 1 ? \"Remove\" : \"At least one resource is required\"}>\n <span>\n <IconButton\n size=\"small\"\n disabled={rows.length === 1}\n onClick={() => removeRow(idx)}\n aria-label={`Remove resource ${idx + 1}`}\n >\n <Trash2Icon size={iconSize.smallest}/>\n </IconButton>\n </span>\n </Tooltip>\n </div>\n );\n })}\n </div>\n\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={addRow}\n className=\"mt-1.5\"\n startIcon={<PlusIcon size={iconSize.smallest}/>}\n >\n Add resource\n </Button>\n\n {/* Plain-language read-back of the grant being built. */}\n <div className={cls(\n \"mt-3 rounded-lg border px-3 py-2.5 bg-surface-accent-50 dark:bg-surface-900\",\n defaultBorderMixin\n )}>\n <Typography\n variant=\"label\"\n gutterBottom={false}\n className=\"text-2xs uppercase tracking-wider font-semibold text-surface-600 dark:text-surface-300\"\n >\n This key will be able to\n </Typography>\n {effectivePermissions.length === 0\n ? (\n <Typography\n variant=\"body2\"\n gutterBottom={false}\n className=\"mt-1.5 text-[13px] text-surface-500 dark:text-surface-400\"\n >\n Nothing yet — pick a resource and at least one operation.\n </Typography>\n )\n : (\n <ul className=\"mt-1.5 space-y-1\">\n {effectivePermissions.map((perm, idx) => (\n <li key={idx} className=\"flex items-start gap-2\">\n <ResourceIcon\n kind={parseResource(perm.collection).kind}\n className=\"mt-[3px] shrink-0 text-surface-500 dark:text-surface-400\"\n />\n <Typography variant=\"body2\" gutterBottom={false} className=\"text-[13px] leading-snug\">\n {grantSentence(perm)}\n </Typography>\n </li>\n ))}\n {admin && (\n <li className=\"flex items-start gap-2\">\n <ShieldIcon size={iconSize.smallest} className=\"mt-[3px] shrink-0 text-amber-600 dark:text-amber-400\"/>\n <Typography variant=\"body2\" gutterBottom={false} className=\"text-[13px] leading-snug\">\n Reach every admin route — users, roles, cron, backups, logs — and read\n through the <span className=\"font-mono text-2xs\">default_admin</span> policies\n </Typography>\n </li>\n )}\n </ul>\n )}\n {droppedRows > 0 && effectivePermissions.length > 0 && (\n <Typography\n variant=\"caption\"\n gutterBottom={false}\n className=\"block mt-2 text-2xs text-surface-500 dark:text-surface-400\"\n >\n {droppedRows === 1\n ? \"1 row grants nothing and will not be saved.\"\n : `${droppedRows} rows grant nothing and will not be saved.`}\n </Typography>\n )}\n </div>\n\n {hasWildcard && (\n <div className=\"flex items-start gap-2 mt-2 px-1\">\n <AlertTriangleIcon size={iconSize.smallest} className=\"mt-[3px] shrink-0 text-amber-600 dark:text-amber-400\"/>\n <Typography\n variant=\"caption\"\n gutterBottom={false}\n className=\"text-2xs text-amber-700 dark:text-amber-300 leading-snug max-w-[70ch]\"\n >\n <span className=\"font-semibold\">Everything</span> is the widest grant there is. It also\n covers collections and functions you add later, without this key being edited again.\n </Typography>\n </div>\n )}\n </div>\n\n {/* ── Admin role ── */}\n <div>\n <SectionLabel hint=\"Off for almost every key\">Admin role</SectionLabel>\n <div className={cls(\n \"rounded-lg border px-3 py-2.5 transition-colors duration-150\",\n admin ? \"border-amber-500/40 bg-amber-500/[0.06]\" : defaultBorderMixin\n )}>\n <BooleanSwitchWithLabel\n size=\"small\"\n position=\"start\"\n invisible\n value={admin}\n onValueChange={setAdmin}\n label={\n <div className=\"flex items-center gap-2\">\n <ShieldIcon\n size={iconSize.smallest}\n className={admin\n ? \"text-amber-600 dark:text-amber-400\"\n : \"text-surface-500 dark:text-surface-400\"}\n />\n <span className=\"text-[13px]\">Grant the admin role</span>\n </div>\n }\n />\n <Typography\n variant=\"caption\"\n color=\"secondary\"\n gutterBottom={false}\n className=\"block mt-1.5 text-2xs leading-snug max-w-[70ch]\"\n >\n {admin\n ? \"The key passes the admin-gated routes and reads through the default_admin RLS policies — far wider than the resources above. It still cannot manage API keys.\"\n : \"Without it the key carries only the service role, and RLS grants it nothing unless a collection policy names that role.\"}\n </Typography>\n </div>\n </div>\n\n {/* ── Limits ── */}\n <div>\n <SectionLabel>Limits</SectionLabel>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3 items-end\">\n {/* Each control gets its own cell: `Select.Root` renders no DOM\n node, so an unwrapped Select puts its label and its control\n into two separate grid items. */}\n <div>\n <Select\n label=\"Expires\"\n value={expiresIn}\n onValueChange={setExpiresIn}\n size=\"small\"\n fullWidth\n position=\"popper\"\n renderValue={(v) =>\n v === \"never\" ? \"Never\"\n : v === \"7d\" ? \"In 7 days\"\n : v === \"30d\" ? \"In 30 days\"\n : v === \"90d\" ? \"In 90 days\" : \"In 1 year\"\n }\n >\n <SelectItem value=\"never\">Never</SelectItem>\n <SelectItem value=\"7d\">In 7 days</SelectItem>\n <SelectItem value=\"30d\">In 30 days</SelectItem>\n <SelectItem value=\"90d\">In 90 days</SelectItem>\n <SelectItem value=\"1y\">In 1 year</SelectItem>\n </Select>\n </div>\n <div>\n {/* Labelled above rather than floating inside, so it lines\n up with the Select beside it — a Select can only label\n above, and the two idioms in one row do not align. */}\n <label\n htmlFor=\"api-key-rate-limit\"\n className=\"block text-sm font-medium ml-3.5 mb-1 text-text-secondary dark:text-text-secondary-dark\"\n >\n Rate limit\n </label>\n <TextField\n id=\"api-key-rate-limit\"\n value={rateLimit}\n onChange={(e) => setRateLimit(e.target.value.replace(/\\D/g, \"\"))}\n placeholder=\"1000\"\n size=\"small\"\n endAdornment={\n <span className=\"text-2xs text-text-secondary dark:text-text-secondary-dark whitespace-nowrap\">\n / 15 min\n </span>\n }\n />\n </div>\n </div>\n <Typography\n variant=\"caption\"\n color=\"secondary\"\n gutterBottom={false}\n className=\"block mt-1.5 text-2xs\"\n >\n Leave the rate limit empty for the server default of 1000 requests per 15-minute window.\n </Typography>\n </div>\n\n </DialogContent>\n\n <DialogActions>\n <Button variant=\"text\" onClick={onClose} disabled={creating}>Cancel</Button>\n <Tooltip title={submitBlockedReason}>\n <span>\n <Button\n color=\"primary\"\n onClick={handleCreate}\n disabled={!canSubmit}\n startIcon={creating ? <CircularProgress size=\"smallest\"/> : undefined}\n >\n {creating ? \"Creating…\" : \"Create key\"}\n </Button>\n </span>\n </Tooltip>\n </DialogActions>\n </Dialog>\n );\n}\n","\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport {\n Button,\n Card,\n Chip,\n CircularProgress,\n cls,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n iconSize,\n KeyRoundIcon,\n RefreshCwIcon,\n ShieldIcon,\n Tooltip,\n Typography,\n CopyIcon,\n PlusIcon as AddIcon,\n Trash2Icon as DeleteIcon,\n AlertCircleIcon,\n CheckCircleIcon\n} from \"@rebasepro/ui\";\nimport { useRebaseClient, useSnackbarController } from \"@rebasepro/app\";\nimport type { ApiKeyMasked, ApiKeyWithSecret, RebaseClient } from \"@rebasepro/types\";\n\nimport { CreateApiKeyDialog } from \"./CreateApiKeyDialog\";\nimport { permissionSummary, resourceLabel, resourcePhrase } from \"./permissions\";\n\n/* ═══════════════════════════════════════════════════════════════\n Helpers\n\n The row types come from `@rebasepro/types`: this view used to declare its\n own copies, and they had already drifted — neither carried `admin`, so the\n panel could not tell an admin key from a scoped one.\n ═══════════════════════════════════════════════════════════════ */\n\nfunction formatRelative(iso: string | null | undefined): string {\n if (!iso) return \"—\";\n const d = new Date(iso);\n const now = Date.now();\n const diff = d.getTime() - now;\n const abs = Math.abs(diff);\n if (abs < 60000) return diff > 0 ? \"in <1m\" : \"<1m ago\";\n if (abs < 3600000) { const m = Math.round(abs / 60000); return diff > 0 ? `in ${m}m` : `${m}m ago`; }\n if (abs < 86400000) { const h = Math.round(abs / 3600000); return diff > 0 ? `in ${h}h` : `${h}h ago`; }\n return d.toLocaleDateString();\n}\n\nfunction isExpired(key: ApiKeyMasked): boolean {\n return !!(key.expires_at && new Date(key.expires_at) < new Date());\n}\n\nfunction keyStatus(key: ApiKeyMasked): { label: string; color: string } {\n if (key.revoked_at) return { label: \"Revoked\", color: \"text-red-500\" };\n if (isExpired(key)) return { label: \"Expired\", color: \"text-amber-500\" };\n return { label: \"Active\", color: \"text-emerald-500\" };\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Main Component\n ═══════════════════════════════════════════════════════════════ */\n\nexport function ApiKeysView() {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const [keys, setKeys] = useState<ApiKeyMasked[]>([]);\n const [loading, setLoading] = useState(true);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [showCreate, setShowCreate] = useState(false);\n const [showSecret, setShowSecret] = useState<ApiKeyWithSecret | null>(null);\n const [revoking, setRevoking] = useState<string | null>(null);\n const [confirmRevoke, setConfirmRevoke] = useState<ApiKeyMasked | null>(null);\n\n const clientRef = useRef(client);\n clientRef.current = client;\n const snackbarRef = useRef(snackbar);\n snackbarRef.current = snackbar;\n\n const loadKeys = useCallback(async () => {\n const c = clientRef.current;\n if (!c?.apiKeys) { setLoading(false); return; }\n try {\n const res = await c.apiKeys.listKeys();\n setKeys(res.keys);\n } catch (e: unknown) {\n snackbarRef.current.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => { loadKeys(); }, [loadKeys]);\n\n const handleRevoke = async (id: string) => {\n const c = clientRef.current;\n if (!c?.apiKeys) return;\n setRevoking(id);\n try {\n await c.apiKeys.revokeKey(id);\n snackbarRef.current.open({ type: \"success\", message: \"API key revoked\" });\n await loadKeys();\n if (selectedId === id) setSelectedId(null);\n } catch (e: unknown) {\n snackbarRef.current.open({ type: \"error\", message: e instanceof Error ? e.message : String(e) });\n } finally { setRevoking(null); }\n };\n\n const handleCreated = (keyWithSecret: ApiKeyWithSecret) => {\n setShowCreate(false);\n setShowSecret(keyWithSecret);\n loadKeys();\n };\n\n const selectedKey = keys.find(k => k.id === selectedId);\n const activeKeys = keys.filter(k => !k.revoked_at && !isExpired(k));\n const inactiveKeys = keys.filter(k => k.revoked_at || isExpired(k));\n\n if (loading) return <div className=\"flex items-center justify-center h-full\"><CircularProgress/></div>;\n\n return (\n <>\n <div className=\"flex h-full w-full overflow-hidden bg-white dark:bg-surface-950\">\n {/* ── Key List ── */}\n <div className={cls(\"flex flex-col w-[340px] min-w-[280px] border-r h-full\", defaultBorderMixin)}>\n <div className={cls(\"flex items-center justify-between px-4 py-2.5 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-2\">\n <KeyRoundIcon size={iconSize.smallest} className=\"text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"font-semibold\">API Keys</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">{activeKeys.length}</Chip>\n </div>\n <div className=\"flex items-center gap-1\">\n <IconButton size=\"small\" onClick={loadKeys} title=\"Refresh\"><RefreshCwIcon size={iconSize.smallest}/></IconButton>\n <Button size=\"small\" color=\"primary\" onClick={() => setShowCreate(true)} startIcon={<AddIcon size={iconSize.smallest}/>}>\n New\n </Button>\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto p-2 space-y-1\">\n {activeKeys.length === 0 && inactiveKeys.length === 0 && (\n <div className=\"flex flex-col items-center justify-center h-full gap-3 text-center p-6\">\n <KeyRoundIcon size={iconSize.medium} className=\"text-surface-300 dark:text-surface-600\"/>\n <Typography variant=\"body2\" color=\"secondary\">No API keys yet</Typography>\n <Typography variant=\"caption\" color=\"disabled\">Create a key to enable machine-to-machine authentication</Typography>\n </div>\n )}\n {activeKeys.map(key => (\n <KeyListItem key={key.id} apiKey={key} selected={selectedId === key.id} onClick={() => setSelectedId(key.id)}/>\n ))}\n {inactiveKeys.length > 0 && (\n <>\n <div className=\"px-2 pt-3 pb-1\">\n <Typography variant=\"caption\" color=\"disabled\" className=\"text-[10px] uppercase tracking-wider font-medium\">Revoked / Expired</Typography>\n </div>\n {inactiveKeys.map(key => (\n <KeyListItem key={key.id} apiKey={key} selected={selectedId === key.id} onClick={() => setSelectedId(key.id)}/>\n ))}\n </>\n )}\n </div>\n </div>\n\n {/* ── Detail Panel ── */}\n <div className=\"flex-1 flex flex-col min-w-0 h-full overflow-hidden\">\n {!selectedKey ? (\n <div className=\"flex items-center justify-center h-full\">\n <Typography variant=\"body2\" color=\"disabled\">Select an API key to view details</Typography>\n </div>\n ) : (\n <>\n {/* Header */}\n <div className={cls(\"flex items-center justify-between px-5 py-3 border-b bg-white dark:bg-surface-950 min-h-[56px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-3 min-w-0\">\n <KeyRoundIcon size={iconSize.small} className=\"text-primary shrink-0\"/>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <Typography variant=\"subtitle1\" className=\"font-semibold truncate\">{selectedKey.name}</Typography>\n {selectedKey.admin && <AdminChip/>}\n </div>\n <Typography variant=\"caption\" color=\"secondary\" className=\"font-mono text-[11px]\">{selectedKey.key_prefix}•••</Typography>\n </div>\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n {!selectedKey.revoked_at && (\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n onClick={() => setConfirmRevoke(selectedKey)}\n disabled={revoking === selectedKey.id}\n startIcon={revoking === selectedKey.id ? <CircularProgress size=\"smallest\"/> : <DeleteIcon size={iconSize.smallest}/>}\n >\n Revoke\n </Button>\n )}\n </div>\n </div>\n\n {/* Stats */}\n <div className=\"px-5 py-4 bg-surface-50 dark:bg-surface-900/50\">\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-3\">\n <StatCard label=\"Status\" value={keyStatus(selectedKey).label} className={keyStatus(selectedKey).color}/>\n <StatCard label=\"Created\" value={formatRelative(selectedKey.created_at)}/>\n <StatCard label=\"Last Used\" value={formatRelative(selectedKey.last_used_at)}/>\n <StatCard label=\"Expires\" value={selectedKey.expires_at ? formatRelative(selectedKey.expires_at) : \"Never\"}/>\n </div>\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-3 mt-3\">\n <StatCard\n label=\"Role\"\n value={selectedKey.admin ? \"Admin\" : \"Service\"}\n className={selectedKey.admin ? \"text-amber-600 dark:text-amber-400\" : undefined}\n />\n <StatCard label=\"Rate Limit\" value={selectedKey.rate_limit ? `${selectedKey.rate_limit}/15min` : \"Default (1000/15min)\"}/>\n <StatCard label=\"Created By\" value={selectedKey.created_by} mono/>\n </div>\n </div>\n\n {/* Permissions */}\n <div className={cls(\"flex items-center gap-2 px-5 py-2 border-y bg-white dark:bg-surface-950\", defaultBorderMixin)}>\n <Typography variant=\"subtitle2\" className=\"font-semibold text-[13px]\">Permissions</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">\n {selectedKey.permissions.length}\n </Chip>\n </div>\n <div className=\"flex-1 overflow-y-auto px-5 py-3\">\n {selectedKey.admin && (\n <div className=\"flex items-start gap-2 mb-3 px-3 py-2 rounded-lg border border-amber-500/40 bg-amber-500/[0.06]\">\n <ShieldIcon size={iconSize.smallest} className=\"mt-[3px] shrink-0 text-amber-600 dark:text-amber-400\"/>\n <Typography variant=\"caption\" className=\"text-[12px] leading-snug text-amber-700 dark:text-amber-300\">\n This key holds the <span className=\"font-semibold\">admin role</span>: it also passes the\n admin-gated routes — users, roles, cron, backups, logs — and reads through the\n <span className=\"font-mono\"> default_admin</span> RLS policies, beyond the resources listed here.\n </Typography>\n </div>\n )}\n {selectedKey.permissions.length === 0 ? (\n <Typography variant=\"body2\" color=\"disabled\">No permissions configured</Typography>\n ) : (\n <div className=\"space-y-2\">\n {selectedKey.permissions.map((perm, idx) => (\n <div key={idx} className={cls(\"flex items-center gap-3 px-3 py-2 rounded-lg border\", defaultBorderMixin)}>\n <div className=\"flex-1 min-w-0\">\n <Typography variant=\"body2\" className=\"text-[13px] font-medium truncate\">\n {resourceLabel(perm.collection)}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[11px]\">\n {resourcePhrase(perm.collection)}\n </Typography>\n </div>\n <div className=\"flex items-center gap-1 shrink-0\">\n {perm.operations.map(op => (\n <Chip key={op} size=\"smallest\" className={cls(\n op === \"read\" && \"bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300\",\n op === \"write\" && \"bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300\",\n op === \"delete\" && \"bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300\"\n )}>{op}</Chip>\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </>\n )}\n </div>\n </div>\n\n {/* Revoke Confirmation Dialog */}\n <Dialog\n open={confirmRevoke !== null}\n onOpenChange={(open) => {\n if (!open && !revoking) setConfirmRevoke(null);\n }}\n >\n <DialogTitle hidden>Revoke Confirmation</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"font-semibold mb-2\">\n Revoke "{confirmRevoke?.name}"?\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\">\n Requests authenticated with this key will stop working immediately. This action cannot be undone.\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => setConfirmRevoke(null)}\n disabled={revoking !== null}\n >\n Cancel\n </Button>\n <Button\n color=\"error\"\n disabled={revoking !== null}\n startIcon={revoking !== null ? <CircularProgress size=\"smallest\"/> : <DeleteIcon size={iconSize.smallest}/>}\n onClick={async () => {\n if (!confirmRevoke) return;\n await handleRevoke(confirmRevoke.id);\n setConfirmRevoke(null);\n }}\n >\n Revoke\n </Button>\n </DialogActions>\n </Dialog>\n\n {/* Create Dialog */}\n {showCreate && (\n <CreateApiKeyDialog\n onClose={() => setShowCreate(false)}\n onCreated={handleCreated}\n />\n )}\n\n {/* Secret Display Dialog */}\n {showSecret && (\n <SecretDisplayDialog\n keyWithSecret={showSecret}\n onClose={() => setShowSecret(null)}\n />\n )}\n </>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n List item\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Marks a key that carries the admin role.\n *\n * Not cosmetic: an admin key reaches the admin routes and the `default_admin`\n * RLS policies, and without this it is indistinguishable in the list from a\n * read-only one.\n */\nfunction AdminChip() {\n return (\n <Tooltip title=\"Carries the admin role: the admin-gated routes and the default_admin RLS policies\">\n <Chip\n size=\"smallest\"\n className=\"shrink-0 bg-amber-500/12 dark:bg-amber-500/12 text-amber-700 dark:text-amber-300 border-amber-500/30 dark:border-amber-500/30\"\n >\n <ShieldIcon size={10}/>\n admin\n </Chip>\n </Tooltip>\n );\n}\n\nfunction KeyListItem({ apiKey, selected, onClick }: { apiKey: ApiKeyMasked; selected: boolean; onClick: () => void }) {\n const status = keyStatus(apiKey);\n return (\n <div\n onClick={onClick}\n className={cls(\n \"flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all\",\n selected\n ? \"bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30\"\n : \"hover:bg-surface-100 dark:hover:bg-surface-950\"\n )}\n >\n <div className={cls(\"w-2 h-2 rounded-full shrink-0\",\n status.label === \"Active\" ? \"bg-emerald-400\" :\n status.label === \"Expired\" ? \"bg-amber-400\" : \"bg-red-400\"\n )}/>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-1.5 min-w-0\">\n <Typography variant=\"body2\" className=\"truncate font-medium text-[13px]\">{apiKey.name}</Typography>\n {apiKey.admin && <AdminChip/>}\n </div>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate text-[11px] font-mono\">{apiKey.key_prefix}•••</Typography>\n </div>\n <div className=\"shrink-0\">\n <Typography variant=\"caption\" color=\"disabled\" className=\"text-[10px]\">{permissionSummary(apiKey.permissions)}</Typography>\n </div>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Stat card\n ═══════════════════════════════════════════════════════════════ */\n\nfunction StatCard({ label, value, mono, className }: { label: string; value: string; mono?: boolean; className?: string }) {\n return (\n <div className={cls(\"px-3 py-2 rounded-lg border bg-white dark:bg-surface-900\", defaultBorderMixin)}>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[10px] uppercase tracking-wider font-medium\">{label}</Typography>\n <Typography variant=\"body2\" className={cls(\n \"mt-0.5 font-semibold text-[13px]\",\n mono && \"font-mono\",\n className\n )}>{value}</Typography>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Secret Display Dialog — shown exactly once after creation\n ═══════════════════════════════════════════════════════════════ */\n\nfunction SecretDisplayDialog({ keyWithSecret, onClose }: { keyWithSecret: ApiKeyWithSecret; onClose: () => void }) {\n const snackbar = useSnackbarController();\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(keyWithSecret.key);\n setCopied(true);\n snackbar.open({ type: \"success\", message: \"API key copied to clipboard\" });\n setTimeout(() => setCopied(false), 2000);\n } catch {\n snackbar.open({ type: \"error\", message: \"Failed to copy\" });\n }\n };\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open) onClose(); }} maxWidth=\"md\">\n <DialogTitle>\n <div className=\"flex items-center gap-2\">\n <CheckCircleIcon size={iconSize.small} className=\"text-emerald-500\"/>\n API Key Created\n </div>\n </DialogTitle>\n <DialogContent>\n <div className=\"p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 mb-4\">\n <div className=\"flex items-center gap-2 mb-1\">\n <AlertCircleIcon size={iconSize.smallest} className=\"text-amber-600 dark:text-amber-400\"/>\n <Typography variant=\"caption\" className=\"font-semibold text-amber-700 dark:text-amber-400\">\n Copy your key now — it won't be shown again\n </Typography>\n </div>\n <Typography variant=\"caption\" className=\"text-amber-600 dark:text-amber-300\">\n This is the only time the full API key will be displayed. Store it securely.\n </Typography>\n </div>\n\n <div className={cls(\"flex items-center gap-2 p-3 rounded-lg border bg-surface-50 dark:bg-surface-900\", defaultBorderMixin)}>\n <code className=\"flex-1 text-[12px] font-mono break-all text-surface-700 dark:text-surface-300 select-all\">\n {keyWithSecret.key}\n </code>\n <Tooltip title={copied ? \"Copied!\" : \"Copy\"}>\n <IconButton size=\"small\" onClick={handleCopy}>\n {copied\n ? <CheckCircleIcon size={iconSize.smallest} className=\"text-emerald-500\"/>\n : <CopyIcon size={iconSize.smallest}/>\n }\n </IconButton>\n </Tooltip>\n </div>\n\n <div className=\"mt-4 space-y-1\">\n <Typography variant=\"caption\" color=\"secondary\">\n <strong>Name:</strong> {keyWithSecret.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n <strong>Access:</strong> {permissionSummary(keyWithSecret.permissions)}\n </Typography>\n {keyWithSecret.admin && (\n <Typography variant=\"caption\" className=\"flex items-center gap-1.5 text-amber-700 dark:text-amber-300\">\n <ShieldIcon size={iconSize.smallest} className=\"shrink-0\"/>\n <span><strong>Admin role granted</strong> — the admin routes and the default_admin policies</span>\n </Typography>\n )}\n </div>\n </DialogContent>\n <DialogActions>\n <Button color=\"primary\" onClick={onClose}>Done</Button>\n </DialogActions>\n </Dialog>\n );\n}\n"],"mappings":";;;;;AA4BA,IAAa,mBAAmB;;AAEhC,IAAa,yBAAyB;;AAEtC,IAAa,kBAAkB;;AAgB/B,SAAgB,cAAc,YAAoC;CAC9D,MAAM,QAAQ,WAAW,KAAK;CAC9B,IAAI,UAAA,KAA+B,OAAO;EAAE,MAAM;EAAc,MAAM;CAAG;CACzE,IAAI,UAAA,WAA4B,OAAO;EAAE,MAAM;EAAW,MAAM;CAAG;CACnE,IAAI,UAAA,aAAkC,OAAO;EAAE,MAAM;EAAiB,MAAM;CAAG;CAC/E,IAAI,MAAM,WAAA,YAA0B,GAChC,OAAO;EAAE,MAAM;EAAY,MAAM,MAAM,MAAM,EAAsB;CAAE;CAEzE,OAAO;EAAE,MAAM;EAAc,MAAM;CAAM;AAC7C;;;;;;AAOA,SAAgB,cAAc,YAA4B;CACtD,MAAM,EAAE,MAAM,SAAS,cAAc,UAAU;CAC/C,QAAQ,MAAR;EACI,KAAK,cAAc,OAAO;EAC1B,KAAK,WAAW,OAAO;EACvB,KAAK,iBAAiB,OAAO;EAC7B,KAAK,YAAY,OAAO,OAAO,GAAG,KAAK,MAAM;EAC7C,KAAK,cAAc,OAAO,QAAQ;CACtC;AACJ;;;;;;;AAQA,SAAgB,eAAe,YAA4B;CACvD,MAAM,EAAE,MAAM,SAAS,cAAc,UAAU;CAC/C,QAAQ,MAAR;EACI,KAAK,cAAc,OAAO;EAC1B,KAAK,WAAW,OAAO;EACvB,KAAK,iBAAiB,OAAO;EAC7B,KAAK,YAAY,OAAO,OAAO,OAAO,KAAK,aAAa;EACxD,KAAK,cAAc,OAAO,OAAO,OAAO,KAAK,eAAe;CAChE;AACJ;;AAGA,SAAS,gBAAgB,MAAoB,WAA2B;CACpE,IAAI,SAAS,mBAAmB,SAAS,YAGrC,OAAO,cAAc,SAAS,eACxB,cAAc,UAAU,4BACpB;CAEd,IAAI,SAAS,WACT,OAAO,cAAc,SAAS,kBACxB,cAAc,UAAU,cACpB;CAEd,OAAO;AACX;;AAGA,SAAS,YAAY,OAAyB;CAC1C,IAAI,MAAM,UAAU,GAAG,OAAO,MAAM,MAAM;CAC1C,OAAO,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,OAAO,MAAM,MAAM,SAAS;AACxE;;;;;;AAOA,SAAgB,cAAc,MAAgC;CAC1D,MAAM,EAAE,SAAS,cAAc,KAAK,UAAU;CAC9C,MAAM,SAAS,eAAe,KAAK,UAAU;CAC7C,IAAI,KAAK,WAAW,WAAW,GAAG,OAAO,gBAAgB;CACzD,MAAM,QAAQ,YAAY,KAAK,WAAW,KAAI,OAAM,gBAAgB,MAAM,EAAE,CAAC,CAAC;CAE9E,OAAO,GADM,MAAM,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,MAAM,MAAM,CAAC,EAC3C,GAAG;AACtB;;;;;;;;AASA,SAAgB,kBAAkB,OAAmC;CACjE,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,MAAM,WAAW,MAAM,MAAK,MAAK,EAAE,eAAA,GAAkC;CACrE,IAAI,UAAU,OAAO,eAAe,SAAS,WAAW,KAAK,IAAI,EAAE;CACnE,IAAI,MAAM,WAAW,GACjB,OAAO,GAAG,cAAc,MAAM,EAAE,CAAC,UAAU,EAAE,IAAI,MAAM,EAAE,CAAC,WAAW,KAAK,IAAI,EAAE;CAEpF,OAAO,GAAG,MAAM,OAAO;AAC3B;;;AC7EA,IAAM,aAAa;CAAC;CAAQ;CAAS;AAAQ;;AAI7C,IAAM,iBAAkE;CACpE,YAAY;EACR,MAAM;EACN,OAAO;EACP,QAAQ;CACZ;CACA,YAAY;EACR,MAAM;EACN,OAAO;EACP,QAAQ;CACZ;CACA,SAAS;EACL,MAAM;EACN,OAAO;EACP,QAAQ;CACZ;CACA,iBAAiB;EACb,MAAM;EACN,OAAO;EACP,QAAQ;CACZ;CACA,UAAU;EACN,MAAM;EACN,OAAO;EACP,QAAQ;CACZ;AACJ;;;;;;;AAQA,IAAM,qBAAqB;AAE3B,IAAM,mBAAmB,SAA0C;CAC/D,YAAY,IAAI,SAAS,KAAK;CAC9B,YAAY,WAAW,QAAO,OAAM,IAAI,GAAG;AAC/C;AAEA,IAAM,oBAAoB,QAAgC;CACtD,MAAM,OAAO,gBAAgB,GAAG;CAChC,OAAO,CAAC,KAAK,cAAc,KAAK,WAAW,WAAW;AAC1D;AAEA,SAAS,aAAa,EAAE,MAAM,aAAyD;CAKnF,OAAO,oBAJW,SAAS,eAAe,YACpC,SAAS,YAAY,aACjB,SAAS,mBAAmB,SAAS,aAAa,qBAC9C,cACP;EAAW,MAAM,SAAS;EAAqB;CAAW,CAAA;AACrE;AAWA,IAAM,mBAAmE;CACrE,MAAM;EACF,IAAI;EACJ,KAAK;CACT;CACA,OAAO;EACH,IAAI;EACJ,KAAK;CACT;CACA,QAAQ;EACJ,IAAI;EACJ,KAAK;CACT;AACJ;AAEA,SAAS,iBAAiB,EACI,KACA,YAI3B;CACC,MAAM,OAAO,cAAc,IAAI,QAAQ,CAAC,CAAC;CACzC,OACI,oBAAC,OAAD;EAAK,WAAU;EAA0B,MAAK;EAAQ,cAAW;YAC5D,WAAW,KAAI,OAAM;GAClB,MAAM,SAAS,IAAI;GACnB,MAAM,SAAS,iBAAiB;GAChC,OACI,oBAAC,SAAD;IAAkB,OAAO,eAAe,KAAK,CAAC;IAAK,eAAe;cAC9D,qBAAC,UAAD;KACI,MAAK;KACL,gBAAc;KACd,eAAe,SAAS,IAAI,CAAC,MAAM;KACnC,WAAW,IACP,6EACA,wDACA,8EACA,SACM,OAAO,KACP,8HACV;eAXJ,CAaI,oBAAC,QAAD,EAAM,WAAW,IACb,2DACA,SAAS,OAAO,MAAM,oCAC1B,EAAG,CAAA,GACF,EACG;;GACH,GApBK,EAoBL;EAEjB,CAAC;CACA,CAAA;AAEb;AAMA,SAAS,aAAa,EAAE,UAAU,QAA+D;CAC7F,OACI,qBAAC,OAAD;EAAK,WAAU;YAAf,CACI,oBAAC,YAAD;GACI,SAAQ;GACR,WAAU;GACV,cAAc;GAEb;EACO,CAAA,GACX,QACG,oBAAC,YAAD;GAAY,SAAQ;GAAU,OAAM;GAAY,WAAU;GAAW,cAAc;aAC9E;EACO,CAAA,CAEf;;AAEb;AAMA,SAAgB,mBAAmB,EACI,SACA,aAIpC;CACC,MAAM,SAAS,gBAA8B;CAC7C,MAAM,WAAW,sBAAsB;CACvC,MAAM,qBAAqB,4BAA4B;CACvD,MAAM,YAAY,aAAa;CAC/B,MAAM,UAAU,WAAW;CAE3B,MAAM,CAAC,MAAM,WAAW,SAAS,EAAE;CACnC,MAAM,CAAC,MAAM,WAAW,SAA0B,CAC9C;EAAE,UAAA;EAA+B,MAAM;EAAM,OAAO;EAAO,QAAQ;EAAO,UAAU;CAAM,CAC9F,CAAC;CACD,MAAM,CAAC,OAAO,YAAY,SAAS,KAAK;CACxC,MAAM,CAAC,WAAW,gBAAgB,SAAS,EAAE;CAC7C,MAAM,CAAC,WAAW,gBAAgB,SAAS,OAAO;CAClD,MAAM,CAAC,UAAU,eAAe,SAAS,KAAK;CAG9C,MAAM,cAAc,eACT,oBAAoB,eAAe,CAAC,EAAA,CACtC,KAAI,SAAQ;EAAE,MAAM,IAAI;EAAM,MAAM,IAAI;CAAK,EAAE,CAAC,CAChD,QAAO,QAAO,CAAC,CAAC,IAAI,IAAI,CAAC,CACzB,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAChD,CAAC,oBAAoB,WAAW,CACpC;;;;;;;CAQA,MAAM,CAAC,eAAe,oBAAoB,SAAmB,CAAC,CAAC;CAC/D,gBAAgB;EACZ,IAAI,CAAC,SAAS;EACd,IAAI,YAAY;EAChB,CAAC,YAAY;GACT,IAAI;IACA,MAAM,QAAQ,MAAM,WAAW,eAAe;IAC9C,MAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,aAAa,EAC5C,SAAS,QAAQ,EAAE,eAAe,UAAU,QAAQ,IAAI,KAAA,EAC5D,CAAC;IACD,IAAI,CAAC,IAAI,IAAI;IACb,MAAM,OAAO,MAAM,IAAI,KAAK;IAC5B,IAAI,WAAW;IACf,kBAAkB,KAAK,aAAa,CAAC,EAAA,CAChC,KAAI,OAAM,GAAG,IAAI,CAAC,CAClB,QAAQ,WAA6B,CAAC,CAAC,MAAM,CAAC;GACvD,QAAQ,CAER;EACJ,EAAA,CAAG;EACH,aAAa;GAAE,YAAY;EAAM;CACrC,GAAG,CAAC,SAAS,SAAS,CAAC;CAEvB,MAAM,YAAY,aAAa,KAAa,UAAkC;EAC1E,SAAQ,YAAW,QAAQ,KAAK,KAAK,MAAM,MAAM,MAAM;GAAE,GAAG;GAAK,GAAG;EAAM,IAAI,GAAG,CAAC;CACtF,GAAG,CAAC,CAAC;CAEL,MAAM,eAAe,SAAQ,YAAW,CACpC,GAAG,SACH;EACI,UAAU,YAAY,EAAE,EAAE,QAAQ;EAClC,MAAM;EACN,OAAO;EACP,QAAQ;EACR,UAAU,YAAY,WAAW;CACrC,CACJ,CAAC;CAED,MAAM,aAAa,QAAgB,SAAQ,YAAW,QAAQ,QAAQ,GAAG,MAAM,MAAM,GAAG,CAAC;CAIzF,MAAM,uBAAuB,cACnB,KAAK,IAAI,eAAe,CAAC,CAAC,QAAO,SAAQ,KAAK,cAAc,KAAK,WAAW,SAAS,CAAC,GAC5F,CAAC,IAAI,CACT;CAEA,MAAM,cAAc,KAAK,OAAO,gBAAgB,CAAC,CAAC;CAClD,MAAM,cAAc,KAAK,MAAK,QAAO,IAAI,aAAA,OAAoC,WAAW,MAAK,OAAM,IAAI,GAAG,CAAC;CAC3G,MAAM,YAAY,CAAC,CAAC,KAAK,KAAK,KAAK,qBAAqB,SAAS,KAAK,CAAC;CAEvE,MAAM,eAAe,YAAY;EAC7B,IAAI,CAAC,QAAQ,WAAW,CAAC,WAAW;EAEpC,IAAI,aAA4B;EAChC,IAAI,cAAc,MAAM,aAAa,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAQ,CAAC,CAAC,YAAY;OAChF,IAAI,cAAc,OAAO,aAAa,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAQ,CAAC,CAAC,YAAY;OACvF,IAAI,cAAc,OAAO,aAAa,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAQ,CAAC,CAAC,YAAY;OACvF,IAAI,cAAc,MAAM,aAAa,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,KAAQ,CAAC,CAAC,YAAY;EAE5F,YAAY,IAAI;EAChB,IAAI;GAQA,WAAU,MAPQ,OAAO,QAAQ,UAAU;IACvC,MAAM,KAAK,KAAK;IAChB,aAAa;IACb;IACA,YAAY,YAAY,SAAS,WAAW,EAAE,IAAI;IAClD;GACJ,CAAC,EAAA,CACa,GAAG;EACrB,SAAS,GAAY;GACjB,SAAS,KAAK;IAAE,MAAM;IAAS,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;GAAE,CAAC;EACxF,UAAU;GACN,YAAY,KAAK;EACrB;CACJ;CAEA,MAAM,sBAAsB,CAAC,KAAK,KAAK,IACjC,uBACA,qBAAqB,WAAW,IAC5B,iDACA;CAEV,OACI,qBAAC,QAAD;EAAQ,MAAA;EAAK,eAAe,SAAS;GAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,QAAQ;EAAG;EAAG,UAAS;YAAtF;GAEI,oBAAC,aAAD;IAAa,SAAQ;IAAY,cAAc;IAAO,WAAU;cAAgB;GAEnE,CAAA;GAEb,qBAAC,eAAD;IAAe,eAAe;IAAO,WAAU;cAA/C;KAEI,oBAAC,YAAD;MAAY,SAAQ;MAAQ,OAAM;MAAY,cAAc;MAAO,WAAU;gBAA2B;KAG5F,CAAA;KAEZ,oBAAC,WAAD;MACI,OAAM;MACN,OAAO;MACP,WAAW,MAAM,QAAQ,EAAE,OAAO,KAAK;MACvC,aAAY;MACZ,MAAK;MACL,WAAA;KACH,CAAA;KAGD,qBAAC,OAAD,EAAA,UAAA;MACI,oBAAC,cAAD;OAAc,MAAK;iBAAwB;MAAoB,CAAA;MAE/D,oBAAC,OAAD;OAAK,WAAW,IAAI,qCAAqC,kBAAkB;iBACtE,KAAK,KAAK,KAAK,QAAQ;QACpB,MAAM,SAAS,cAAc,IAAI,QAAQ;QACzC,OACI,qBAAC,OAAD;SAEI,MAAK;SACL,cAAY,YAAY,MAAM;SAC9B,WAAW,IACP,2DACA,MAAM,KAAK,IAAI,YAAY,kBAAkB,CACjD;mBAPJ;UASI,qBAAC,OAAD;WAAK,WAAU;qBAAf,CACI,oBAAC,cAAD;YACI,MAAM,OAAO;YACb,WAAW,IACP,YACA,OAAO,SAAS,eACV,uCACA,wCACV;WACH,CAAA,GACA,IAAI,WAEG,oBAAC,WAAD;YACI,MAAK;YACL,cAAY,YAAY,MAAM,EAAE;YAChC,OAAO,IAAI;YACX,WAAW,MAAM,UAAU,KAAK,EAAE,UAAU,EAAE,OAAO,MAAM,CAAC;YAC5D,aAAY;YACZ,WAAU;YACV,cACI,oBAAC,SAAD;aAAS,OAAM;uBACX,oBAAC,YAAD;cACI,MAAK;cACL,cAAW;cACX,eAAe,UAAU,KAAK;eAC1B,UAAU,YAAY,EAAE,EAAE,QAAA;eAC1B,UAAU;cACd,CAAC;wBAED,oBAAC,oBAAD,EAAoB,MAAM,SAAS,SAAU,CAAA;aACrC,CAAA;YACP,CAAA;WAEhB,CAAA,IAGD,qBAAC,QAAD;YACI,MAAK;YACL,WAAA;YACA,WAAU;YACV,cAAY,YAAY,MAAM;YAC9B,OAAO,IAAI;YACX,UAAS;YACT,gBAAgB,UAAU;aACtB,IAAI,UAAU,oBACV,UAAU,KAAK;cAAE,UAAU;cAAM,UAAU;aAAG,CAAC;kBAE/C,UAAU,KAAK;cAAE,UAAU;cAAO,UAAU;aAAM,CAAC;YAE3D;YACA,cAAc,UACV,oBAAC,QAAD;aAAM,WAAU;uBACX,UAAA,MAAgC,eAC3B,UAAA,YAA6B,YACzB,UAAA,cAAmC,kBAC/B,OAAO,KAAK,CAAC,CAAC,WAAA,YAA0B,IACpC,GAAG,OAAO,KAAK,CAAC,CAAC,MAAM,gBAAgB,MAAM,EAAE,MAC/C,OAAO,KAAK;YAC5B,CAAA;sBAtBd;aAyBI,oBAAC,YAAD;cAAY,OAAA;wBACR,qBAAC,OAAD;eAAK,WAAU;yBAAf,CACI,oBAAC,QAAD,EAAA,UAAM,aAAgB,CAAA,GACtB,oBAAC,QAAD;gBAAM,WAAU;0BAA6D;eAEvE,CAAA,CACL;;aACG,CAAA;aAEX,YAAY,SAAS,KAClB,oBAAC,aAAD;cAAa,OAAM;wBACd,YAAY,KAAI,QACb,oBAAC,YAAD;eAA2B,OAAO,IAAI;yBAClC,qBAAC,OAAD;gBAAK,WAAU;0BAAf,CACI,oBAAC,QAAD,EAAA,UAAO,IAAI,KAAW,CAAA,GACrB,IAAI,QAAQ,IAAI,SAAS,IAAI,QAC1B,oBAAC,QAAD;iBAAM,WAAU;2BACX,IAAI;gBACH,CAAA,CAET;;cACG,GATK,IAAI,IAST,CACf;aACQ,CAAA;aAGjB,qBAAC,aAAD;cAAa,OAAM;wBAAnB,CACI,oBAAC,YAAD;eAAY,OAAO;yBAAwB;cAAyB,CAAA,GACnE,cAAc,KAAI,WACf,qBAAC,YAAD;eAAyB,OAAO,GAAG,kBAAkB;yBAArD,CACK,QAAO,IACA;iBAFK,MAEL,CACf,CACQ;;aAEb,oBAAC,aAAD;cAAa,OAAM;wBACf,oBAAC,YAAD;eAAY,OAAO;yBAAkB;cAAmB,CAAA;aAC/C,CAAA;aAEb,oBAAC,aAAD;cAAa,OAAM;wBACf,oBAAC,YAAD;eAAY,OAAO;yBAAoB;cAAwB,CAAA;aACtD,CAAA;YACT;aAEf;;UAEL,oBAAC,kBAAD;WACS;WACL,WAAW,WAAW,UAAU,UAAU,KAAK,GAAG,YAAY,MAAM,CAAC;UACxE,CAAA;UAED,oBAAC,SAAD;WAAS,OAAO,KAAK,SAAS,IAAI,WAAW;qBACzC,oBAAC,QAAD,EAAA,UACI,oBAAC,YAAD;YACI,MAAK;YACL,UAAU,KAAK,WAAW;YAC1B,eAAe,UAAU,GAAG;YAC5B,cAAY,mBAAmB,MAAM;sBAErC,oBAAC,YAAD,EAAY,MAAM,SAAS,SAAU,CAAA;WAC7B,CAAA,EACV,CAAA;UACD,CAAA;SACR;WApII,GAoIJ;OAEb,CAAC;MACA,CAAA;MAEL,oBAAC,QAAD;OACI,MAAK;OACL,SAAQ;OACR,SAAS;OACT,WAAU;OACV,WAAW,oBAAC,UAAD,EAAU,MAAM,SAAS,SAAU,CAAA;iBACjD;MAEO,CAAA;MAGR,qBAAC,OAAD;OAAK,WAAW,IACZ,+EACA,kBACJ;iBAHA;QAII,oBAAC,YAAD;SACI,SAAQ;SACR,cAAc;SACd,WAAU;mBACb;QAEW,CAAA;QACX,qBAAqB,WAAW,IAEzB,oBAAC,YAAD;SACI,SAAQ;SACR,cAAc;SACd,WAAU;mBACb;QAEW,CAAA,IAGZ,qBAAC,MAAD;SAAI,WAAU;mBAAd,CACK,qBAAqB,KAAK,MAAM,QAC7B,qBAAC,MAAD;UAAc,WAAU;oBAAxB,CACI,oBAAC,cAAD;WACI,MAAM,cAAc,KAAK,UAAU,CAAC,CAAC;WACrC,WAAU;UACb,CAAA,GACD,oBAAC,YAAD;WAAY,SAAQ;WAAQ,cAAc;WAAO,WAAU;qBACtD,cAAc,IAAI;UACX,CAAA,CACZ;YARK,GAQL,CACP,GACA,SACG,qBAAC,MAAD;UAAI,WAAU;oBAAd,CACI,oBAAC,YAAD;WAAY,MAAM,SAAS;WAAU,WAAU;UAAuD,CAAA,GACtG,qBAAC,YAAD;WAAY,SAAQ;WAAQ,cAAc;WAAO,WAAU;qBAA3D;YAAsF;YAEtE,oBAAC,QAAD;aAAM,WAAU;uBAAqB;YAAmB,CAAA;YAAC;WAC7D;YACZ;WAER;;QAEX,cAAc,KAAK,qBAAqB,SAAS,KAC9C,oBAAC,YAAD;SACI,SAAQ;SACR,cAAc;SACd,WAAU;mBAET,gBAAgB,IACX,gDACA,GAAG,YAAY;QACb,CAAA;OAEf;;MAEJ,eACG,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,mBAAD;QAAmB,MAAM,SAAS;QAAU,WAAU;OAAuD,CAAA,GAC7G,qBAAC,YAAD;QACI,SAAQ;QACR,cAAc;QACd,WAAU;kBAHd,CAKI,oBAAC,QAAD;SAAM,WAAU;mBAAgB;QAAgB,CAAA,GAAC,6HAEzC;SACX;;KAER,EAAA,CAAA;KAGL,qBAAC,OAAD,EAAA,UAAA,CACI,oBAAC,cAAD;MAAc,MAAK;gBAA2B;KAAwB,CAAA,GACtE,qBAAC,OAAD;MAAK,WAAW,IACZ,gEACA,QAAQ,4CAA4C,kBACxD;gBAHA,CAII,oBAAC,wBAAD;OACI,MAAK;OACL,UAAS;OACT,WAAA;OACA,OAAO;OACP,eAAe;OACf,OACI,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACI,oBAAC,YAAD;SACI,MAAM,SAAS;SACf,WAAW,QACL,uCACA;QACT,CAAA,GACD,oBAAC,QAAD;SAAM,WAAU;mBAAc;QAA0B,CAAA,CACvD;;MAEZ,CAAA,GACD,oBAAC,YAAD;OACI,SAAQ;OACR,OAAM;OACN,cAAc;OACd,WAAU;iBAET,QACK,kKACA;MACE,CAAA,CACX;OACJ,EAAA,CAAA;KAGL,qBAAC,OAAD,EAAA,UAAA;MACI,oBAAC,cAAD,EAAA,UAAc,SAAoB,CAAA;MAClC,qBAAC,OAAD;OAAK,WAAU;iBAAf,CAII,oBAAC,OAAD,EAAA,UACI,qBAAC,QAAD;QACI,OAAM;QACN,OAAO;QACP,eAAe;QACf,MAAK;QACL,WAAA;QACA,UAAS;QACT,cAAc,MACV,MAAM,UAAU,UACV,MAAM,OAAO,cACT,MAAM,QAAQ,eACV,MAAM,QAAQ,eAAe;kBAXnD;SAcI,oBAAC,YAAD;UAAY,OAAM;oBAAQ;SAAiB,CAAA;SAC3C,oBAAC,YAAD;UAAY,OAAM;oBAAK;SAAqB,CAAA;SAC5C,oBAAC,YAAD;UAAY,OAAM;oBAAM;SAAsB,CAAA;SAC9C,oBAAC,YAAD;UAAY,OAAM;oBAAM;SAAsB,CAAA;SAC9C,oBAAC,YAAD;UAAY,OAAM;oBAAK;SAAqB,CAAA;QACxC;UACP,CAAA,GACL,qBAAC,OAAD,EAAA,UAAA,CAII,oBAAC,SAAD;QACI,SAAQ;QACR,WAAU;kBACb;OAEM,CAAA,GACP,oBAAC,WAAD;QACI,IAAG;QACH,OAAO;QACP,WAAW,MAAM,aAAa,EAAE,OAAO,MAAM,QAAQ,OAAO,EAAE,CAAC;QAC/D,aAAY;QACZ,MAAK;QACL,cACI,oBAAC,QAAD;SAAM,WAAU;mBAA+E;QAEzF,CAAA;OAEb,CAAA,CACA,EAAA,CAAA,CACJ;;MACL,oBAAC,YAAD;OACI,SAAQ;OACR,OAAM;OACN,cAAc;OACd,WAAU;iBACb;MAEW,CAAA;KACX,EAAA,CAAA;IAEM;;GAEf,qBAAC,eAAD,EAAA,UAAA,CACI,oBAAC,QAAD;IAAQ,SAAQ;IAAO,SAAS;IAAS,UAAU;cAAU;GAAc,CAAA,GAC3E,oBAAC,SAAD;IAAS,OAAO;cACZ,oBAAC,QAAD,EAAA,UACI,oBAAC,QAAD;KACI,OAAM;KACN,SAAS;KACT,UAAU,CAAC;KACX,WAAW,WAAW,oBAAC,kBAAD,EAAkB,MAAK,WAAW,CAAA,IAAI,KAAA;eAE3D,WAAW,cAAc;IACtB,CAAA,EACN,CAAA;GACD,CAAA,CACE,EAAA,CAAA;EACX;;AAEhB;;;AC9pBA,SAAS,eAAe,KAAwC;CAC5D,IAAI,CAAC,KAAK,OAAO;CACjB,MAAM,IAAI,IAAI,KAAK,GAAG;CACtB,MAAM,MAAM,KAAK,IAAI;CACrB,MAAM,OAAO,EAAE,QAAQ,IAAI;CAC3B,MAAM,MAAM,KAAK,IAAI,IAAI;CACzB,IAAI,MAAM,KAAO,OAAO,OAAO,IAAI,WAAW;CAC9C,IAAI,MAAM,MAAS;EAAE,MAAM,IAAI,KAAK,MAAM,MAAM,GAAK;EAAG,OAAO,OAAO,IAAI,MAAM,EAAE,KAAK,GAAG,EAAE;CAAQ;CACpG,IAAI,MAAM,OAAU;EAAE,MAAM,IAAI,KAAK,MAAM,MAAM,IAAO;EAAG,OAAO,OAAO,IAAI,MAAM,EAAE,KAAK,GAAG,EAAE;CAAQ;CACvG,OAAO,EAAE,mBAAmB;AAChC;AAEA,SAAS,UAAU,KAA4B;CAC3C,OAAO,CAAC,EAAE,IAAI,cAAc,IAAI,KAAK,IAAI,UAAU,oBAAI,IAAI,KAAK;AACpE;AAEA,SAAS,UAAU,KAAqD;CACpE,IAAI,IAAI,YAAY,OAAO;EAAE,OAAO;EAAW,OAAO;CAAe;CACrE,IAAI,UAAU,GAAG,GAAG,OAAO;EAAE,OAAO;EAAW,OAAO;CAAiB;CACvE,OAAO;EAAE,OAAO;EAAU,OAAO;CAAmB;AACxD;AAMA,SAAgB,cAAc;CAC1B,MAAM,SAAS,gBAA8B;CAC7C,MAAM,WAAW,sBAAsB;CACvC,MAAM,CAAC,MAAM,WAAW,SAAyB,CAAC,CAAC;CACnD,MAAM,CAAC,SAAS,cAAc,SAAS,IAAI;CAC3C,MAAM,CAAC,YAAY,iBAAiB,SAAwB,IAAI;CAChE,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAClD,MAAM,CAAC,YAAY,iBAAiB,SAAkC,IAAI;CAC1E,MAAM,CAAC,UAAU,eAAe,SAAwB,IAAI;CAC5D,MAAM,CAAC,eAAe,oBAAoB,SAA8B,IAAI;CAE5E,MAAM,YAAY,OAAO,MAAM;CAC/B,UAAU,UAAU;CACpB,MAAM,cAAc,OAAO,QAAQ;CACnC,YAAY,UAAU;CAEtB,MAAM,WAAW,YAAY,YAAY;EACrC,MAAM,IAAI,UAAU;EACpB,IAAI,CAAC,GAAG,SAAS;GAAE,WAAW,KAAK;GAAG;EAAQ;EAC9C,IAAI;GACA,MAAM,MAAM,MAAM,EAAE,QAAQ,SAAS;GACrC,QAAQ,IAAI,IAAI;EACpB,SAAS,GAAY;GACjB,YAAY,QAAQ,KAAK;IACrB,MAAM;IACN,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;GACtD,CAAC;EACL,UAAU;GACN,WAAW,KAAK;EACpB;CACJ,GAAG,CAAC,CAAC;CAEL,gBAAgB;EAAE,SAAS;CAAG,GAAG,CAAC,QAAQ,CAAC;CAE3C,MAAM,eAAe,OAAO,OAAe;EACvC,MAAM,IAAI,UAAU;EACpB,IAAI,CAAC,GAAG,SAAS;EACjB,YAAY,EAAE;EACd,IAAI;GACA,MAAM,EAAE,QAAQ,UAAU,EAAE;GAC5B,YAAY,QAAQ,KAAK;IAAE,MAAM;IAAW,SAAS;GAAkB,CAAC;GACxE,MAAM,SAAS;GACf,IAAI,eAAe,IAAI,cAAc,IAAI;EAC7C,SAAS,GAAY;GACjB,YAAY,QAAQ,KAAK;IAAE,MAAM;IAAS,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;GAAE,CAAC;EACnG,UAAU;GAAE,YAAY,IAAI;EAAG;CACnC;CAEA,MAAM,iBAAiB,kBAAoC;EACvD,cAAc,KAAK;EACnB,cAAc,aAAa;EAC3B,SAAS;CACb;CAEA,MAAM,cAAc,KAAK,MAAK,MAAK,EAAE,OAAO,UAAU;CACtD,MAAM,aAAa,KAAK,QAAO,MAAK,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;CAClE,MAAM,eAAe,KAAK,QAAO,MAAK,EAAE,cAAc,UAAU,CAAC,CAAC;CAElE,IAAI,SAAS,OAAO,oBAAC,OAAD;EAAK,WAAU;YAA0C,oBAAC,kBAAD,CAAkB,CAAA;CAAM,CAAA;CAErG,OACI,qBAAA,UAAA,EAAA,UAAA;EACI,qBAAC,OAAD;GAAK,WAAU;aAAf,CAEI,qBAAC,OAAD;IAAK,WAAW,IAAI,yDAAyD,kBAAkB;cAA/F,CACI,qBAAC,OAAD;KAAK,WAAW,IAAI,yGAAyG,kBAAkB;eAA/I,CACI,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACI,oBAAC,cAAD;QAAc,MAAM,SAAS;QAAU,WAAU;OAAe,CAAA;OAChE,oBAAC,YAAD;QAAY,SAAQ;QAAY,WAAU;kBAAgB;OAAoB,CAAA;OAC9E,oBAAC,MAAD;QAAM,MAAK;QAAW,WAAU;kBAA6E,WAAW;OAAa,CAAA;MACpI;SACL,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACI,oBAAC,YAAD;OAAY,MAAK;OAAQ,SAAS;OAAU,OAAM;iBAAU,oBAAC,eAAD,EAAe,MAAM,SAAS,SAAU,CAAA;MAAa,CAAA,GACjH,oBAAC,QAAD;OAAQ,MAAK;OAAQ,OAAM;OAAU,eAAe,cAAc,IAAI;OAAG,WAAW,oBAAC,UAAD,EAAS,MAAM,SAAS,SAAU,CAAA;iBAAG;MAEjH,CAAA,CACP;OACJ;QACL,qBAAC,OAAD;KAAK,WAAU;eAAf;MACK,WAAW,WAAW,KAAK,aAAa,WAAW,KAChD,qBAAC,OAAD;OAAK,WAAU;iBAAf;QACI,oBAAC,cAAD;SAAc,MAAM,SAAS;SAAQ,WAAU;QAAyC,CAAA;QACxF,oBAAC,YAAD;SAAY,SAAQ;SAAQ,OAAM;mBAAY;QAA2B,CAAA;QACzE,oBAAC,YAAD;SAAY,SAAQ;SAAU,OAAM;mBAAW;QAAoE,CAAA;OAClH;;MAER,WAAW,KAAI,QACZ,oBAAC,aAAD;OAA0B,QAAQ;OAAK,UAAU,eAAe,IAAI;OAAI,eAAe,cAAc,IAAI,EAAE;MAAG,GAA5F,IAAI,EAAwF,CACjH;MACA,aAAa,SAAS,KACnB,qBAAA,UAAA,EAAA,UAAA,CACI,oBAAC,OAAD;OAAK,WAAU;iBACX,oBAAC,YAAD;QAAY,SAAQ;QAAU,OAAM;QAAW,WAAU;kBAAmD;OAA6B,CAAA;MACxI,CAAA,GACJ,aAAa,KAAI,QACd,oBAAC,aAAD;OAA0B,QAAQ;OAAK,UAAU,eAAe,IAAI;OAAI,eAAe,cAAc,IAAI,EAAE;MAAG,GAA5F,IAAI,EAAwF,CACjH,CACH,EAAA,CAAA;KAEL;MACJ;OAGL,oBAAC,OAAD;IAAK,WAAU;cACV,CAAC,cACE,oBAAC,OAAD;KAAK,WAAU;eACX,oBAAC,YAAD;MAAY,SAAQ;MAAQ,OAAM;gBAAW;KAA6C,CAAA;IACzF,CAAA,IAEL,qBAAA,UAAA,EAAA,UAAA;KAEI,qBAAC,OAAD;MAAK,WAAW,IAAI,kGAAkG,kBAAkB;gBAAxI,CACI,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,cAAD;QAAc,MAAM,SAAS;QAAO,WAAU;OAAwB,CAAA,GACtE,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACI,qBAAC,OAAD;SAAK,WAAU;mBAAf,CACI,oBAAC,YAAD;UAAY,SAAQ;UAAY,WAAU;oBAA0B,YAAY;SAAiB,CAAA,GAChG,YAAY,SAAS,oBAAC,WAAD,CAAW,CAAA,CAChC;YACL,qBAAC,YAAD;SAAY,SAAQ;SAAU,OAAM;SAAY,WAAU;mBAA1D,CAAmF,YAAY,YAAW,KAAe;UACxH;SACJ;UACL,oBAAC,OAAD;OAAK,WAAU;iBACV,CAAC,YAAY,cACV,oBAAC,QAAD;QACI,MAAK;QACL,OAAM;QACN,SAAQ;QACR,eAAe,iBAAiB,WAAW;QAC3C,UAAU,aAAa,YAAY;QACnC,WAAW,aAAa,YAAY,KAAK,oBAAC,kBAAD,EAAkB,MAAK,WAAW,CAAA,IAAI,oBAAC,YAAD,EAAY,MAAM,SAAS,SAAU,CAAA;kBACvH;OAEO,CAAA;MAEX,CAAA,CACJ;;KAGL,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACI,qBAAC,OAAD;OAAK,WAAU;iBAAf;QACI,oBAAC,UAAD;SAAU,OAAM;SAAS,OAAO,UAAU,WAAW,CAAC,CAAC;SAAO,WAAW,UAAU,WAAW,CAAC,CAAC;QAAO,CAAA;QACvG,oBAAC,UAAD;SAAU,OAAM;SAAU,OAAO,eAAe,YAAY,UAAU;QAAG,CAAA;QACzE,oBAAC,UAAD;SAAU,OAAM;SAAY,OAAO,eAAe,YAAY,YAAY;QAAG,CAAA;QAC7E,oBAAC,UAAD;SAAU,OAAM;SAAU,OAAO,YAAY,aAAa,eAAe,YAAY,UAAU,IAAI;QAAS,CAAA;OAC3G;UACL,qBAAC,OAAD;OAAK,WAAU;iBAAf;QACI,oBAAC,UAAD;SACI,OAAM;SACN,OAAO,YAAY,QAAQ,UAAU;SACrC,WAAW,YAAY,QAAQ,uCAAuC,KAAA;QACzE,CAAA;QACD,oBAAC,UAAD;SAAU,OAAM;SAAa,OAAO,YAAY,aAAa,GAAG,YAAY,WAAW,UAAU;QAAwB,CAAA;QACzH,oBAAC,UAAD;SAAU,OAAM;SAAa,OAAO,YAAY;SAAY,MAAA;QAAK,CAAA;OAChE;QACJ;;KAGL,qBAAC,OAAD;MAAK,WAAW,IAAI,2EAA2E,kBAAkB;gBAAjH,CACI,oBAAC,YAAD;OAAY,SAAQ;OAAY,WAAU;iBAA4B;MAAuB,CAAA,GAC7F,oBAAC,MAAD;OAAM,MAAK;OAAW,WAAU;iBAC3B,YAAY,YAAY;MACvB,CAAA,CACL;;KACL,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACK,YAAY,SACT,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACI,oBAAC,YAAD;QAAY,MAAM,SAAS;QAAU,WAAU;OAAuD,CAAA,GACtG,qBAAC,YAAD;QAAY,SAAQ;QAAU,WAAU;kBAAxC;SAAsG;SAC/E,oBAAC,QAAD;UAAM,WAAU;oBAAgB;SAAgB,CAAA;SAAC;SAEpE,oBAAC,QAAD;UAAM,WAAU;oBAAY;SAAoB,CAAA;SAAC;QACzC;SACX;UAER,YAAY,YAAY,WAAW,IAChC,oBAAC,YAAD;OAAY,SAAQ;OAAQ,OAAM;iBAAW;MAAqC,CAAA,IAElF,oBAAC,OAAD;OAAK,WAAU;iBACV,YAAY,YAAY,KAAK,MAAM,QAChC,qBAAC,OAAD;QAAe,WAAW,IAAI,uDAAuD,kBAAkB;kBAAvG,CACI,qBAAC,OAAD;SAAK,WAAU;mBAAf,CACI,oBAAC,YAAD;UAAY,SAAQ;UAAQ,WAAU;oBACjC,cAAc,KAAK,UAAU;SACtB,CAAA,GACZ,oBAAC,YAAD;UAAY,SAAQ;UAAU,OAAM;UAAY,WAAU;oBACrD,eAAe,KAAK,UAAU;SACvB,CAAA,CACX;YACL,oBAAC,OAAD;SAAK,WAAU;mBACV,KAAK,WAAW,KAAI,OACjB,oBAAC,MAAD;UAAe,MAAK;UAAW,WAAW,IACtC,OAAO,UAAU,gFACjB,OAAO,WAAW,oEAClB,OAAO,YAAY,8DACvB;oBAAI;SAAS,GAJF,EAIE,CAChB;QACA,CAAA,CACJ;UAlBK,GAkBL,CACR;MACA,CAAA,CAER;;IACP,EAAA,CAAA;GAEL,CAAA,CACJ;;EAGL,qBAAC,QAAD;GACI,MAAM,kBAAkB;GACxB,eAAe,SAAS;IACpB,IAAI,CAAC,QAAQ,CAAC,UAAU,iBAAiB,IAAI;GACjD;aAJJ;IAMI,oBAAC,aAAD;KAAa,QAAA;eAAO;IAAgC,CAAA;IACpD,qBAAC,eAAD,EAAA,UAAA,CACI,qBAAC,YAAD;KAAY,SAAQ;KAAY,WAAU;eAA1C;MAA+D;MAC7C,eAAe;MAAK;KAC1B;QACZ,oBAAC,YAAD;KAAY,SAAQ;KAAQ,OAAM;eAAY;IAElC,CAAA,CACD,EAAA,CAAA;IACf,qBAAC,eAAD,EAAA,UAAA,CACI,oBAAC,QAAD;KACI,SAAQ;KACR,eAAe,iBAAiB,IAAI;KACpC,UAAU,aAAa;eAC1B;IAEO,CAAA,GACR,oBAAC,QAAD;KACI,OAAM;KACN,UAAU,aAAa;KACvB,WAAW,aAAa,OAAO,oBAAC,kBAAD,EAAkB,MAAK,WAAW,CAAA,IAAI,oBAAC,YAAD,EAAY,MAAM,SAAS,SAAU,CAAA;KAC1G,SAAS,YAAY;MACjB,IAAI,CAAC,eAAe;MACpB,MAAM,aAAa,cAAc,EAAE;MACnC,iBAAiB,IAAI;KACzB;eACH;IAEO,CAAA,CACG,EAAA,CAAA;GACX;;EAGP,cACG,oBAAC,oBAAD;GACI,eAAe,cAAc,KAAK;GAClC,WAAW;EACd,CAAA;EAIJ,cACG,oBAAC,qBAAD;GACI,eAAe;GACf,eAAe,cAAc,IAAI;EACpC,CAAA;CAEP,EAAA,CAAA;AAEV;;;;;;;;AAaA,SAAS,YAAY;CACjB,OACI,oBAAC,SAAD;EAAS,OAAM;YACX,qBAAC,MAAD;GACI,MAAK;GACL,WAAU;aAFd,CAII,oBAAC,YAAD,EAAY,MAAM,GAAI,CAAA,GAAC,OAErB;;CACD,CAAA;AAEjB;AAEA,SAAS,YAAY,EAAE,QAAQ,UAAU,WAA6E;CAClH,MAAM,SAAS,UAAU,MAAM;CAC/B,OACI,qBAAC,OAAD;EACa;EACT,WAAW,IACP,gFACA,WACM,4DACA,gDACV;YAPJ;GASI,oBAAC,OAAD,EAAK,WAAW,IAAI,iCAChB,OAAO,UAAU,WAAW,mBAC5B,OAAO,UAAU,YAAY,iBAAiB,YAClD,EAAG,CAAA;GACH,qBAAC,OAAD;IAAK,WAAU;cAAf,CACI,qBAAC,OAAD;KAAK,WAAU;eAAf,CACI,oBAAC,YAAD;MAAY,SAAQ;MAAQ,WAAU;gBAAoC,OAAO;KAAiB,CAAA,GACjG,OAAO,SAAS,oBAAC,WAAD,CAAW,CAAA,CAC3B;QACL,qBAAC,YAAD;KAAY,SAAQ;KAAU,OAAM;KAAY,WAAU;eAA1D,CAA4F,OAAO,YAAW,KAAe;MAC5H;;GACL,oBAAC,OAAD;IAAK,WAAU;cACX,oBAAC,YAAD;KAAY,SAAQ;KAAU,OAAM;KAAW,WAAU;eAAe,kBAAkB,OAAO,WAAW;IAAc,CAAA;GACzH,CAAA;EACJ;;AAEb;AAMA,SAAS,SAAS,EAAE,OAAO,OAAO,MAAM,aAAmF;CACvH,OACI,qBAAC,OAAD;EAAK,WAAW,IAAI,4DAA4D,kBAAkB;YAAlG,CACI,oBAAC,YAAD;GAAY,SAAQ;GAAU,OAAM;GAAY,WAAU;aAAoD;EAAkB,CAAA,GAChI,oBAAC,YAAD;GAAY,SAAQ;GAAQ,WAAW,IACnC,oCACA,QAAQ,aACR,SACJ;aAAI;EAAkB,CAAA,CACrB;;AAEb;AAMA,SAAS,oBAAoB,EAAE,eAAe,WAAqE;CAC/G,MAAM,WAAW,sBAAsB;CACvC,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAE1C,MAAM,aAAa,YAAY;EAC3B,IAAI;GACA,MAAM,UAAU,UAAU,UAAU,cAAc,GAAG;GACrD,UAAU,IAAI;GACd,SAAS,KAAK;IAAE,MAAM;IAAW,SAAS;GAA8B,CAAC;GACzE,iBAAiB,UAAU,KAAK,GAAG,GAAI;EAC3C,QAAQ;GACJ,SAAS,KAAK;IAAE,MAAM;IAAS,SAAS;GAAiB,CAAC;EAC9D;CACJ;CAEA,OACI,qBAAC,QAAD;EAAQ,MAAA;EAAK,eAAe,SAAS;GAAE,IAAI,CAAC,MAAM,QAAQ;EAAG;EAAG,UAAS;YAAzE;GACI,oBAAC,aAAD,EAAA,UACI,qBAAC,OAAD;IAAK,WAAU;cAAf,CACI,oBAAC,iBAAD;KAAiB,MAAM,SAAS;KAAO,WAAU;IAAmB,CAAA,GAAC,iBAEpE;MACI,CAAA;GACb,qBAAC,eAAD,EAAA,UAAA;IACI,qBAAC,OAAD;KAAK,WAAU;eAAf,CACI,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACI,oBAAC,iBAAD;OAAiB,MAAM,SAAS;OAAU,WAAU;MAAqC,CAAA,GACzF,oBAAC,YAAD;OAAY,SAAQ;OAAU,WAAU;iBAAmD;MAE/E,CAAA,CACX;SACL,oBAAC,YAAD;MAAY,SAAQ;MAAU,WAAU;gBAAqC;KAEjE,CAAA,CACX;;IAEL,qBAAC,OAAD;KAAK,WAAW,IAAI,mFAAmF,kBAAkB;eAAzH,CACI,oBAAC,QAAD;MAAM,WAAU;gBACX,cAAc;KACb,CAAA,GACN,oBAAC,SAAD;MAAS,OAAO,SAAS,YAAY;gBACjC,oBAAC,YAAD;OAAY,MAAK;OAAQ,SAAS;iBAC7B,SACK,oBAAC,iBAAD;QAAiB,MAAM,SAAS;QAAU,WAAU;OAAmB,CAAA,IACvE,oBAAC,UAAD,EAAU,MAAM,SAAS,SAAU,CAAA;MAEjC,CAAA;KACP,CAAA,CACR;;IAEL,qBAAC,OAAD;KAAK,WAAU;eAAf;MACI,qBAAC,YAAD;OAAY,SAAQ;OAAU,OAAM;iBAApC;QACI,oBAAC,UAAD,EAAA,UAAQ,QAAa,CAAA;QAAC;QAAE,cAAc;OAC9B;;MACZ,qBAAC,YAAD;OAAY,SAAQ;OAAU,OAAM;iBAApC;QACI,oBAAC,UAAD,EAAA,UAAQ,UAAe,CAAA;QAAC;QAAE,kBAAkB,cAAc,WAAW;OAC7D;;MACX,cAAc,SACX,qBAAC,YAAD;OAAY,SAAQ;OAAU,WAAU;iBAAxC,CACI,oBAAC,YAAD;QAAY,MAAM,SAAS;QAAU,WAAU;OAAW,CAAA,GAC1D,qBAAC,QAAD,EAAA,UAAA,CAAM,oBAAC,UAAD,EAAA,UAAQ,qBAA0B,CAAA,GAAC,oDAAwD,EAAA,CAAA,CACzF;;KAEf;;GACM,EAAA,CAAA;GACf,oBAAC,eAAD,EAAA,UACI,oBAAC,QAAD;IAAQ,OAAM;IAAU,SAAS;cAAS;GAAY,CAAA,EAC3C,CAAA;EACX;;AAEhB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary of an API key's permission entries.
|
|
3
|
+
*
|
|
4
|
+
* A permission entry is `{ collection, operations }`, but `collection` is not
|
|
5
|
+
* only a collection: the same field addresses three namespaces, and the guard
|
|
6
|
+
* that enforces them lives in `@rebasepro/server`
|
|
7
|
+
* (`auth/api-keys/api-key-permission-guard.ts`):
|
|
8
|
+
*
|
|
9
|
+
* - `"*"` — every collection, every custom function, and storage
|
|
10
|
+
* - `"storage"` — the storage routes
|
|
11
|
+
* - `"functions"` — every custom function, including the function index
|
|
12
|
+
* - `"functions/<name>"`— one named function
|
|
13
|
+
* - anything else — the collection with that slug
|
|
14
|
+
*
|
|
15
|
+
* The UI used to label the field "Collection slug or *", which made two of
|
|
16
|
+
* those namespaces undiscoverable and made `"*"` read as "all collections"
|
|
17
|
+
* when it also hands over storage and every function. This module is the one
|
|
18
|
+
* place that knows the mapping, so the picker, the row description, the grant
|
|
19
|
+
* summary and the detail panel cannot drift from each other.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import type { ApiKeyPermission } from "@rebasepro/types";
|
|
24
|
+
/** Grants every collection, every function, and storage. */
|
|
25
|
+
export declare const RESOURCE_EVERYTHING = "*";
|
|
26
|
+
/** Grants the storage routes. */
|
|
27
|
+
export declare const RESOURCE_STORAGE = "storage";
|
|
28
|
+
/** Grants every custom function. */
|
|
29
|
+
export declare const RESOURCE_ALL_FUNCTIONS = "functions";
|
|
30
|
+
/** Prefix addressing a single custom function. */
|
|
31
|
+
export declare const FUNCTION_PREFIX = "functions/";
|
|
32
|
+
export type ResourceKind = "everything" | "storage" | "all-functions" | "function" | "collection";
|
|
33
|
+
export interface ParsedResource {
|
|
34
|
+
kind: ResourceKind;
|
|
35
|
+
/** The function name for `"function"`, the slug for `"collection"`. */
|
|
36
|
+
name: string;
|
|
37
|
+
}
|
|
38
|
+
/** Classify a raw `collection` field into the namespace it addresses. */
|
|
39
|
+
export declare function parseResource(collection: string): ParsedResource;
|
|
40
|
+
/**
|
|
41
|
+
* Short label for a resource — what a picker or a chip shows.
|
|
42
|
+
*
|
|
43
|
+
* Deliberately not the raw value: `"*"` alone is the thing nobody could read.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resourceLabel(collection: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* The resource as a sentence fragment, for "this key can read <fragment>".
|
|
48
|
+
*
|
|
49
|
+
* `"everything"` spells out all three namespaces, because that is exactly the
|
|
50
|
+
* fact the old `*` input hid.
|
|
51
|
+
*/
|
|
52
|
+
export declare function resourcePhrase(collection: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* One plain sentence per permission entry: what the key will actually be able
|
|
55
|
+
* to do. An entry with no operations selected grants nothing and says so,
|
|
56
|
+
* rather than being silently dropped at submit time.
|
|
57
|
+
*/
|
|
58
|
+
export declare function grantSentence(perm: ApiKeyPermission): string;
|
|
59
|
+
/**
|
|
60
|
+
* Dense one-line summary for list rows and the created-key confirmation.
|
|
61
|
+
*
|
|
62
|
+
* The wildcard wins over everything else in the array, because the guard
|
|
63
|
+
* returns on the first match — a key holding `*` is a full-access key no
|
|
64
|
+
* matter what else is listed beside it.
|
|
65
|
+
*/
|
|
66
|
+
export declare function permissionSummary(perms: ApiKeyPermission[]): string;
|
package/dist/index.es.js
CHANGED
|
@@ -559,7 +559,7 @@ var BranchesView = lazyChunk(() => import("./BranchesView-CM3B-ER3.js").then((m)
|
|
|
559
559
|
var BackupsView = lazyChunk(() => import("./BackupsView-DNS6LdVg.js").then((m) => ({ default: m.BackupsView })));
|
|
560
560
|
var ApiExplorer = lazyChunk(() => import("./ApiExplorer-9iwGvNnt.js").then((m) => ({ default: m.ApiExplorer })));
|
|
561
561
|
var LogsExplorer = lazyChunk(() => import("./LogsExplorer-DI8SVpya.js").then((m) => ({ default: m.LogsExplorer })));
|
|
562
|
-
var ApiKeysView = lazyChunk(() => import("./ApiKeysView-
|
|
562
|
+
var ApiKeysView = lazyChunk(() => import("./ApiKeysView-DiCurTEU.js").then((m) => ({ default: m.ApiKeysView })));
|
|
563
563
|
/**
|
|
564
564
|
* Declarative component to configure the Studio in Rebase.
|
|
565
565
|
* Renders nothing — purely registers config into the RebaseRegistry.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/studio",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.17.
|
|
4
|
+
"version": "0.17.3-canary.gdd23447",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.es.js",
|
|
7
7
|
"module": "./dist/index.es.js",
|
|
@@ -16,19 +16,19 @@
|
|
|
16
16
|
"pgsql-ast-parser": "12.0.2",
|
|
17
17
|
"prism-react-renderer": "^2.4.1",
|
|
18
18
|
"react-dropzone": "^19.1.1",
|
|
19
|
-
"@rebasepro/cms-types": "0.17.
|
|
20
|
-
"@rebasepro/client": "0.17.
|
|
21
|
-
"@rebasepro/types": "0.17.
|
|
22
|
-
"@rebasepro/
|
|
23
|
-
"@rebasepro/
|
|
24
|
-
"@rebasepro/
|
|
25
|
-
"@rebasepro/ui": "0.17.
|
|
19
|
+
"@rebasepro/cms-types": "0.17.3-canary.gdd23447",
|
|
20
|
+
"@rebasepro/client": "0.17.3-canary.gdd23447",
|
|
21
|
+
"@rebasepro/types": "0.17.3-canary.gdd23447",
|
|
22
|
+
"@rebasepro/common": "0.17.3-canary.gdd23447",
|
|
23
|
+
"@rebasepro/utils": "0.17.3-canary.gdd23447",
|
|
24
|
+
"@rebasepro/app": "0.17.3-canary.gdd23447",
|
|
25
|
+
"@rebasepro/ui": "0.17.3-canary.gdd23447"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react": ">=19.2.7",
|
|
29
29
|
"react-dom": ">=19.2.7",
|
|
30
30
|
"react-router": "^8.3.0",
|
|
31
|
-
"@rebasepro/cms": "0.17.
|
|
31
|
+
"@rebasepro/cms": "0.17.3-canary.gdd23447"
|
|
32
32
|
},
|
|
33
33
|
"peerDependenciesMeta": {
|
|
34
34
|
"@rebasepro/cms": {
|