@rebasepro/studio 0.6.1 → 0.8.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.
Files changed (37) hide show
  1. package/dist/ApiKeysView-D-_FSlNL.js +684 -0
  2. package/dist/ApiKeysView-D-_FSlNL.js.map +1 -0
  3. package/dist/{BranchesView-DncIRcZt.js → BranchesView-Dlg78EQ8.js} +4 -5
  4. package/dist/{BranchesView-DncIRcZt.js.map → BranchesView-Dlg78EQ8.js.map} +1 -1
  5. package/dist/{JSEditor-BhAbEjCP.js → JSEditor-Ca4XYGRp.js} +7 -7
  6. package/dist/JSEditor-Ca4XYGRp.js.map +1 -0
  7. package/dist/{LogsExplorer-CqtKILj8.js → LogsExplorer-J4xfsuv3.js} +49 -83
  8. package/dist/LogsExplorer-J4xfsuv3.js.map +1 -0
  9. package/dist/{RLSEditor-DpF1u9EC.js → RLSEditor-BM64laoW.js} +294 -107
  10. package/dist/RLSEditor-BM64laoW.js.map +1 -0
  11. package/dist/{SQLEditor-BLuq_zDM.js → SQLEditor-CuAhR-zr.js} +4 -4
  12. package/dist/{SQLEditor-BLuq_zDM.js.map → SQLEditor-CuAhR-zr.js.map} +1 -1
  13. package/dist/{SchemaVisualizer-BJK2u3C0.js → SchemaVisualizer-OibKoD3g.js} +2 -2
  14. package/dist/{SchemaVisualizer-BJK2u3C0.js.map → SchemaVisualizer-OibKoD3g.js.map} +1 -1
  15. package/dist/{StorageView-CvrnHmDG.js → StorageView-BMhD29YO.js} +33 -5
  16. package/dist/StorageView-BMhD29YO.js.map +1 -0
  17. package/dist/components/ApiKeys/ApiKeysView.d.ts +2 -0
  18. package/dist/index.es.js +141 -94
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +1221 -317
  21. package/dist/index.umd.js.map +1 -1
  22. package/package.json +8 -8
  23. package/src/components/ApiKeys/ApiKeysView.tsx +580 -0
  24. package/src/components/Branches/BranchesView.tsx +3 -4
  25. package/src/components/JSEditor/JSEditorSidebar.tsx +3 -3
  26. package/src/components/JSEditor/JSMonacoEditor.tsx +3 -3
  27. package/src/components/LogsExplorer/LogsExplorer.tsx +46 -84
  28. package/src/components/RLSEditor/RLSEditor.tsx +354 -117
  29. package/src/components/RebaseStudio.tsx +10 -1
  30. package/src/components/SQLEditor/SchemaBrowser.tsx +3 -3
  31. package/src/components/StorageView/StorageView.tsx +43 -4
  32. package/src/components/StudioHomePage.tsx +144 -85
  33. package/src/utils/pgColumnToProperty.ts +2 -2
  34. package/dist/JSEditor-BhAbEjCP.js.map +0 -1
  35. package/dist/LogsExplorer-CqtKILj8.js.map +0 -1
  36. package/dist/RLSEditor-DpF1u9EC.js.map +0 -1
  37. package/dist/StorageView-CvrnHmDG.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiKeysView-D-_FSlNL.js","names":[],"sources":["../src/components/ApiKeys/ApiKeysView.tsx"],"sourcesContent":["\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 Tooltip,\n Typography,\n TextField,\n Select,\n SelectItem,\n Checkbox,\n CopyIcon,\n PlusIcon as AddIcon,\n Trash2Icon as DeleteIcon,\n AlertCircleIcon,\n CheckCircleIcon\n} from \"@rebasepro/ui\";\nimport { useRebaseClient, useSnackbarController } from \"@rebasepro/core\";\nimport type { RebaseClient } from \"@rebasepro/types\";\n\n/* ═══════════════════════════════════════════════════════════════\n Types — mirrors server-core api-key-types.ts\n ═══════════════════════════════════════════════════════════════ */\n\ninterface ApiKeyPermission {\n collection: string;\n operations: (\"read\" | \"write\" | \"delete\")[];\n}\n\ninterface ApiKeyMasked {\n id: string;\n name: string;\n key_prefix: string;\n permissions: ApiKeyPermission[];\n rate_limit: number | null;\n created_by: string;\n created_at: string;\n updated_at: string;\n last_used_at: string | null;\n expires_at: string | null;\n revoked_at: string | null;\n}\n\ninterface ApiKeyWithSecret extends ApiKeyMasked {\n key: string;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Helpers\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 permissionSummary(perms: ApiKeyPermission[]): string {\n if (perms.length === 0) return \"No permissions\";\n const wildcard = perms.find(p => p.collection === \"*\");\n if (wildcard) {\n return `All collections (${wildcard.operations.join(\", \")})`;\n }\n if (perms.length === 1) {\n return `${perms[0].collection} (${perms[0].operations.join(\", \")})`;\n }\n return `${perms.length} collections`;\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\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 <Typography variant=\"subtitle1\" className=\"font-semibold truncate\">{selectedKey.name}</Typography>\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={() => handleRevoke(selectedKey.id)}\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 gap-3 mt-3\">\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.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 <Typography variant=\"body2\" className=\"font-mono text-[13px] font-medium flex-1\">\n {perm.collection === \"*\" ? \"* (all collections)\" : perm.collection}\n </Typography>\n <div className=\"flex items-center gap-1\">\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 {/* 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\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 <Typography variant=\"body2\" className=\"truncate font-medium text-[13px]\">{apiKey.name}</Typography>\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 Create API Key Dialog\n ═══════════════════════════════════════════════════════════════ */\n\ninterface PermissionRow {\n collection: string;\n read: boolean;\n write: boolean;\n delete: boolean;\n}\n\nfunction CreateApiKeyDialog({ onClose, onCreated }: { onClose: () => void; onCreated: (key: ApiKeyWithSecret) => void }) {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const [name, setName] = useState(\"\");\n const [permissions, setPermissions] = useState<PermissionRow[]>([{ collection: \"*\", read: true, write: false, delete: false }]);\n const [rateLimit, setRateLimit] = useState(\"\");\n const [expiresIn, setExpiresIn] = useState(\"never\");\n const [creating, setCreating] = useState(false);\n\n const addRow = () => setPermissions([...permissions, { collection: \"\", read: true, write: false, delete: false }]);\n const removeRow = (idx: number) => setPermissions(permissions.filter((_, i) => i !== idx));\n\n const updateRow = (idx: number, field: keyof PermissionRow, value: string | boolean) => {\n setPermissions(permissions.map((row, i) => i === idx ? { ...row, [field]: value } : row));\n };\n\n const handleCreate = async () => {\n if (!client?.apiKeys || !name.trim()) return;\n const apiPerms: ApiKeyPermission[] = permissions\n .filter(r => r.collection.trim())\n .map(r => ({\n collection: r.collection.trim(),\n operations: [\n ...(r.read ? [\"read\" as const] : []),\n ...(r.write ? [\"write\" as const] : []),\n ...(r.delete ? [\"delete\" as const] : [])\n ]\n }))\n .filter(p => p.operations.length > 0);\n\n if (apiPerms.length === 0) {\n snackbar.open({ type: \"error\", message: \"At least one permission is required\" });\n return;\n }\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: apiPerms,\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 { setCreating(false); }\n };\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open) onClose(); }} maxWidth=\"lg\">\n <DialogTitle>Create API Key</DialogTitle>\n <DialogContent className=\"space-y-4\">\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 <div>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[11px] uppercase tracking-wider font-medium mb-2 block\">\n Permissions\n </Typography>\n <div className=\"space-y-2\">\n {permissions.map((row, idx) => (\n <div key={idx} className={cls(\"flex items-center gap-2 p-2 rounded-lg border\", defaultBorderMixin)}>\n <TextField\n size=\"small\"\n value={row.collection}\n onChange={(e) => updateRow(idx, \"collection\", e.target.value)}\n placeholder=\"Collection slug or *\"\n className=\"flex-1\"\n />\n <label className=\"flex items-center gap-1 text-xs cursor-pointer\">\n <Checkbox size=\"small\" checked={row.read} onCheckedChange={(v) => updateRow(idx, \"read\", !!v)}/>\n <span className=\"text-emerald-600 dark:text-emerald-400\">read</span>\n </label>\n <label className=\"flex items-center gap-1 text-xs cursor-pointer\">\n <Checkbox size=\"small\" checked={row.write} onCheckedChange={(v) => updateRow(idx, \"write\", !!v)}/>\n <span className=\"text-blue-600 dark:text-blue-400\">write</span>\n </label>\n <label className=\"flex items-center gap-1 text-xs cursor-pointer\">\n <Checkbox size=\"small\" checked={row.delete} onCheckedChange={(v) => updateRow(idx, \"delete\", !!v)}/>\n <span className=\"text-red-600 dark:text-red-400\">delete</span>\n </label>\n {permissions.length > 1 && (\n <IconButton size=\"small\" onClick={() => removeRow(idx)}>\n <DeleteIcon size={iconSize.smallest}/>\n </IconButton>\n )}\n </div>\n ))}\n </div>\n <Button size=\"small\" variant=\"text\" onClick={addRow} className=\"mt-1\" startIcon={<AddIcon size={iconSize.smallest}/>}>\n Add collection\n </Button>\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <Select label=\"Expires\" value={expiresIn} onValueChange={setExpiresIn} size=\"small\" renderValue={(v) =>\n v === \"never\" ? \"Never\" :\n v === \"7d\" ? \"7 days\" :\n v === \"30d\" ? \"30 days\" :\n v === \"90d\" ? \"90 days\" : \"1 year\"\n }>\n <SelectItem value=\"never\">Never</SelectItem>\n <SelectItem value=\"7d\">7 days</SelectItem>\n <SelectItem value=\"30d\">30 days</SelectItem>\n <SelectItem value=\"90d\">90 days</SelectItem>\n <SelectItem value=\"1y\">1 year</SelectItem>\n </Select>\n </div>\n <div className=\"flex-1\">\n <TextField\n label=\"Rate Limit (per 15 min)\"\n value={rateLimit}\n onChange={(e) => setRateLimit(e.target.value.replace(/\\D/g, \"\"))}\n placeholder=\"Default: 1000\"\n size=\"small\"\n />\n </div>\n </div>\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={onClose}>Cancel</Button>\n <Button\n color=\"primary\"\n onClick={handleCreate}\n disabled={creating || !name.trim()}\n startIcon={creating ? <CircularProgress size=\"smallest\"/> : undefined}\n >\n Create Key\n </Button>\n </DialogActions>\n </Dialog>\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&apos;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>Permissions:</strong> {permissionSummary(keyWithSecret.permissions)}\n </Typography>\n </div>\n </DialogContent>\n <DialogActions>\n <Button color=\"primary\" onClick={onClose}>Done</Button>\n </DialogActions>\n </Dialog>\n );\n}\n"],"mappings":"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"}
@@ -393,13 +393,12 @@ function BranchesView() {
393
393
  }) }),
394
394
  branches.length > 0 && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(Select, {
395
395
  label: "Source Database",
396
- value: sourceBranch ?? "",
397
- onValueChange: (v) => setSourceBranch(v || void 0),
396
+ value: sourceBranch ?? "__main__",
397
+ onValueChange: (v) => setSourceBranch(v === "__main__" ? void 0 : v),
398
398
  placeholder: "Default (main database)",
399
399
  size: "small",
400
- renderValue: (v) => v || "Default (main database)",
401
400
  children: [/* @__PURE__ */ jsx(SelectItem, {
402
- value: "",
401
+ value: "__main__",
403
402
  children: "Default (main database)"
404
403
  }), branches.map((b) => /* @__PURE__ */ jsx(SelectItem, {
405
404
  value: b.name,
@@ -458,4 +457,4 @@ function StatCard({ label, value, mono }) {
458
457
  //#endregion
459
458
  export { BranchesView };
460
459
 
461
- //# sourceMappingURL=BranchesView-DncIRcZt.js.map
460
+ //# sourceMappingURL=BranchesView-Dlg78EQ8.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BranchesView-DncIRcZt.js","names":[],"sources":["../src/components/Branches/BranchesView.tsx"],"sourcesContent":["\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport {\n Alert,\n Button,\n Chip,\n CircularProgress,\n cls,\n CopyIcon,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n GitBranchIcon,\n IconButton,\n iconSize,\n Paper,\n PlusIcon,\n RefreshCwIcon,\n Select,\n SelectItem,\n TextField,\n Trash2Icon,\n Typography\n} from \"@rebasepro/ui\";\nimport { useRebaseContext, useSnackbarController, ConfirmationDialog } from \"@rebasepro/core\";\nimport { isBranchAdmin } from \"@rebasepro/types\";\nimport type { BranchInfo } from \"@rebasepro/types\";\n\nfunction formatSize(bytes: number | undefined): string {\n if (bytes === undefined || bytes === null) return \"—\";\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n}\n\nfunction formatRelative(date: Date | string | undefined): string {\n if (!date) return \"—\";\n const d = date instanceof Date ? date : new Date(date);\n if (isNaN(d.getTime())) return \"—\";\n const now = Date.now();\n const diff = now - d.getTime();\n if (diff < 60_000) return \"just now\";\n if (diff < 3_600_000) { const m = Math.round(diff / 60_000); return `${m}m ago`; }\n if (diff < 86_400_000) { const h = Math.round(diff / 3_600_000); return `${h}h ago`; }\n if (diff < 604_800_000) { const d2 = Math.round(diff / 86_400_000); return `${d2}d ago`; }\n return d.toLocaleDateString();\n}\n\nexport function BranchesView() {\n const { databaseAdmin } = useRebaseContext();\n const snackbar = useSnackbarController();\n\n const [branches, setBranches] = useState<BranchInfo[]>([]);\n const [loading, setLoading] = useState(true);\n const [selectedBranch, setSelectedBranch] = useState<string | null>(null);\n\n // Create dialog\n const [createOpen, setCreateOpen] = useState(false);\n const [newBranchName, setNewBranchName] = useState(\"\");\n const [sourceBranch, setSourceBranch] = useState<string | undefined>(undefined);\n const [creating, setCreating] = useState(false);\n\n // Delete confirm\n const [deleteTarget, setDeleteTarget] = useState<string | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n // Refs\n const snackbarRef = useRef(snackbar);\n snackbarRef.current = snackbar;\n\n const branchAdmin = isBranchAdmin(databaseAdmin) ? databaseAdmin : undefined;\n\n const loadBranches = useCallback(async () => {\n if (!branchAdmin) {\n setLoading(false);\n return;\n }\n try {\n const result = await branchAdmin.listBranches();\n setBranches(result);\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 }, [branchAdmin]);\n\n useEffect(() => {\n loadBranches();\n }, [loadBranches]);\n\n const handleCreate = async () => {\n if (!branchAdmin || !newBranchName.trim()) return;\n setCreating(true);\n try {\n await branchAdmin.createBranch(newBranchName.trim(), sourceBranch ? { source: sourceBranch } : undefined);\n snackbarRef.current.open({\n type: \"success\",\n message: `Branch \"${newBranchName.trim()}\" created successfully`\n });\n setCreateOpen(false);\n setNewBranchName(\"\");\n setSourceBranch(undefined);\n await loadBranches();\n } catch (e: unknown) {\n snackbarRef.current.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n } finally {\n setCreating(false);\n }\n };\n\n const handleDelete = async () => {\n if (!branchAdmin || !deleteTarget) return;\n setDeleting(true);\n try {\n await branchAdmin.deleteBranch(deleteTarget);\n snackbarRef.current.open({\n type: \"success\",\n message: `Branch \"${deleteTarget}\" deleted`\n });\n if (selectedBranch === deleteTarget) setSelectedBranch(null);\n setDeleteTarget(null);\n await loadBranches();\n } catch (e: unknown) {\n snackbarRef.current.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n } finally {\n setDeleting(false);\n }\n };\n\n // Not supported\n if (!branchAdmin) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full gap-4 text-center p-8\">\n <GitBranchIcon size={iconSize.large} className=\"text-surface-300 dark:text-surface-600\"/>\n <Typography variant=\"h6\" color=\"secondary\">Database Branching Not Available</Typography>\n <Typography variant=\"body2\" color=\"disabled\" className=\"max-w-md\">\n Branching requires a PostgreSQL backend with an admin connection configured.\n Set <code className=\"text-xs bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded font-mono\">adminConnectionString</code> in your server configuration.\n </Typography>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <CircularProgress/>\n </div>\n );\n }\n\n const selected = branches.find(b => b.name === selectedBranch);\n\n return (\n <div className=\"flex h-full w-full overflow-hidden bg-white dark:bg-surface-950\">\n {/* ── Branch 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 <GitBranchIcon size={iconSize.small} className=\"text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"font-semibold\">Branches</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">{branches.length}</Chip>\n </div>\n <div className=\"flex items-center gap-1\">\n <IconButton size=\"small\" onClick={loadBranches} title=\"Refresh\">\n <RefreshCwIcon size={iconSize.smallest}/>\n </IconButton>\n <IconButton size=\"small\" onClick={() => setCreateOpen(true)} title=\"Create branch\" className=\"text-primary\">\n <PlusIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto p-2 space-y-1\">\n {branches.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center h-full gap-3 text-center px-4\">\n <CopyIcon size={iconSize.small} className=\"text-surface-300 dark:text-surface-600\"/>\n <Typography variant=\"body2\" color=\"disabled\" className=\"text-[13px]\">\n No branches yet. Create one to start working with an isolated database copy.\n </Typography>\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={() => setCreateOpen(true)}\n startIcon={<PlusIcon size={iconSize.smallest}/>}\n >\n Create Branch\n </Button>\n </div>\n ) : (\n branches.map(branch => (\n <div\n key={branch.name}\n onClick={() => setSelectedBranch(branch.name)}\n className={cls(\n \"flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all\",\n selectedBranch === branch.name\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=\"w-2 h-2 rounded-full shrink-0 bg-emerald-500\"/>\n <div className=\"flex-1 min-w-0\">\n <Typography variant=\"body2\" className=\"truncate font-medium text-[13px]\">{branch.name}</Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate text-[11px]\">\n from {branch.parentDatabase} · {formatRelative(branch.createdAt)}\n </Typography>\n </div>\n {branch.sizeBytes !== undefined && (\n <Typography variant=\"caption\" color=\"disabled\" className=\"font-mono text-[10px] shrink-0\">\n {formatSize(branch.sizeBytes)}\n </Typography>\n )}\n </div>\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 {!selected ? (\n <div className=\"flex flex-col items-center justify-center h-full gap-3\">\n <GitBranchIcon size={iconSize.large} className=\"text-surface-200 dark:text-surface-700\"/>\n <Typography variant=\"body2\" color=\"disabled\">\n {branches.length === 0 ? \"Create a branch to get started\" : \"Select a branch to view details\"}\n </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 <div className=\"w-2.5 h-2.5 rounded-full bg-emerald-500\"/>\n <div className=\"min-w-0\">\n <Typography variant=\"subtitle1\" className=\"font-semibold truncate\">{selected.name}</Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate\">\n Created from <span className=\"font-mono text-[11px]\">{selected.parentDatabase}</span>\n </Typography>\n </div>\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n onClick={() => setDeleteTarget(selected.name)}\n startIcon={<Trash2Icon size={iconSize.smallest}/>}\n >\n Delete\n </Button>\n </div>\n </div>\n\n {/* Info Cards */}\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=\"Branch Name\" value={selected.name} mono/>\n <StatCard label=\"Source Database\" value={selected.parentDatabase} mono/>\n <StatCard label=\"Created\" value={formatRelative(selected.createdAt)}/>\n <StatCard label=\"Size\" value={formatSize(selected.sizeBytes)}/>\n </div>\n </div>\n\n {/* Usage Info */}\n <div className=\"flex-1 overflow-y-auto px-5 py-4\">\n <Alert color=\"info\">\n <Typography variant=\"body2\" className=\"text-[13px]\">\n <strong>How to use this branch:</strong> Switch your application&apos;s database connection to\n <code className=\"mx-1 px-1.5 py-0.5 rounded bg-surface-100 dark:bg-surface-950 font-mono text-[12px]\">{selected.name}</code>\n to work with an isolated copy of your data. Changes made to this branch won&apos;t affect your main database.\n </Typography>\n </Alert>\n <div className=\"mt-4 p-4 rounded-lg border bg-surface-50 dark:bg-surface-900 border-surface-200 dark:border-surface-700\">\n <Typography variant=\"caption\" className=\"text-[10px] uppercase tracking-wider text-surface-400 mb-2 block font-medium\">Connection Details</Typography>\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <Typography variant=\"caption\" color=\"secondary\" className=\"w-24 shrink-0 text-[11px]\">Database:</Typography>\n <Typography variant=\"body2\" className=\"font-mono text-[12px]\">{selected.name}</Typography>\n </div>\n <div className=\"flex items-center gap-2\">\n <Typography variant=\"caption\" color=\"secondary\" className=\"w-24 shrink-0 text-[11px]\">Branched from:</Typography>\n <Typography variant=\"body2\" className=\"font-mono text-[12px]\">{selected.parentDatabase}</Typography>\n </div>\n <div className=\"flex items-center gap-2\">\n <Typography variant=\"caption\" color=\"secondary\" className=\"w-24 shrink-0 text-[11px]\">Created at:</Typography>\n <Typography variant=\"body2\" className=\"font-mono text-[12px]\">\n {selected.createdAt instanceof Date\n ? selected.createdAt.toLocaleString()\n : new Date(selected.createdAt).toLocaleString()}\n </Typography>\n </div>\n </div>\n </div>\n </div>\n </>\n )}\n </div>\n\n {/* ── Create Dialog ── */}\n <Dialog open={createOpen} onOpenChange={setCreateOpen}>\n <DialogTitle>Create New Branch</DialogTitle>\n <DialogContent className=\"space-y-4 min-w-[400px]\">\n <Typography variant=\"body2\" color=\"secondary\" className=\"text-[13px]\">\n Create an isolated database copy. The branch will be a full clone of the source database at this point in time.\n </Typography>\n <div>\n <TextField\n label=\"Branch Name\"\n value={newBranchName}\n onChange={(e) => setNewBranchName(e.target.value)}\n placeholder=\"e.g. feature-auth, staging, preview-pr-42\"\n size=\"small\"\n autoFocus\n />\n </div>\n {branches.length > 0 && (\n <div>\n <Select\n label=\"Source Database\"\n value={sourceBranch ?? \"\"}\n onValueChange={(v) => setSourceBranch(v || undefined)}\n placeholder=\"Default (main database)\"\n size=\"small\"\n renderValue={(v) => v || \"Default (main database)\"}\n >\n <SelectItem value=\"\">Default (main database)</SelectItem>\n {branches.map(b => (\n <SelectItem key={b.name} value={b.name}>{b.name}</SelectItem>\n ))}\n </Select>\n </div>\n )}\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={() => setCreateOpen(false)} disabled={creating}>\n Cancel\n </Button>\n <Button\n color=\"primary\"\n onClick={handleCreate}\n disabled={!newBranchName.trim() || creating}\n startIcon={creating ? <CircularProgress size=\"smallest\"/> : <PlusIcon size={iconSize.smallest}/>}\n >\n {creating ? \"Creating...\" : \"Create Branch\"}\n </Button>\n </DialogActions>\n </Dialog>\n\n {/* ── Delete Confirm ── */}\n <ConfirmationDialog\n open={!!deleteTarget}\n onAccept={handleDelete}\n onCancel={() => setDeleteTarget(null)}\n title=\"Delete Branch\"\n body={\n <Typography variant=\"body2\">\n Are you sure you want to permanently delete the branch <strong>{deleteTarget}</strong>?\n This action cannot be undone and all data in this branch will be lost.\n </Typography>\n }\n loading={deleting}\n />\n </div>\n );\n}\n\nfunction StatCard({ label, value, mono }: {\n label: string; value: string; mono?: boolean;\n}) {\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] truncate\",\n mono && \"font-mono\"\n )}>{value}</Typography>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"BranchesView-Dlg78EQ8.js","names":[],"sources":["../src/components/Branches/BranchesView.tsx"],"sourcesContent":["\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport {\n Alert,\n Button,\n Chip,\n CircularProgress,\n cls,\n CopyIcon,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n GitBranchIcon,\n IconButton,\n iconSize,\n Paper,\n PlusIcon,\n RefreshCwIcon,\n Select,\n SelectItem,\n TextField,\n Trash2Icon,\n Typography\n} from \"@rebasepro/ui\";\nimport { useRebaseContext, useSnackbarController, ConfirmationDialog } from \"@rebasepro/core\";\nimport { isBranchAdmin } from \"@rebasepro/types\";\nimport type { BranchInfo } from \"@rebasepro/types\";\n\nfunction formatSize(bytes: number | undefined): string {\n if (bytes === undefined || bytes === null) return \"—\";\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n}\n\nfunction formatRelative(date: Date | string | undefined): string {\n if (!date) return \"—\";\n const d = date instanceof Date ? date : new Date(date);\n if (isNaN(d.getTime())) return \"—\";\n const now = Date.now();\n const diff = now - d.getTime();\n if (diff < 60_000) return \"just now\";\n if (diff < 3_600_000) { const m = Math.round(diff / 60_000); return `${m}m ago`; }\n if (diff < 86_400_000) { const h = Math.round(diff / 3_600_000); return `${h}h ago`; }\n if (diff < 604_800_000) { const d2 = Math.round(diff / 86_400_000); return `${d2}d ago`; }\n return d.toLocaleDateString();\n}\n\nexport function BranchesView() {\n const { databaseAdmin } = useRebaseContext();\n const snackbar = useSnackbarController();\n\n const [branches, setBranches] = useState<BranchInfo[]>([]);\n const [loading, setLoading] = useState(true);\n const [selectedBranch, setSelectedBranch] = useState<string | null>(null);\n\n // Create dialog\n const [createOpen, setCreateOpen] = useState(false);\n const [newBranchName, setNewBranchName] = useState(\"\");\n const [sourceBranch, setSourceBranch] = useState<string | undefined>(undefined);\n const [creating, setCreating] = useState(false);\n\n // Delete confirm\n const [deleteTarget, setDeleteTarget] = useState<string | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n // Refs\n const snackbarRef = useRef(snackbar);\n snackbarRef.current = snackbar;\n\n const branchAdmin = isBranchAdmin(databaseAdmin) ? databaseAdmin : undefined;\n\n const loadBranches = useCallback(async () => {\n if (!branchAdmin) {\n setLoading(false);\n return;\n }\n try {\n const result = await branchAdmin.listBranches();\n setBranches(result);\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 }, [branchAdmin]);\n\n useEffect(() => {\n loadBranches();\n }, [loadBranches]);\n\n const handleCreate = async () => {\n if (!branchAdmin || !newBranchName.trim()) return;\n setCreating(true);\n try {\n await branchAdmin.createBranch(newBranchName.trim(), sourceBranch ? { source: sourceBranch } : undefined);\n snackbarRef.current.open({\n type: \"success\",\n message: `Branch \"${newBranchName.trim()}\" created successfully`\n });\n setCreateOpen(false);\n setNewBranchName(\"\");\n setSourceBranch(undefined);\n await loadBranches();\n } catch (e: unknown) {\n snackbarRef.current.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n } finally {\n setCreating(false);\n }\n };\n\n const handleDelete = async () => {\n if (!branchAdmin || !deleteTarget) return;\n setDeleting(true);\n try {\n await branchAdmin.deleteBranch(deleteTarget);\n snackbarRef.current.open({\n type: \"success\",\n message: `Branch \"${deleteTarget}\" deleted`\n });\n if (selectedBranch === deleteTarget) setSelectedBranch(null);\n setDeleteTarget(null);\n await loadBranches();\n } catch (e: unknown) {\n snackbarRef.current.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n } finally {\n setDeleting(false);\n }\n };\n\n // Not supported\n if (!branchAdmin) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full gap-4 text-center p-8\">\n <GitBranchIcon size={iconSize.large} className=\"text-surface-300 dark:text-surface-600\"/>\n <Typography variant=\"h6\" color=\"secondary\">Database Branching Not Available</Typography>\n <Typography variant=\"body2\" color=\"disabled\" className=\"max-w-md\">\n Branching requires a PostgreSQL backend with an admin connection configured.\n Set <code className=\"text-xs bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded font-mono\">adminConnectionString</code> in your server configuration.\n </Typography>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <CircularProgress/>\n </div>\n );\n }\n\n const selected = branches.find(b => b.name === selectedBranch);\n\n return (\n <div className=\"flex h-full w-full overflow-hidden bg-white dark:bg-surface-950\">\n {/* ── Branch 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 <GitBranchIcon size={iconSize.small} className=\"text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"font-semibold\">Branches</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">{branches.length}</Chip>\n </div>\n <div className=\"flex items-center gap-1\">\n <IconButton size=\"small\" onClick={loadBranches} title=\"Refresh\">\n <RefreshCwIcon size={iconSize.smallest}/>\n </IconButton>\n <IconButton size=\"small\" onClick={() => setCreateOpen(true)} title=\"Create branch\" className=\"text-primary\">\n <PlusIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto p-2 space-y-1\">\n {branches.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center h-full gap-3 text-center px-4\">\n <CopyIcon size={iconSize.small} className=\"text-surface-300 dark:text-surface-600\"/>\n <Typography variant=\"body2\" color=\"disabled\" className=\"text-[13px]\">\n No branches yet. Create one to start working with an isolated database copy.\n </Typography>\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={() => setCreateOpen(true)}\n startIcon={<PlusIcon size={iconSize.smallest}/>}\n >\n Create Branch\n </Button>\n </div>\n ) : (\n branches.map(branch => (\n <div\n key={branch.name}\n onClick={() => setSelectedBranch(branch.name)}\n className={cls(\n \"flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all\",\n selectedBranch === branch.name\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=\"w-2 h-2 rounded-full shrink-0 bg-emerald-500\"/>\n <div className=\"flex-1 min-w-0\">\n <Typography variant=\"body2\" className=\"truncate font-medium text-[13px]\">{branch.name}</Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate text-[11px]\">\n from {branch.parentDatabase} · {formatRelative(branch.createdAt)}\n </Typography>\n </div>\n {branch.sizeBytes !== undefined && (\n <Typography variant=\"caption\" color=\"disabled\" className=\"font-mono text-[10px] shrink-0\">\n {formatSize(branch.sizeBytes)}\n </Typography>\n )}\n </div>\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 {!selected ? (\n <div className=\"flex flex-col items-center justify-center h-full gap-3\">\n <GitBranchIcon size={iconSize.large} className=\"text-surface-200 dark:text-surface-700\"/>\n <Typography variant=\"body2\" color=\"disabled\">\n {branches.length === 0 ? \"Create a branch to get started\" : \"Select a branch to view details\"}\n </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 <div className=\"w-2.5 h-2.5 rounded-full bg-emerald-500\"/>\n <div className=\"min-w-0\">\n <Typography variant=\"subtitle1\" className=\"font-semibold truncate\">{selected.name}</Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate\">\n Created from <span className=\"font-mono text-[11px]\">{selected.parentDatabase}</span>\n </Typography>\n </div>\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n onClick={() => setDeleteTarget(selected.name)}\n startIcon={<Trash2Icon size={iconSize.smallest}/>}\n >\n Delete\n </Button>\n </div>\n </div>\n\n {/* Info Cards */}\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=\"Branch Name\" value={selected.name} mono/>\n <StatCard label=\"Source Database\" value={selected.parentDatabase} mono/>\n <StatCard label=\"Created\" value={formatRelative(selected.createdAt)}/>\n <StatCard label=\"Size\" value={formatSize(selected.sizeBytes)}/>\n </div>\n </div>\n\n {/* Usage Info */}\n <div className=\"flex-1 overflow-y-auto px-5 py-4\">\n <Alert color=\"info\">\n <Typography variant=\"body2\" className=\"text-[13px]\">\n <strong>How to use this branch:</strong> Switch your application&apos;s database connection to\n <code className=\"mx-1 px-1.5 py-0.5 rounded bg-surface-100 dark:bg-surface-950 font-mono text-[12px]\">{selected.name}</code>\n to work with an isolated copy of your data. Changes made to this branch won&apos;t affect your main database.\n </Typography>\n </Alert>\n <div className=\"mt-4 p-4 rounded-lg border bg-surface-50 dark:bg-surface-900 border-surface-200 dark:border-surface-700\">\n <Typography variant=\"caption\" className=\"text-[10px] uppercase tracking-wider text-surface-400 mb-2 block font-medium\">Connection Details</Typography>\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <Typography variant=\"caption\" color=\"secondary\" className=\"w-24 shrink-0 text-[11px]\">Database:</Typography>\n <Typography variant=\"body2\" className=\"font-mono text-[12px]\">{selected.name}</Typography>\n </div>\n <div className=\"flex items-center gap-2\">\n <Typography variant=\"caption\" color=\"secondary\" className=\"w-24 shrink-0 text-[11px]\">Branched from:</Typography>\n <Typography variant=\"body2\" className=\"font-mono text-[12px]\">{selected.parentDatabase}</Typography>\n </div>\n <div className=\"flex items-center gap-2\">\n <Typography variant=\"caption\" color=\"secondary\" className=\"w-24 shrink-0 text-[11px]\">Created at:</Typography>\n <Typography variant=\"body2\" className=\"font-mono text-[12px]\">\n {selected.createdAt instanceof Date\n ? selected.createdAt.toLocaleString()\n : new Date(selected.createdAt).toLocaleString()}\n </Typography>\n </div>\n </div>\n </div>\n </div>\n </>\n )}\n </div>\n\n {/* ── Create Dialog ── */}\n <Dialog open={createOpen} onOpenChange={setCreateOpen}>\n <DialogTitle>Create New Branch</DialogTitle>\n <DialogContent className=\"space-y-4 min-w-[400px]\">\n <Typography variant=\"body2\" color=\"secondary\" className=\"text-[13px]\">\n Create an isolated database copy. The branch will be a full clone of the source database at this point in time.\n </Typography>\n <div>\n <TextField\n label=\"Branch Name\"\n value={newBranchName}\n onChange={(e) => setNewBranchName(e.target.value)}\n placeholder=\"e.g. feature-auth, staging, preview-pr-42\"\n size=\"small\"\n autoFocus\n />\n </div>\n {branches.length > 0 && (\n <div>\n <Select\n label=\"Source Database\"\n value={sourceBranch ?? \"__main__\"}\n onValueChange={(v) => setSourceBranch(v === \"__main__\" ? undefined : v)}\n placeholder=\"Default (main database)\"\n size=\"small\"\n >\n <SelectItem value=\"__main__\">Default (main database)</SelectItem>\n {branches.map(b => (\n <SelectItem key={b.name} value={b.name}>{b.name}</SelectItem>\n ))}\n </Select>\n </div>\n )}\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={() => setCreateOpen(false)} disabled={creating}>\n Cancel\n </Button>\n <Button\n color=\"primary\"\n onClick={handleCreate}\n disabled={!newBranchName.trim() || creating}\n startIcon={creating ? <CircularProgress size=\"smallest\"/> : <PlusIcon size={iconSize.smallest}/>}\n >\n {creating ? \"Creating...\" : \"Create Branch\"}\n </Button>\n </DialogActions>\n </Dialog>\n\n {/* ── Delete Confirm ── */}\n <ConfirmationDialog\n open={!!deleteTarget}\n onAccept={handleDelete}\n onCancel={() => setDeleteTarget(null)}\n title=\"Delete Branch\"\n body={\n <Typography variant=\"body2\">\n Are you sure you want to permanently delete the branch <strong>{deleteTarget}</strong>?\n This action cannot be undone and all data in this branch will be lost.\n </Typography>\n }\n loading={deleting}\n />\n </div>\n );\n}\n\nfunction StatCard({ label, value, mono }: {\n label: string; value: string; mono?: boolean;\n}) {\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] truncate\",\n mono && \"font-mono\"\n )}>{value}</Typography>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -38,10 +38,10 @@ interface FindResponse<M extends Record<string, any> = any> {
38
38
  };
39
39
  }
40
40
 
41
- type FilterOperator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "in" | "not-in" | "array-contains" | "array-contains-any" | "is" | "is_not" | "like" | "ilike";
41
+ type WhereFilterOp = "<" | "<=" | "==" | "!=" | ">=" | ">" | "in" | "not-in" | "array-contains" | "array-contains-any";
42
42
 
43
43
  interface QueryBuilder<M extends Record<string, any> = any> {
44
- where(column: keyof M & string, operator: FilterOperator, value: any): QueryBuilder<M>;
44
+ where(column: keyof M & string, operator: WhereFilterOp, value: any): QueryBuilder<M>;
45
45
  orderBy(column: keyof M & string, direction?: "asc" | "desc"): QueryBuilder<M>;
46
46
  limit(count: number): QueryBuilder<M>;
47
47
  offset(count: number): QueryBuilder<M>;
@@ -57,7 +57,7 @@ interface CollectionClient<M extends Record<string, any> = any> {
57
57
  create(data: Partial<M>, id?: string | number): Promise<Entity<M>>;
58
58
  update(id: string | number, data: Partial<M>): Promise<Entity<M>>;
59
59
  delete(id: string | number): Promise<void>;
60
- where(column: keyof M & string, operator: FilterOperator, value: any): QueryBuilder<M>;
60
+ where(column: keyof M & string, operator: WhereFilterOp, value: any): QueryBuilder<M>;
61
61
  orderBy(column: keyof M & string, direction?: "asc" | "desc"): QueryBuilder<M>;
62
62
  limit(count: number): QueryBuilder<M>;
63
63
  offset(count: number): QueryBuilder<M>;
@@ -541,7 +541,7 @@ var JSEditorSidebar = ({ collections, snippets, history, onSelectSnippet, onDele
541
541
  children: "No execution history yet"
542
542
  })
543
543
  }) : [...history].reverse().map((code, i) => /* @__PURE__ */ jsxs("div", {
544
- className: "p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-950 cursor-pointer group transition-colors flex items-start",
544
+ className: "p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-900 cursor-pointer group transition-colors flex items-start",
545
545
  onClick: () => onSelectSnippet(code),
546
546
  children: [/* @__PURE__ */ jsx("svg", {
547
547
  className: "w-3 h-3 mt-1 mr-2 text-text-disabled dark:text-text-disabled-dark group-hover:text-primary transition-colors flex-shrink-0",
@@ -569,7 +569,7 @@ var JSEditorSidebar = ({ collections, snippets, history, onSelectSnippet, onDele
569
569
  function CollectionItem({ collection, onInsertCode }) {
570
570
  const [expanded, setExpanded] = useState(false);
571
571
  return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
572
- className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors group relative",
572
+ className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative",
573
573
  onClick: () => setExpanded(!expanded),
574
574
  children: [
575
575
  /* @__PURE__ */ jsx("svg", {
@@ -611,7 +611,7 @@ function CollectionItem({ collection, onInsertCode }) {
611
611
  }), expanded && collection.properties.length > 0 && /* @__PURE__ */ jsx("div", {
612
612
  className: cls("ml-5 mt-1 space-y-0.5 border-l", defaultBorderMixin),
613
613
  children: collection.properties.map((prop) => /* @__PURE__ */ jsxs("div", {
614
- className: "flex items-center p-1 pl-2 hover:bg-surface-50 dark:hover:bg-surface-950 rounded-r cursor-pointer transition-colors group/prop relative min-h-[28px]",
614
+ className: "flex items-center p-1 pl-2 hover:bg-surface-50 dark:hover:bg-surface-900 rounded-r cursor-pointer transition-colors group/prop relative min-h-[28px]",
615
615
  onClick: () => onInsertCode(`"${prop}"`),
616
616
  children: [/* @__PURE__ */ jsx("svg", {
617
617
  className: "w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0",
@@ -1570,4 +1570,4 @@ function JSONHighlight({ value }) {
1570
1570
  //#endregion
1571
1571
  export { JSEditor };
1572
1572
 
1573
- //# sourceMappingURL=JSEditor-BhAbEjCP.js.map
1573
+ //# sourceMappingURL=JSEditor-Ca4XYGRp.js.map