@rebasepro/studio 0.17.3 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -1
- package/README.md +12 -4
- package/dist/{ApiKeysView-DiCurTEU.js → ApiKeysView-EIvegrUk.js} +54 -39
- package/dist/ApiKeysView-EIvegrUk.js.map +1 -0
- package/dist/{BackupsView-DNS6LdVg.js → BackupsView-DVkKzaPR.js} +29 -31
- package/dist/BackupsView-DVkKzaPR.js.map +1 -0
- package/dist/{BranchesView-CM3B-ER3.js → BranchesView-Df8hQ8wE.js} +85 -24
- package/dist/BranchesView-Df8hQ8wE.js.map +1 -0
- package/dist/{CronJobsView-BHtJZeJZ.js → CronJobsView-CLOGjb-L.js} +32 -27
- package/dist/CronJobsView-CLOGjb-L.js.map +1 -0
- package/dist/{RLSEditor-CBX4uD66.js → RLSEditor-Sa1D34ny.js} +724 -615
- package/dist/RLSEditor-Sa1D34ny.js.map +1 -0
- package/dist/{SQLEditor-ByecwX8B.js → SQLEditor-BfNA8xM3.js} +9 -4
- package/dist/{SQLEditor-ByecwX8B.js.map → SQLEditor-BfNA8xM3.js.map} +1 -1
- package/dist/{SchemaVisualizer-D6tMZgeC.js → SchemaVisualizer-DTDMYL6o.js} +84 -110
- package/dist/SchemaVisualizer-DTDMYL6o.js.map +1 -0
- package/dist/{StorageView-BGDeC5GI.js → StorageView-Ed9Q_VlB.js} +5 -32
- package/dist/StorageView-Ed9Q_VlB.js.map +1 -0
- package/dist/components/RLSEditor/saveRules.d.ts +38 -0
- package/dist/components/RebaseStudio.d.ts +1 -1
- package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +5 -2
- package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +0 -4
- package/dist/components/load-failure-view.d.ts +17 -0
- package/dist/components/load-failure.d.ts +47 -0
- package/dist/index.es.js +56 -36
- package/dist/index.es.js.map +1 -1
- package/dist/load-failure-C6OMJSGC.js +31 -0
- package/dist/load-failure-C6OMJSGC.js.map +1 -0
- package/dist/load-failure-view-CBTB_h-h.js +48 -0
- package/dist/load-failure-view-CBTB_h-h.js.map +1 -0
- package/package.json +36 -21
- package/dist/ApiKeysView-DiCurTEU.js.map +0 -1
- package/dist/BackupsView-DNS6LdVg.js.map +0 -1
- package/dist/BranchesView-CM3B-ER3.js.map +0 -1
- package/dist/CronJobsView-BHtJZeJZ.js.map +0 -1
- package/dist/RLSEditor-CBX4uD66.js.map +0 -1
- package/dist/SchemaVisualizer-D6tMZgeC.js.map +0 -1
- package/dist/StorageView-BGDeC5GI.js.map +0 -1
- package/dist/components/StorageView/storage-failure.d.ts +0 -34
- package/src/components/ApiExplorer/ApiExplorer.tsx +0 -299
- package/src/components/ApiExplorer/EndpointDetail.tsx +0 -279
- package/src/components/ApiExplorer/TryItPanel.tsx +0 -499
- package/src/components/ApiExplorer/parseSpec.ts +0 -104
- package/src/components/ApiExplorer/types.ts +0 -84
- package/src/components/ApiKeys/ApiKeysView.tsx +0 -479
- package/src/components/ApiKeys/CreateApiKeyDialog.tsx +0 -711
- package/src/components/ApiKeys/permissions.ts +0 -145
- package/src/components/AuthSimulationSelector.tsx +0 -72
- package/src/components/Backups/BackupsView.tsx +0 -185
- package/src/components/Branches/BranchesView.tsx +0 -386
- package/src/components/CronJobs/CronJobsView.tsx +0 -413
- package/src/components/JSEditor/JSEditor.tsx +0 -1100
- package/src/components/JSEditor/JSEditorSidebar.tsx +0 -350
- package/src/components/JSEditor/JSMonacoEditor.tsx +0 -410
- package/src/components/LogsExplorer/LogsExplorer.tsx +0 -273
- package/src/components/LogsExplorer/useLogTail.ts +0 -356
- package/src/components/RLSEditor/PolicyEditor.tsx +0 -399
- package/src/components/RLSEditor/RLSEditor.tsx +0 -1128
- package/src/components/RLSEditor/index.ts +0 -1
- package/src/components/RLSEditor/policy-presets.ts +0 -176
- package/src/components/RLSEditor/saveSecurityRules.ts +0 -48
- package/src/components/RebaseStudio.tsx +0 -174
- package/src/components/SQLEditor/ExplainVisualizer.tsx +0 -128
- package/src/components/SQLEditor/MonacoEditor.tsx +0 -203
- package/src/components/SQLEditor/SQLEditor.tsx +0 -1518
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +0 -173
- package/src/components/SQLEditor/SchemaBrowser.tsx +0 -168
- package/src/components/SQLEditor/sql_editor_types.ts +0 -18
- package/src/components/SchemaVisualizer/RelationEdge.tsx +0 -102
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +0 -710
- package/src/components/SchemaVisualizer/TableNode.tsx +0 -257
- package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +0 -140
- package/src/components/SchemaVisualizer/useLiveRls.ts +0 -86
- package/src/components/SchemaVisualizer/useSchemaGraph.ts +0 -426
- package/src/components/StorageView/StorageView.tsx +0 -1588
- package/src/components/StorageView/storage-failure.ts +0 -63
- package/src/components/StudioHomePage.tsx +0 -430
- package/src/index.ts +0 -31
- package/src/utils/parseSpec.test.ts +0 -309
- package/src/utils/sql_utils.test.ts +0 -265
- package/src/utils/sql_utils.ts +0 -291
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RLSEditor-CBX4uD66.js","names":[],"sources":["../src/components/RLSEditor/policy-presets.ts","../src/components/RLSEditor/PolicyEditor.tsx","../src/components/RLSEditor/saveSecurityRules.ts","../src/components/RLSEditor/RLSEditor.tsx"],"sourcesContent":["/**\n * What the RLS editor offers before the user types anything: the roles a `TO`\n * list may name, and the ready-made policies.\n *\n * Separated from the component because these are the parts that have to agree\n * with the *server*, not with the UI — and the ways they can silently disagree\n * are the reason this file has tests.\n */\nimport { policy as policyExpr } from \"@rebasepro/types\";\nimport { policyToPostgres, REBASE_USER_ROLE } from \"@rebasepro/common\";\n\nexport type PolicyCommand = \"ALL\" | \"SELECT\" | \"INSERT\" | \"UPDATE\" | \"DELETE\";\n\nexport const COMMAND_OPTIONS: PolicyCommand[] = [\"ALL\", \"SELECT\", \"INSERT\", \"UPDATE\", \"DELETE\"];\n\n/**\n * The roles a policy's `TO` list can name when nothing better is known.\n *\n * Not `authenticated` / `anon` / `admin`, which is what this list used to be.\n * The first two are Supabase's role names and Rebase never creates them, so\n * `CREATE POLICY ... TO authenticated` — which is literally what the editor\n * builds and executes — fails with `role \"authenticated\" does not exist`. The\n * driver's `validatePolicyPgRoles` rejects the same three names in\n * `SecurityRule.pgRoles` and says why: they are another platform's convention,\n * and application roles belong in a condition, not in the `TO` list.\n *\n * `admin` was the more dangerous entry, because it is a plausible *application*\n * role. Had one existed as a database role too, the policy would have been\n * created successfully and then matched nothing at all: requests run as\n * {@link REBASE_USER_ROLE} after a `SET LOCAL ROLE`, and a `TO` list naming a\n * role the request never assumes filters every row without erroring.\n *\n * So: `public` (what the framework's own generator emits) and the role requests\n * actually arrive as. \"Signed-in users\" and \"admins\" are not roles here — they\n * are conditions over `rebase.uid()` and `rebase.roles()`, which is what the\n * presets below now compile to.\n */\nexport const FALLBACK_ROLE_OPTIONS = [\"public\", REBASE_USER_ROLE];\n\n/**\n * The `TO` list the editor offers, given what the database reported and what\n * the policy being edited already targets.\n *\n * Three sources, and each is there for a reason the others do not cover:\n *\n * - {@link FALLBACK_ROLE_OPTIONS} always, because `public` is a **keyword, not\n * a row in `pg_roles`** — `fetchAvailableRoles` cannot return it, so seeding\n * from the live fetch alone would drop the one role every generated policy\n * targets.\n * - the live roles, so an operator's own `app_read` is reachable without\n * hand-editing SQL.\n * - the edited policy's own roles, because a `MultiSelect` silently drops a\n * value with no matching item. That value is the `TO` list of a policy\n * already enforcing something, so opening an unrelated policy for a one-word\n * rename would have quietly rewritten who it applies to.\n */\nexport function roleOptionsFor(\n fetched: readonly string[] | undefined,\n policyRoles: readonly string[] | undefined\n): string[] {\n return [...new Set([...FALLBACK_ROLE_OPTIONS, ...(fetched ?? []), ...(policyRoles ?? [])])];\n}\n\n/**\n * Preset conditions, compiled by the framework's own policy compiler rather\n * than written out here.\n *\n * `policyToPostgres` is the function `db push` generates policies with, so a\n * preset cannot drift from what the framework emits — and the one that matters\n * has drifted before: `authenticated()` used to compile to a bare\n * `IS NOT NULL`, which is true for anonymous visitors, and any copy of that\n * string sitting in a preset would still be handing out the old grant today.\n */\nexport const SIGNED_IN_SQL = policyToPostgres(policyExpr.authenticated());\nexport const IS_ADMIN_SQL = policyToPostgres(policyExpr.rolesOverlap([\"admin\"]));\nexport const OWNS_ROW_SQL = policyToPostgres(\n policyExpr.compare(policyExpr.authUid(), \"eq\", policyExpr.field(\"uid\"))\n);\n\nexport interface PolicyPreset {\n id: string;\n label: string;\n description: string;\n policyname: string;\n cmd: PolicyCommand;\n permissive: \"PERMISSIVE\" | \"RESTRICTIVE\";\n roles: string[];\n qual: string;\n with_check: string;\n}\n\n/**\n * Every preset targets `TO public`, which is what the framework's generator\n * emits and what actually reaches a request running as `rebase_user`. The part\n * that used to be expressed as a role — \"authenticated\", \"admin\" — is a\n * condition now, so it is enforced where Postgres will actually evaluate it.\n */\nexport const POLICY_PRESETS: PolicyPreset[] = [\n {\n id: \"public_read\",\n label: \"Enable read access to everyone\",\n description: \"Anyone can read data, regardless of authentication status.\",\n policyname: \"Enable read access for all users\",\n cmd: \"SELECT\",\n permissive: \"PERMISSIVE\",\n roles: [\"public\"],\n qual: \"true\",\n with_check: \"\"\n },\n {\n id: \"auth_read\",\n label: \"Enable read access for signed-in users only\",\n description: \"Only signed-in users are allowed to read data. Anonymous requests carry a sentinel uid, so the condition excludes them explicitly.\",\n policyname: \"Enable read access for signed-in users\",\n cmd: \"SELECT\",\n permissive: \"PERMISSIVE\",\n roles: [\"public\"],\n qual: SIGNED_IN_SQL,\n with_check: \"\"\n },\n {\n id: \"auth_insert\",\n label: \"Enable insert for signed-in users only\",\n description: \"Only signed-in users are allowed to insert new data.\",\n policyname: \"Enable insert for signed-in users only\",\n cmd: \"INSERT\",\n permissive: \"PERMISSIVE\",\n roles: [\"public\"],\n qual: \"\",\n with_check: SIGNED_IN_SQL\n },\n {\n id: \"admin_all\",\n label: \"Admins can do anything\",\n description: \"Restricted to users holding the `admin` application role, matched inside the policy via rebase.roles().\",\n policyname: \"Admins have full access\",\n cmd: \"ALL\",\n permissive: \"PERMISSIVE\",\n roles: [\"public\"],\n qual: IS_ADMIN_SQL,\n with_check: IS_ADMIN_SQL\n },\n {\n id: \"user_select_own\",\n label: \"Users can read their own rows\",\n description: \"Users can only read rows whose uid column matches their rebase.uid()\",\n policyname: \"Users can select their own data\",\n cmd: \"SELECT\",\n permissive: \"PERMISSIVE\",\n roles: [\"public\"],\n qual: OWNS_ROW_SQL,\n with_check: \"\"\n },\n {\n id: \"user_update_own\",\n label: \"Users can update their own rows\",\n description: \"Users can only update rows whose uid column matches their rebase.uid()\",\n policyname: \"Users can update their own data\",\n cmd: \"UPDATE\",\n permissive: \"PERMISSIVE\",\n roles: [\"public\"],\n qual: OWNS_ROW_SQL,\n with_check: OWNS_ROW_SQL\n },\n {\n id: \"user_delete_own\",\n label: \"Users can delete their own rows\",\n description: \"Users can only delete rows whose uid column matches their rebase.uid()\",\n policyname: \"Users can delete their own data\",\n cmd: \"DELETE\",\n permissive: \"PERMISSIVE\",\n roles: [\"public\"],\n qual: OWNS_ROW_SQL,\n with_check: \"\"\n }\n];\n","import React, { useEffect, useMemo, useState } from \"react\";\nimport {\n Button,\n cls,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n HelpCircleIcon,\n IconButton,\n iconSize,\n MultiSelect,\n MultiSelectItem,\n Paper,\n Select,\n SelectItem,\n TextField,\n Typography\n} from \"@rebasepro/ui\";\nimport { useTranslation } from \"@rebasepro/app\";\nimport { RLS_JWT_SQL, RLS_ROLES_SQL, RLS_UID_SQL } from \"@rebasepro/types\";\nimport {\n COMMAND_OPTIONS,\n POLICY_PRESETS,\n roleOptionsFor,\n type PolicyCommand\n} from \"./policy-presets\";\nimport { MonacoEditor } from \"../SQLEditor/MonacoEditor\";\nimport type { PostgresPolicy } from \"./RLSEditor\";\n\nexport interface PolicyEditorProps {\n policy?: PostgresPolicy;\n schema: string;\n table: string;\n /**\n * Native PostgreSQL roles this database actually has, for the `TO` list.\n *\n * Supplied by the caller from `fetchAvailableRoles()`. Merged with the\n * always-offered defaults by `roleOptionsFor`, never used in place of them:\n * `public` is a keyword and never comes back from that query.\n */\n roleOptions?: readonly string[];\n onSave: (policyData: Partial<PostgresPolicy>) => void;\n onCancel: () => void;\n}\n\nexport const PolicyEditor = ({\n policy,\n schema,\n table,\n roleOptions,\n onSave,\n onCancel\n}: PolicyEditorProps) => {\n\n const { t } = useTranslation();\n const [name, setName] = useState(\"\");\n const [helpOpen, setHelpOpen] = useState(false);\n const [behavior, setBehavior] = useState<\"PERMISSIVE\" | \"RESTRICTIVE\">(\"PERMISSIVE\");\n const [command, setCommand] = useState<PolicyCommand>(\"ALL\");\n const [roles, setRoles] = useState<string[]>([\"public\"]);\n const [usingExpr, setUsingExpr] = useState<string>(\"\");\n const [checkExpr, setCheckExpr] = useState<string>(\"\");\n\n const [selectedPreset, setSelectedPreset] = useState<string>(\"\");\n\n const availableRoles = useMemo(\n () => roleOptionsFor(roleOptions, policy?.roles),\n [roleOptions, policy]\n );\n\n useEffect(() => {\n if (policy) {\n setName(policy.policyname || \"\");\n setBehavior(policy.permissive || \"PERMISSIVE\");\n setCommand((policy.cmd as PolicyCommand) || \"ALL\");\n\n const initialRoles = policy.roles\n ? (Array.isArray(policy.roles) ? policy.roles : [policy.roles])\n : [\"public\"];\n setRoles(initialRoles as string[]);\n\n setUsingExpr(policy.qual || \"\");\n setCheckExpr(policy.with_check || \"\");\n } else {\n setName(\"\");\n setBehavior(\"PERMISSIVE\");\n setCommand(\"ALL\");\n setRoles([\"public\"]);\n setUsingExpr(\"\");\n setCheckExpr(\"\");\n setSelectedPreset(\"\");\n }\n }, [policy]);\n\n const handlePresetChange = (presetId: string) => {\n const preset = POLICY_PRESETS.find(p => p.id === presetId);\n if (!preset) return;\n\n setSelectedPreset(presetId);\n setName(preset.policyname);\n setBehavior(preset.permissive);\n setCommand(preset.cmd);\n setRoles(preset.roles);\n setUsingExpr(preset.qual);\n setCheckExpr(preset.with_check);\n };\n\n const showCheck = command === \"ALL\" || command === \"INSERT\" || command === \"UPDATE\";\n\n const handleSave = () => {\n onSave({\n policyname: name,\n permissive: behavior,\n cmd: command,\n roles: roles,\n qual: usingExpr,\n with_check: showCheck ? checkExpr : null\n });\n };\n\n return (\n <>\n {/* Inline header: this editor renders as a full pane, not inside a Dialog,\n so DialogTitle (Radix-bound) cannot be used here. */}\n <Typography variant=\"h6\" gutterBottom className=\"mt-8 mx-8 flex justify-between items-center w-full\">\n <div>\n <div>{policy ? t(\"studio_policy_edit\") : t(\"studio_policy_create\")}</div>\n <div className=\"text-sm font-normal text-text-secondary dark:text-text-secondary-dark tracking-wide mt-1\">\n {t(\"studio_policy_defining_rules\")} <span className=\"font-mono text-primary bg-primary-bg dark:bg-primary-bg-dark px-1 py-0.5 rounded\">{schema}.{table}</span>\n </div>\n </div>\n <IconButton size=\"small\" onClick={() => setHelpOpen(true)}>\n <HelpCircleIcon size={iconSize.smallest}/>\n </IconButton>\n </Typography>\n\n <DialogContent className=\"p-4 md:p-6 border-t dark:border-surface-950 bg-surface-50 dark:bg-surface-950\" includeMargin={false}>\n <div className=\"max-w-4xl mx-auto\">\n <Paper className={cls(\"p-4 md:p-6 flex flex-col gap-6 bg-white dark:bg-surface-900 border-none sm:border-solid rounded-none sm:rounded-xl\", defaultBorderMixin)}>\n\n {/* Presets - only for new policies */}\n {!policy && (\n <div className=\"flex flex-col gap-1.5 bg-primary/5 dark:bg-primary-bg-dark/20 p-3 sm:p-4 rounded-lg border border-primary/10 dark:border-primary/20\">\n <Typography variant=\"caption\" className=\"text-primary dark:text-primary-light uppercase tracking-wider\">{t(\"studio_policy_template\")}</Typography>\n <Select\n size=\"small\"\n value={selectedPreset}\n onValueChange={handlePresetChange}\n position=\"item-aligned\"\n placeholder={t(\"studio_policy_select_template\")}\n className=\"bg-white dark:bg-surface-950\"\n >\n {POLICY_PRESETS.map((preset) => (\n <SelectItem key={preset.id} value={preset.id}>\n <div className=\"flex flex-col text-left\">\n <span className=\"text-sm\">{preset.label}</span>\n <span className=\"text-xs text-text-secondary dark:text-text-secondary-dark\">{preset.description}</span>\n </div>\n </SelectItem>\n ))}\n </Select>\n </div>\n )}\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6\">\n {/* Policy Name */}\n <div className=\"flex flex-col gap-1.5\">\n <Typography variant=\"caption\" className=\"uppercase tracking-wider text-text-secondary\">{t(\"studio_policy_name\")}</Typography>\n <TextField\n value={name}\n aria-label={t(\"studio_policy_name\")}\n onChange={(e) => setName(e.target.value)}\n placeholder={t(\"studio_policy_name_placeholder\")}\n className=\"w-full\"\n />\n </div>\n\n {/* Behavior */}\n <div className=\"flex flex-col gap-1.5\">\n <Typography variant=\"caption\" className=\"uppercase tracking-wider text-text-secondary\">\n {t(\"studio_policy_behavior\")} <code className=\"text-[10px] bg-surface-200 dark:bg-surface-950 text-text-secondary dark:text-text-secondary-dark px-1 py-0.5 rounded ml-1\">AS</code>\n </Typography>\n <Select\n value={behavior}\n onValueChange={(val) => setBehavior(val as \"PERMISSIVE\" | \"RESTRICTIVE\")}\n position=\"item-aligned\"\n >\n <SelectItem value=\"PERMISSIVE\">\n <div className=\"flex flex-col text-left\">\n <span className=\"\">{t(\"studio_policy_permissive\")}</span>\n <span className=\"text-xs text-text-secondary dark:text-text-secondary-dark\">{t(\"studio_policy_permissive_desc\")}</span>\n </div>\n </SelectItem>\n <SelectItem value=\"RESTRICTIVE\">\n <div className=\"flex flex-col text-left\">\n <span className=\"\">{t(\"studio_policy_restrictive\")}</span>\n <span className=\"text-xs text-text-secondary dark:text-text-secondary-dark\">{t(\"studio_policy_restrictive_desc\")}</span>\n </div>\n </SelectItem>\n </Select>\n </div>\n </div>\n\n {/* Command */}\n <div className=\"flex flex-col gap-1.5\">\n <Typography variant=\"caption\" className=\"uppercase tracking-wider text-text-secondary\">\n {t(\"studio_policy_command\")} <code className=\"text-[10px] bg-surface-200 dark:bg-surface-950 text-text-secondary dark:text-text-secondary-dark px-1 py-0.5 rounded ml-1\">FOR</code>\n </Typography>\n <div className=\"flex flex-wrap gap-1.5\">\n {COMMAND_OPTIONS.map(cmd => (\n <Button\n key={cmd}\n size=\"small\"\n variant={command === cmd ? \"filled\" : \"text\"}\n color=\"neutral\"\n onClick={() => setCommand(cmd)}\n className=\"min-w-[80px]\"\n >\n {cmd}\n </Button>\n ))}\n </div>\n </div>\n\n {/* Roles */}\n <div className=\"flex flex-col gap-1.5\">\n <Typography variant=\"caption\" className=\"uppercase tracking-wider text-text-secondary\">\n {t(\"studio_policy_target_roles\")} <code className=\"text-[10px] bg-surface-200 dark:bg-surface-950 text-text-secondary dark:text-text-secondary-dark px-1 py-0.5 rounded ml-1\">TO</code>\n </Typography>\n <MultiSelect\n size=\"small\"\n value={roles}\n onValueChange={setRoles}\n placeholder={t(\"studio_policy_roles_placeholder\")}\n >\n {availableRoles.map(role => (\n <MultiSelectItem key={role} value={role}>\n {role}\n </MultiSelectItem>\n ))}\n </MultiSelect>\n </div>\n\n </Paper>\n\n <div className=\"mt-8 flex flex-col gap-4\">\n {/* USING Expression */}\n {command !== \"INSERT\" && (\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex flex-col gap-0.5\">\n <Typography variant=\"caption\" className=\"uppercase tracking-wider text-text-secondary\">{t(\"studio_policy_using_expr\")}</Typography>\n <Typography variant=\"caption\" className=\"text-text-secondary opacity-70\">{t(\"studio_policy_using_expr_desc\")}</Typography>\n </div>\n <div className={cls(\"h-32 border rounded-md overflow-hidden bg-white dark:bg-[#1e1e1e]\", defaultBorderMixin)}>\n <MonacoEditor\n value={usingExpr}\n onChange={(v) => setUsingExpr(v || \"\")}\n readOnly={false}\n autoFocus={true}\n />\n </div>\n </div>\n )}\n\n {/* WITH CHECK Expression */}\n {showCheck && (\n <div className=\"flex flex-col gap-1.5 mt-2\">\n <div className=\"flex flex-col gap-0.5\">\n <Typography variant=\"caption\" className=\"uppercase tracking-wider text-text-secondary\">{t(\"studio_policy_check_expr\")}</Typography>\n <Typography variant=\"caption\" className=\"text-text-secondary opacity-70\">{t(\"studio_policy_check_expr_desc\")}</Typography>\n </div>\n <div className={cls(\"h-32 border rounded-md overflow-hidden bg-white dark:bg-[#1e1e1e]\", defaultBorderMixin)}>\n <MonacoEditor\n value={checkExpr}\n onChange={(v) => setCheckExpr(v || \"\")}\n readOnly={false}\n autoFocus={command === \"INSERT\"}\n />\n </div>\n </div>\n )}\n </div>\n\n </div>\n </DialogContent>\n\n <DialogActions>\n <Button size=\"small\" variant=\"text\" color=\"neutral\" onClick={onCancel}>\n {t(\"studio_policy_cancel\")}\n </Button>\n <Button size=\"small\" variant=\"filled\" color=\"primary\" onClick={handleSave} disabled={!name}>\n {t(\"studio_policy_save\")}\n </Button>\n </DialogActions>\n\n <Dialog open={helpOpen} onOpenChange={setHelpOpen} maxWidth=\"3xl\">\n <DialogTitle hidden>Row-Level Security Help</DialogTitle>\n <DialogContent className=\"p-4 sm:p-6 lg:p-8 flex flex-col gap-6\">\n <div>\n <Typography variant=\"h5\" className=\"mb-2\">{t(\"studio_policy_help_title\")}</Typography>\n <Typography className=\"text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_policy_help_intro\")}\n </Typography>\n </div>\n\n <div className=\"flex flex-col gap-4\">\n <Paper className={cls(\"p-4 sm:p-5 flex flex-col gap-1\", defaultBorderMixin)}>\n <Typography variant=\"subtitle2\" className=\"text-primary dark:text-primary-light font-medium\">{t(\"studio_policy_help_step1_title\")}</Typography>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_policy_help_step1_desc\")}\n </Typography>\n </Paper>\n\n <Paper className={cls(\"p-4 sm:p-5 flex flex-col gap-1\", defaultBorderMixin)}>\n <Typography variant=\"subtitle2\" className=\"text-primary dark:text-primary-light font-medium\">{t(\"studio_policy_help_step2_title\")}</Typography>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark mb-1\">\n {t(\"studio_policy_help_step2_desc\")}\n </Typography>\n <ul className=\"list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark\">\n <li><strong>public</strong>: {t(\"studio_policy_help_role_public\")}</li>\n <li><strong>authenticated</strong>: {t(\"studio_policy_help_role_authenticated\")}</li>\n <li><strong>anon</strong>: {t(\"studio_policy_help_role_anon\")}</li>\n </ul>\n </Paper>\n\n <Paper className={cls(\"p-4 sm:p-5 flex flex-col gap-1\", defaultBorderMixin)}>\n <Typography variant=\"subtitle2\" className=\"text-primary dark:text-primary-light font-medium\">{t(\"studio_policy_help_step3_title\")}</Typography>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark mb-1\">\n {t(\"studio_policy_help_step3_desc\")}\n </Typography>\n <div className={cls(\"bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2\", defaultBorderMixin)}>\n Example: {RLS_UID_SQL} = uid\n </div>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_policy_help_step3_example\")}\n </Typography>\n </Paper>\n\n <Paper className={cls(\"p-4 sm:p-5 flex flex-col gap-1\", defaultBorderMixin)}>\n <Typography variant=\"subtitle2\" className=\"text-primary dark:text-primary-light font-medium\">{t(\"studio_policy_help_step4_title\")}</Typography>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark mb-1\">\n {t(\"studio_policy_help_step4_desc\")}\n </Typography>\n <div className={cls(\"bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2\", defaultBorderMixin)}>\n Example: {RLS_UID_SQL} = uid\n </div>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_policy_help_step4_example\")}\n </Typography>\n </Paper>\n\n <Paper className={cls(\"p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10\", defaultBorderMixin)}>\n <Typography variant=\"subtitle2\" className=\"text-primary dark:text-primary-light font-medium\">{t(\"studio_policy_help_auth_vars_title\")}</Typography>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_policy_help_auth_vars_desc\")}\n </Typography>\n <ul className=\"list-disc pl-5 space-y-2 text-sm text-text-secondary dark:text-text-secondary-dark font-normal\">\n <li>\n <code className=\"bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap\">{RLS_UID_SQL}</code>\n <span className=\"block mt-0.5\">Returns the current user's ID as text. Example: <code className=\"bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]\">{RLS_UID_SQL} = uid</code></span>\n </li>\n <li>\n <code className=\"bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap\">{RLS_JWT_SQL}</code>\n <span className=\"block mt-0.5\">Returns the full JWT payload as JSONB so you can check custom claims. Example: <code className=\"bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]\">{RLS_JWT_SQL} ->> 'email' = 'admin@example.com'</code></span>\n </li>\n <li>\n <code className=\"bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap\">{RLS_ROLES_SQL}</code>\n <span className=\"block mt-0.5\">Returns the user's role IDs as a comma-separated string. Best used with: <code className=\"bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]\">string_to_array({RLS_ROLES_SQL}, ',') @> ARRAY['admin']</code></span>\n </li>\n </ul>\n </Paper>\n </div>\n\n <div className={cls(\"mt-2 flex flex-col sm:flex-row justify-between items-start sm:items-center bg-primary/5 dark:bg-primary-bg-dark/10 p-4 rounded-xl border border-primary/10 dark:border-primary/20\", defaultBorderMixin)}>\n <Typography variant=\"body2\" className=\"text-primary dark:text-primary-light mb-4 sm:mb-0 max-w-md\">\n {t(\"studio_policy_help_docs_cta\")}\n </Typography>\n <Button\n component=\"a\"\n href=\"https://www.postgresql.org/docs/current/sql-createpolicy.html\"\n target=\"_blank\"\n variant=\"outlined\"\n color=\"primary\"\n size=\"small\"\n className=\"whitespace-nowrap flex-shrink-0\"\n >\n {t(\"studio_policy_help_read_docs\")}\n </Button>\n </div>\n </DialogContent>\n <DialogActions className=\"p-4 sm:px-6 sm:pb-6 pt-0 border-t-0\">\n <Button onClick={() => setHelpOpen(false)} variant=\"filled\" color=\"primary\">{t(\"studio_policy_help_got_it\")}</Button>\n </DialogActions>\n </Dialog>\n </>\n );\n};\n","/**\n * Write a collection's security rules to its source file.\n *\n * The schema-editor routes sit behind an admin gate that accepts a bearer token\n * and nothing else — there is no cookie fallback — so a request without an\n * `Authorization` header is a 401 every time. The RLS editor's two write paths\n * (\"Create policy\" on a mapped table, and \"Import to codebase\") both sent none,\n * and both reported the failure as a constant \"Failed to save policy\", which is\n * also what a 501 and a parse error looked like.\n *\n * It lives in its own module rather than inline in the component so that the\n * header can be asserted without rendering the whole RLS editor.\n */\nexport async function saveSecurityRulesToCodebase(options: {\n apiBase: string;\n collectionId: string;\n securityRules: unknown[];\n getAuthToken?: () => Promise<string | null | undefined>;\n}): Promise<void> {\n const { apiBase, collectionId, securityRules, getAuthToken } = options;\n\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n const token = getAuthToken ? await getAuthToken() : null;\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n\n const response = await fetch(`${apiBase}/schema-editor/collection/save`, {\n method: \"POST\",\n headers,\n // Only the rules change here. `partial` says so: read as a\n // whole-collection save, a payload of one key deletes everything it does\n // not mention.\n body: JSON.stringify({ collectionId,\ncollectionData: { securityRules },\npartial: true })\n });\n\n if (response.ok) return;\n\n const body = await response.text().catch(() => \"\");\n let message: string | undefined;\n try {\n const parsed = JSON.parse(body) as { error?: { message?: string }, message?: string };\n message = parsed.error?.message ?? parsed.message;\n } catch {\n message = body.trim() || undefined;\n }\n throw new Error(message ?? `Failed to save policy (HTTP ${response.status}).`);\n}\n","\nimport { useStudioCollectionRegistry, useStudioCapabilities } from \"@rebasepro/app\";\nimport { useApiBase, useApiConfig } from \"@rebasepro/app\";\nimport React, { useState, useEffect, useCallback, useMemo } from \"react\";\nimport {\n Alert,\n AlertTriangleIcon,\n Button,\n Chip,\n CircularProgress,\n cls,\n DatabaseIcon,\n defaultBorderMixin,\n IconButton,\n iconSize,\n KeyIcon,\n Link2Icon,\n LockIcon,\n Paper,\n RefreshCwIcon,\n ResizablePanels,\n ShieldIcon,\n Tab,\n Tabs,\n Tooltip,\n Trash2Icon,\n Typography\n} from \"@rebasepro/ui\";\nimport { useRebaseContext, useSnackbarController, ErrorView, useTranslation } from \"@rebasepro/app\";\nimport { isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport { REBASE_INTERNAL_SCHEMAS, REBASE_INTERNAL_PREFIXES, JUNCTION_TABLES_SQL } from \"@rebasepro/common\";\nimport { getPolicyNamesForRule, getPolicyNamesForRules, getPolicyOperations } from \"@rebasepro/utils\";\nimport { resolveJunctionSpecs, getJunctionSecurityRules, getEffectiveSecurityRules } from \"@rebasepro/common\";\nimport { PolicyEditor } from \"./PolicyEditor\";\nimport { saveSecurityRulesToCodebase } from \"./saveSecurityRules\";\n\ntype TableCategory = \"collection\" | \"junction\" | \"internal\" | \"other\";\n\nfunction classifyTableClient(\n tableName: string,\n schemaName: string,\n junctionTableNames: Set<string>,\n isMappedToCollection: boolean\n): TableCategory {\n if (\n REBASE_INTERNAL_SCHEMAS.includes(schemaName) ||\n REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))\n ) {\n return \"internal\";\n }\n if (isMappedToCollection) return \"collection\";\n if (junctionTableNames.has(tableName)) return \"junction\";\n return \"other\";\n}\n\n/**\n * Validates and double-quotes a SQL identifier to prevent injection.\n * Only allows safe Postgres identifiers (letters, digits, underscores).\n * Throws if the identifier contains unsafe characters.\n */\nfunction sanitizeSqlIdentifier(name: string): string {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n throw new Error(`Invalid SQL identifier: \"${name}\". Only letters, digits, and underscores are allowed.`);\n }\n return `\"${name}\"`;\n}\n\n// Re-exported for the components that used to import it from here.\nexport type { PostgresPolicy } from \"@rebasepro/types\";\nimport type { CollectionConfig, PostgresPolicy } from \"@rebasepro/types\";\n\ninterface TableRLSStatus {\n schemaName: string;\n tableName: string;\n rlsEnabled: boolean;\n policies: PostgresPolicy[];\n}\n\n// ─── Sidebar helper components ──────────────────────────────────────\n\nfunction SidebarSection({ title, icon, expanded, onToggle, count, children }: {\n title: string;\n icon: React.ReactNode;\n expanded: boolean;\n onToggle: () => void;\n count?: number;\n children: React.ReactNode;\n}) {\n return (\n <div className=\"mb-2\">\n <div\n className=\"flex items-center p-1.5 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors\"\n onClick={onToggle}\n >\n <svg className={cls(\"w-3 h-3 mr-1.5 transition-transform text-text-disabled dark:text-text-disabled-dark\", expanded ? \"rotate-90\" : \"\")} fill=\"currentColor\" viewBox=\"0 0 20 20\"><path d=\"M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z\"/></svg>\n {icon}\n <Typography variant=\"body2\" className=\"text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow ml-1.5\">{title}</Typography>\n {count !== undefined && (\n <span className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark font-medium tabular-nums mr-1\">{count}</span>\n )}\n </div>\n {expanded && (\n <div className=\"ml-3 mt-0.5 space-y-0.5\">\n {children}\n </div>\n )}\n </div>\n );\n}\n\nfunction SidebarTableRow({ table, isSelected, onSelect, badge, dimmed, t }: {\n table: TableRLSStatus;\n isSelected: boolean;\n onSelect: () => void;\n badge?: string;\n dimmed?: boolean;\n t: (key: string) => string;\n}) {\n return (\n <div\n onClick={onSelect}\n className={cls(\n \"flex items-center p-1 cursor-pointer rounded transition-colors group relative\",\n isSelected\n ? \"bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-light\"\n : \"hover:bg-surface-100 dark:hover:bg-surface-900 text-text-secondary dark:text-text-secondary-dark\",\n dimmed && !isSelected && \"opacity-60\"\n )}\n >\n <svg className=\"w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"/></svg>\n <Typography variant=\"body2\" className=\"text-xs truncate flex-1 min-w-0\">{table.tableName}</Typography>\n <div className=\"flex items-center gap-1.5 shrink-0 ml-2\">\n {badge && (\n <span className=\"text-[9px] uppercase tracking-wider font-semibold text-text-disabled dark:text-text-disabled-dark bg-surface-200 dark:bg-surface-800 rounded px-1 py-px\">\n {badge}\n </span>\n )}\n {table.rlsEnabled ? (\n <Tooltip title={t(\"studio_rls_enabled\")}>\n <div className=\"w-1.5 h-1.5 rounded-full bg-green-500\"/>\n </Tooltip>\n ) : (\n <Tooltip title={t(\"studio_rls_disabled\")}>\n <div className=\"w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50\"/>\n </Tooltip>\n )}\n <span className=\"text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium\">\n {table.policies.length}\n </span>\n </div>\n </div>\n );\n}\n\nexport const RLSEditor = ({ apiUrl = \"\" }: { apiUrl?: string }) => {\n const { databaseAdmin } = useRebaseContext();\n const snackbarController = useSnackbarController();\n const collectionRegistry = useStudioCollectionRegistry();\n const { codebase: hasCodebase } = useStudioCapabilities();\n const apiConfig = useApiConfig();\n /* `apiUrl` is a bare origin; the routes live under the backend's\n `basePath`, which is `/api` only by default. */\n const apiBase = useApiBase() ?? `${apiUrl.replace(/\\/+$/, \"\")}/api`;\n\n const saveSecurityRules = useCallback(\n (collectionId: string, securityRules: unknown[]) => saveSecurityRulesToCodebase({\n apiBase,\n collectionId,\n securityRules,\n getAuthToken: apiConfig?.getAuthToken\n }),\n [apiBase, apiConfig]);\n const { t } = useTranslation();\n\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [tables, setTables] = useState<TableRLSStatus[]>([]);\n const [selectedTable, setSelectedTable] = useState<string | null>(null);\n const [activeTab, setActiveTab] = useState(0);\n const [junctionTableNames, setJunctionTableNames] = useState<Set<string>>(new Set());\n\n const [editingPolicy, setEditingPolicy] = useState<PostgresPolicy | \"new\" | null>(null);\n\n /**\n * Native PostgreSQL roles, for the policy editor's `TO` list.\n *\n * `fetchAvailableRoles` and not `fetchApplicationRoles`: a policy's `TO`\n * clause names database roles, and an application role put there produces a\n * policy that either fails to create or matches nothing. The editor falls\n * back to a static pair when the driver cannot answer.\n */\n const [pgRoleOptions, setPgRoleOptions] = useState<string[]>([]);\n\n useEffect(() => {\n let mounted = true;\n if (!databaseAdmin?.fetchAvailableRoles) return;\n databaseAdmin.fetchAvailableRoles()\n .then((roles) => { if (mounted) setPgRoleOptions(roles); })\n .catch((e) => console.error(\"Failed to fetch database roles\", e));\n return () => { mounted = false; };\n }, [databaseAdmin]);\n\n const [sidebarSize, setSidebarSize] = useState(() => {\n try {\n const saved = localStorage.getItem(\"rebase_rls_editor_sidebar_size\");\n return saved !== null ? parseFloat(saved) : 20;\n } catch (e) {\n return 20;\n }\n });\n\n const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({\n collection: true,\n other: true,\n internal: false\n });\n\n // Sidebar tab: \"tables\" or \"info\"\n const [sidebarTab, setSidebarTab] = useState<\"tables\" | \"info\">(\"tables\");\n\n useEffect(() => {\n try {\n localStorage.setItem(\"rebase_rls_editor_sidebar_size\", sidebarSize.toString());\n } catch (e) { /* ignore */ }\n }, [sidebarSize]);\n\n const fetchRLSData = useCallback(async () => {\n if (!databaseAdmin?.executeSql) {\n setError(t(\"studio_sql_sql_not_supported\"));\n setIsLoading(false);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n try {\n // 1. Fetch tables and whether RLS is enabled\n const tablesSql = `\n SELECT \n schemaname, \n tablename, \n rowsecurity \n FROM pg_tables \n WHERE schemaname NOT IN ('information_schema', 'pg_catalog')\n ORDER BY schemaname, tablename;\n `;\n const tablesResult = await databaseAdmin!.executeSql!(tablesSql);\n\n // 2. Fetch all policies\n const policiesSql = `\n SELECT \n schemaname,\n tablename,\n policyname,\n permissive,\n roles,\n cmd,\n qual,\n with_check\n FROM pg_policies\n WHERE schemaname NOT IN ('information_schema', 'pg_catalog');\n `;\n const policiesResult = await databaseAdmin!.executeSql!(policiesSql);\n\n const extractRows = (result: unknown): Record<string, unknown>[] => {\n if (result && typeof result === \"object\" && \"rows\" in result && Array.isArray((result as { rows: Record<string, unknown>[] }).rows)) {\n return (result as { rows: Record<string, unknown>[] }).rows;\n }\n if (Array.isArray(result)) return result as Record<string, unknown>[];\n return [];\n };\n\n const tRows = extractRows(tablesResult);\n const pRows = extractRows(policiesResult);\n\n const tableMap: Record<string, TableRLSStatus> = {};\n\n tRows.forEach((tRow: Record<string, unknown>) => {\n const t = tRow as { schemaname?: string, SCHEMANAME?: string, tablename?: string, TABLENAME?: string, rowsecurity?: boolean, ROWSECURITY?: boolean };\n const schema = t.schemaname || t.SCHEMANAME || \"public\";\n const table = t.tablename || t.TABLENAME || \"\";\n const rlsEnabled = t.rowsecurity || t.ROWSECURITY || false;\n\n const key = `${schema}.${table}`;\n tableMap[key] = {\n schemaName: schema,\n tableName: table,\n rlsEnabled: rlsEnabled,\n policies: []\n };\n });\n\n pRows.forEach((pRow: Record<string, unknown>) => {\n const p = pRow as { schemaname?: string, SCHEMANAME?: string, tablename?: string, TABLENAME?: string, roles?: string | string[], ROLES?: string | string[], policyname?: string, POLICYNAME?: string, permissive?: \"PERMISSIVE\" | \"RESTRICTIVE\", PERMISSIVE?: \"PERMISSIVE\" | \"RESTRICTIVE\", cmd?: \"SELECT\" | \"INSERT\" | \"UPDATE\" | \"DELETE\" | \"ALL\", CMD?: \"SELECT\" | \"INSERT\" | \"UPDATE\" | \"DELETE\" | \"ALL\", qual?: string | null, QUAL?: string | null, with_check?: string | null, WITH_CHECK?: string | null };\n const schema = p.schemaname || p.SCHEMANAME || \"public\";\n const table = p.tablename || p.TABLENAME || \"\";\n const key = `${schema}.${table}`;\n\n if (tableMap[key]) {\n // Postgres roles come back as an array string like \"{public}\" or literal array\n let parsedRoles: string[] = [];\n const r = p.roles || p.ROLES;\n if (Array.isArray(r)) {\n parsedRoles = r;\n } else if (typeof r === \"string\") {\n parsedRoles = r.replace(/^{|}$/g, \"\").split(\",\").map(s => s.trim());\n }\n\n tableMap[key].policies.push({\n policyname: p.policyname || p.POLICYNAME || \"\",\n tablename: table,\n permissive: p.permissive || p.PERMISSIVE || \"PERMISSIVE\",\n roles: parsedRoles,\n cmd: p.cmd || p.CMD || \"ALL\",\n qual: p.qual || p.QUAL || null,\n with_check: p.with_check || p.WITH_CHECK || null\n });\n }\n });\n\n const sortedTables = Object.values(tableMap).sort((a, b) => a.tableName.localeCompare(b.tableName));\n setTables(sortedTables);\n\n // Detect junction tables (where all columns are FKs)\n try {\n const junctionResult = await databaseAdmin!.executeSql!(JUNCTION_TABLES_SQL);\n const jRows = extractRows(junctionResult);\n setJunctionTableNames(new Set(\n jRows.map((r: Record<string, unknown>) => r.table_name as string).filter(Boolean)\n ));\n } catch {\n // Junction detection is best-effort\n }\n\n if (sortedTables.length > 0 && !selectedTable) {\n // Auto-select first public/collection table, skip internal\n const firstUserTable = sortedTables.find(t => !REBASE_INTERNAL_SCHEMAS.includes(t.schemaName));\n if (firstUserTable) {\n setSelectedTable(`${firstUserTable.schemaName}.${firstUserTable.tableName}`);\n } else {\n setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);\n }\n }\n\n } catch (e: unknown) {\n console.error(\"RLS fetch error:\", e);\n setError(\"Failed to fetch RLS policies: \" + (e instanceof Error ? e.message : String(e)));\n } finally {\n setIsLoading(false);\n }\n }, [databaseAdmin, selectedTable]);\n\n useEffect(() => {\n setEditingPolicy(null);\n }, [selectedTable]);\n\n useEffect(() => {\n fetchRLSData();\n }, [fetchRLSData]);\n\n const activeTableData = useMemo(() => {\n if (!selectedTable) return null;\n return tables.find(t => `${t.schemaName}.${t.tableName}` === selectedTable) || null;\n }, [selectedTable, tables]);\n\n /** Categorize tables into 4 buckets for the sidebar. */\n const categorizedTables = useMemo(() => {\n const groups: Record<TableCategory, TableRLSStatus[]> = {\n collection: [],\n junction: [],\n internal: [],\n other: []\n };\n\n tables.forEach(table => {\n const isMapped = !!collectionRegistry.collections?.find(\n (c: { id?: string, path?: string, table?: string, slug?: string, collectionId?: string }) =>\n c.id === table.tableName ||\n c.path === table.tableName ||\n c.table === table.tableName ||\n c.slug === table.tableName ||\n c.collectionId === table.tableName\n );\n const cat = classifyTableClient(table.tableName, table.schemaName, junctionTableNames, isMapped);\n groups[cat].push(table);\n });\n\n return groups;\n }, [tables, junctionTableNames, collectionRegistry.collections]);\n\n const activeCollection = useMemo(() => {\n if (!activeTableData) return null;\n return collectionRegistry.collections?.find((c: { id?: string, path?: string, table?: string, slug?: string, collectionId?: string }) =>\n c.id === activeTableData.tableName ||\n c.path === activeTableData.tableName ||\n c.table === activeTableData.tableName ||\n c.slug === activeTableData.tableName ||\n c.collectionId === activeTableData.tableName\n ) || null;\n }, [activeTableData, collectionRegistry.collections]);\n\n /** The category of the currently selected table. */\n const activeTableCategory = useMemo((): TableCategory | null => {\n if (!activeTableData) return null;\n const isMapped = !!activeCollection;\n return classifyTableClient(activeTableData.tableName, activeTableData.schemaName, junctionTableNames, isMapped);\n }, [activeTableData, activeCollection, junctionTableNames]);\n\n const mergedPolicies = useMemo(() => {\n if (!activeTableData) return [];\n\n const policiesMap: Record<string, PostgresPolicy> = {};\n\n // Load live policies\n (activeTableData.policies || []).forEach(p => {\n policiesMap[p.policyname] = { ...p,\nstatus: \"live\" };\n });\n\n // Merge code-based policies.\n //\n // A rule without an explicit `name` still produces policies — Postgres gets\n // `<table>_<op>_<hash>`, one per operation. Skipping those rules left their\n // live policies looking like hand-written SQL (\"DB Only\"), so derive the\n // names the generator would emit and match on those.\n //\n // `getEffectiveSecurityRules` rather than `securityRules`, for the same\n // reason one step further out: the generator also injects the\n // safe-by-default baseline (`<table>_default_admin_read` and the three\n // `_default_admin_write_*`), which appears in no collection's\n // `securityRules`. Reading the declared rules alone made four policies\n // *Rebase itself wrote* look like drift on every table in the project —\n // badged \"DB Only\" and offered for import back into the codebase that\n // produced them. The admin panel's own RLS tab already derived them this\n // way; this view, its sibling, did not. Both start from\n // `getEffectiveSecurityRules` now — this one needs the rule bodies to\n // render a policy row, the other only the names it compiles to\n // (`getGeneratedPolicyNames`).\n if (activeCollection && isPostgresCollectionConfig(activeCollection)) {\n getEffectiveSecurityRules(activeCollection as unknown as CollectionConfig).forEach((rule) => {\n const ops = getPolicyOperations(rule);\n const policyNames = getPolicyNamesForRule(rule, activeTableData.tableName);\n\n policyNames.forEach((policyName, opIdx) => {\n policiesMap[policyName] = {\n policyname: policyName,\n tablename: activeTableData.tableName,\n permissive: (rule.mode || \"permissive\").toUpperCase() as PostgresPolicy[\"permissive\"],\n cmd: (ops[opIdx] ?? rule.operation ?? \"ALL\").toUpperCase() as PostgresPolicy[\"cmd\"],\n // `pgRoles`, not `roles`. This is a policy's `TO` list —\n // database roles — while `SecurityRule.roles` holds\n // *application* roles, which the generator compiles into\n // the USING clause via `rebase.roles()` and never puts in\n // the `TO` list. Reading the wrong field made every rule\n // scoped `roles: [\"admin\"]` render as `TO admin`, a\n // policy the framework has never written.\n roles: [...(rule.pgRoles ?? [\"public\"])],\n qual: rule.using || null,\n with_check: rule.withCheck || null,\n // \"both\" = defined in code and live in Postgres (potentially edited)\n status: policiesMap[policyName] ? \"both\" : \"code_only\"\n };\n });\n });\n }\n\n // Junction tables have no collection, but their policies are generated\n // too — derived from the endpoints' relations. Recognise them so they\n // don't show as hand-written SQL (\"DB Only\"). If the registry's\n // collections don't carry resolvable relations, they simply stay \"live\".\n if (!activeCollection) {\n try {\n const registryCollections = (collectionRegistry.collections ?? []) as unknown as Parameters<typeof resolveJunctionSpecs>[0];\n const spec = resolveJunctionSpecs(registryCollections).get(activeTableData.tableName);\n if (spec) {\n const generatedNames = getPolicyNamesForRules(getJunctionSecurityRules(spec), spec.table);\n for (const p of Object.values(policiesMap)) {\n if (generatedNames.has(p.policyname)) p.status = \"both\";\n }\n }\n } catch {\n /* serialized configs without relation closures — leave as live */\n }\n }\n\n return Object.values(policiesMap).sort((a, b) => a.policyname.localeCompare(b.policyname));\n }, [activeTableData, activeCollection, collectionRegistry.collections]);\n\n // Stats for the info tab\n const rlsStats = useMemo(() => {\n const total = tables.length;\n const enabled = tables.filter(t => t.rlsEnabled).length;\n const withPolicies = tables.filter(t => t.policies.length > 0).length;\n const totalPolicies = tables.reduce((sum, t) => sum + t.policies.length, 0);\n return { total,\nenabled,\nwithPolicies,\ntotalPolicies };\n }, [tables]);\n\n const renderPolicyTag = (label: string, value: string) => {\n return (\n <div className=\"flex items-center gap-1.5 px-2 py-1 rounded-md bg-surface-100 dark:bg-surface-950 border border-surface-200 dark:border-surface-700/50\">\n <span className=\"text-[10px] uppercase text-text-secondary dark:text-text-secondary-dark font-medium tracking-wider\">\n {label}:\n </span>\n <span className=\"font-mono text-xs text-text-primary dark:text-text-primary-dark break-all\">\n {value}\n </span>\n </div>\n );\n };\n\n return (\n <div className=\"flex h-full w-full bg-white dark:bg-surface-950 overflow-hidden text-text-primary dark:text-text-primary-dark\">\n <ResizablePanels\n orientation=\"horizontal\"\n panelSizePercent={sidebarSize}\n onPanelSizeChange={setSidebarSize}\n minPanelSizePx={220}\n firstPanel={\n <div className={cls(\"flex flex-col h-full w-full bg-white dark:bg-surface-950 border-r\", defaultBorderMixin)}>\n <Tabs value={sidebarTab} onValueChange={(v) => setSidebarTab(v as \"tables\" | \"info\")} variant=\"boxy\" className=\"border-b border-surface-200 dark:border-surface-950\">\n <Tab value=\"tables\">Tables</Tab>\n <Tab value=\"info\">Info</Tab>\n </Tabs>\n\n <div className=\"flex-grow overflow-hidden relative\">\n {sidebarTab === \"tables\" && (\n <div className=\"flex flex-col h-full\">\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\">\n {t(\"studio_schema_tables\")}\n </Typography>\n <IconButton size=\"small\" onClick={fetchRLSData} title=\"Refresh\">\n <RefreshCwIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n <div className=\"flex-grow overflow-y-auto no-scrollbar p-1\">\n {isLoading && tables.length === 0 ? (\n <div className=\"flex justify-center p-4\"><CircularProgress size=\"small\"/></div>\n ) : tables.length === 0 ? (\n <div className=\"p-4 text-center\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark italic\">{t(\"studio_rls_no_tables\")}</Typography>\n </div>\n ) : (\n <>\n {/* ── Schema Collections ── */}\n {categorizedTables.collection.length > 0 && (\n <SidebarSection\n title=\"Schema Collections\"\n icon={<DatabaseIcon size={12} className=\"text-primary dark:text-primary-light\"/>}\n expanded={expandedSections.collection ?? true}\n onToggle={() => setExpandedSections(prev => ({ ...prev, collection: !(prev.collection ?? true) }))}\n >\n {categorizedTables.collection.map(table => (\n <SidebarTableRow\n key={`${table.schemaName}.${table.tableName}`}\n table={table}\n isSelected={selectedTable === `${table.schemaName}.${table.tableName}`}\n onSelect={() => setSelectedTable(`${table.schemaName}.${table.tableName}`)}\n t={t}\n />\n ))}\n </SidebarSection>\n )}\n\n {/* ── Other Tables (unmapped + junction) ── */}\n {(categorizedTables.other.length > 0 || categorizedTables.junction.length > 0) && (\n <SidebarSection\n title=\"Other Tables\"\n icon={<AlertTriangleIcon size={12} className=\"text-yellow-500 dark:text-yellow-400\"/>}\n expanded={expandedSections.other ?? true}\n onToggle={() => setExpandedSections(prev => ({ ...prev, other: !(prev.other ?? true) }))}\n >\n {categorizedTables.other.map(table => (\n <SidebarTableRow\n key={`${table.schemaName}.${table.tableName}`}\n table={table}\n isSelected={selectedTable === `${table.schemaName}.${table.tableName}`}\n onSelect={() => setSelectedTable(`${table.schemaName}.${table.tableName}`)}\n t={t}\n />\n ))}\n {categorizedTables.junction.map(table => (\n <SidebarTableRow\n key={`${table.schemaName}.${table.tableName}`}\n table={table}\n isSelected={selectedTable === `${table.schemaName}.${table.tableName}`}\n onSelect={() => setSelectedTable(`${table.schemaName}.${table.tableName}`)}\n badge=\"Junction\"\n dimmed\n t={t}\n />\n ))}\n </SidebarSection>\n )}\n\n {/* ── Rebase Internal ── */}\n {categorizedTables.internal.length > 0 && (\n <SidebarSection\n title=\"Rebase Internal\"\n icon={<LockIcon size={12} className=\"text-text-disabled dark:text-text-disabled-dark\"/>}\n expanded={expandedSections.internal ?? false}\n onToggle={() => setExpandedSections(prev => ({ ...prev, internal: !(prev.internal ?? false) }))}\n count={categorizedTables.internal.length}\n >\n {categorizedTables.internal.map(table => (\n <SidebarTableRow\n key={`${table.schemaName}.${table.tableName}`}\n table={table}\n isSelected={selectedTable === `${table.schemaName}.${table.tableName}`}\n onSelect={() => setSelectedTable(`${table.schemaName}.${table.tableName}`)}\n dimmed\n t={t}\n />\n ))}\n </SidebarSection>\n )}\n </>\n )}\n </div>\n </div>\n )}\n\n {sidebarTab === \"info\" && (\n <div className=\"flex flex-col h-full\">\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\">\n Overview\n </Typography>\n </div>\n <div className=\"flex-grow overflow-y-auto p-3 space-y-3 no-scrollbar\">\n <div className={cls(\"p-3 rounded-lg border bg-white dark:bg-surface-900\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-2 mb-2\">\n <ShieldIcon size={iconSize.smallest} className=\"text-primary\"/>\n <Typography variant=\"body2\" className=\"font-semibold text-[13px]\">RLS Studio</Typography>\n </div>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark text-[11px] leading-relaxed block\">\n Manage Row Level Security policies for your PostgreSQL tables. Enable RLS and create fine-grained access policies.\n </Typography>\n </div>\n\n <div className=\"space-y-2\">\n <div className={cls(\"p-2.5 rounded border bg-white dark:bg-surface-900 flex items-center justify-between\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark text-[11px]\">Total tables</Typography>\n <Typography variant=\"body2\" className=\"font-mono text-[13px] font-medium\">{rlsStats.total}</Typography>\n </div>\n <div className={cls(\"p-2.5 rounded border bg-white dark:bg-surface-900 flex items-center justify-between\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark text-[11px]\">RLS enabled</Typography>\n <div className=\"flex items-center gap-1.5\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-green-500\"/>\n <Typography variant=\"body2\" className=\"font-mono text-[13px] font-medium\">{rlsStats.enabled}</Typography>\n </div>\n </div>\n <div className={cls(\"p-2.5 rounded border bg-white dark:bg-surface-900 flex items-center justify-between\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark text-[11px]\">Tables with policies</Typography>\n <Typography variant=\"body2\" className=\"font-mono text-[13px] font-medium\">{rlsStats.withPolicies}</Typography>\n </div>\n <div className={cls(\"p-2.5 rounded border bg-white dark:bg-surface-900 flex items-center justify-between\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark text-[11px]\">Total policies</Typography>\n <Typography variant=\"body2\" className=\"font-mono text-[13px] font-medium\">{rlsStats.totalPolicies}</Typography>\n </div>\n </div>\n\n {/* Security health indicators */}\n {rlsStats.total - rlsStats.enabled > 0 && (\n <div className={cls(\"p-2.5 rounded border border-yellow-200 dark:border-yellow-900/50 bg-yellow-50 dark:bg-yellow-900/20 flex items-start gap-2\", defaultBorderMixin)}>\n <AlertTriangleIcon size={14} className=\"text-yellow-600 dark:text-yellow-500 mt-0.5 shrink-0\"/>\n <div>\n <Typography variant=\"caption\" className=\"text-yellow-800 dark:text-yellow-400 text-[11px] font-semibold block\">\n {rlsStats.total - rlsStats.enabled} table{rlsStats.total - rlsStats.enabled > 1 ? \"s\" : \"\"} without RLS\n </Typography>\n <Typography variant=\"caption\" className=\"text-yellow-700 dark:text-yellow-600 text-[10px] block mt-0.5\">\n These tables have no row-level access control. If auth enforcement is disabled, data may be publicly accessible.\n </Typography>\n </div>\n </div>\n )}\n\n {rlsStats.enabled > 0 && rlsStats.enabled - rlsStats.withPolicies > 0 && (\n <div className={cls(\"p-2.5 rounded border border-blue-200 dark:border-blue-900/50 bg-blue-50 dark:bg-blue-900/20 flex items-start gap-2\", defaultBorderMixin)}>\n <ShieldIcon size={14} className=\"text-blue-600 dark:text-blue-400 mt-0.5 shrink-0\"/>\n <div>\n <Typography variant=\"caption\" className=\"text-blue-800 dark:text-blue-300 text-[11px] font-semibold block\">\n {rlsStats.enabled - rlsStats.withPolicies} table{rlsStats.enabled - rlsStats.withPolicies > 1 ? \"s\" : \"\"} with RLS but no policies\n </Typography>\n <Typography variant=\"caption\" className=\"text-blue-700 dark:text-blue-500 text-[10px] block mt-0.5\">\n RLS is enabled but no permissive policies exist. All access is denied by default (Postgres deny-all).\n </Typography>\n </div>\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n }\n secondPanel={\n <div className=\"flex-grow flex flex-col min-w-0 h-full w-full bg-white dark:bg-surface-950\">\n {/* Toolbar Header matching SQL/JS Editor style */}\n <div className={cls(\"flex items-center justify-between pr-2 border-b bg-white dark:bg-surface-950 min-h-[46px]\", defaultBorderMixin)}>\n <div className=\"flex items-center flex-grow overflow-hidden px-4\">\n <Typography variant=\"subtitle2\" className=\"font-mono text-text-secondary dark:text-text-secondary-dark truncate\">\n {activeTableData ? `${activeTableData.schemaName}.${activeTableData.tableName}` : t(\"studio_rls_select_table\")}\n </Typography>\n {activeTableData && (\n <div className=\"ml-3\">\n {activeTableData.rlsEnabled ? (\n <Chip size=\"smallest\" className=\"bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400 border-green-200 dark:border-green-800\">{t(\"studio_rls_enabled\")}</Chip>\n ) : (\n <Chip size=\"smallest\" className=\"bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800\">{t(\"studio_rls_disabled\")}</Chip>\n )}\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center justify-end gap-1.5\">\n {activeTableData && (\n <>\n <Button\n variant=\"text\"\n size=\"small\"\n onClick={async () => {\n const table = activeTableData.tableName;\n const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;\n const action = activeTableData.rlsEnabled ? \"DISABLE\" : \"ENABLE\";\n if (!confirm(`Are you sure you want to ${action.toLowerCase()} Row Level Security on \"${table}\"?`)) return;\n try {\n await databaseAdmin!.executeSql!(`ALTER TABLE ${qualifiedTable} ${action} ROW LEVEL SECURITY`);\n snackbarController.open({ type: \"success\",\nmessage: `RLS ${action.toLowerCase()}d on ${table}` });\n fetchRLSData();\n } catch (e: unknown) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n }\n }}\n >\n {activeTableData.rlsEnabled ? t(\"studio_rls_disable_rls\") : t(\"studio_rls_enable_rls\")}\n </Button>\n\n <div className=\"h-4 w-px bg-surface-200 dark:bg-surface-950 mx-1\"/>\n\n <Button\n variant=\"text\"\n size=\"small\"\n onClick={fetchRLSData}\n startIcon={<RefreshCwIcon size={iconSize.smallest}/>}\n >\n Refresh\n </Button>\n\n <div className=\"h-4 w-px bg-surface-200 dark:bg-surface-950 mx-1\"/>\n\n <Button\n size=\"small\"\n color=\"primary\"\n onClick={() => setEditingPolicy(\"new\")}\n >\n {t(\"studio_rls_create_policy\")}\n </Button>\n </>\n )}\n </div>\n </div>\n\n {isLoading && !activeTableData ? (\n <div className=\"flex-grow flex items-center justify-center h-full\">\n <CircularProgress size=\"small\"/>\n </div>\n ) : error ? (\n <div className=\"p-6 h-full flex items-center justify-center\">\n <ErrorView title={t(\"studio_rls_error\")} error={error} onRetry={fetchRLSData}/>\n </div>\n ) : !activeTableData ? (\n <div className=\"flex-grow flex items-center justify-center text-text-disabled h-full\">\n <div className=\"text-center\">\n <svg className=\"w-12 h-12 mx-auto mb-4 opacity-50\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1} d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"/></svg>\n <Typography variant=\"body2\">{t(\"studio_rls_select_table\")}</Typography>\n </div>\n </div>\n ) : editingPolicy ? (\n <PolicyEditor\n policy={editingPolicy === \"new\" ? undefined : editingPolicy}\n schema={activeTableData.schemaName}\n table={activeTableData.tableName}\n roleOptions={pgRoleOptions}\n onSave={async (newPolicy) => {\n /*\n * Where a policy for a *mapped* table belongs depends\n * on the host, not on the table.\n *\n * Beside its own source, the collection file is the\n * right home: the rule is checked in, and the next\n * migration applies it. The hosted console has no\n * source — the container is rebuilt from the\n * customer's repository on every deploy — and the\n * schema-editor routes it would POST to are not even\n * mounted, because the framework switches them off\n * under NODE_ENV=production, which every tenant runs.\n * So this branch used to make \"Create Policy\" and\n * \"Edit\" fail with \"Failed to save policy\" on every\n * mapped table in the console, which is most of them.\n * There, the database is the only place a policy can\n * live, so write it there — the same path unmapped\n * tables have always used.\n */\n if (activeCollection && hasCodebase) {\n // Collection-mapped table: save via schema-editor API\n const rule: Record<string, unknown> = {\n name: newPolicy.policyname,\n operation: newPolicy.cmd?.toLowerCase(),\n mode: newPolicy.permissive?.toLowerCase(),\n using: newPolicy.qual || undefined,\n withCheck: newPolicy.with_check || undefined,\n // The editor edits a `PostgresPolicy`, whose\n // `roles` is the `TO` list — so it maps to\n // `pgRoles`. Writing it to `roles` filed\n // database roles as *application* roles, and\n // the generator then compiled them into a\n // `rebase.roles()` check no user could\n // satisfy: the rule saved cleanly, pushed\n // cleanly, and matched nothing.\n //\n // Omitted at the default so a rule that\n // targets `public` — nearly all of them —\n // does not carry an advanced field it does\n // not need.\n ...(newPolicy.roles && !(newPolicy.roles.length === 1 && newPolicy.roles[0] === \"public\")\n ? { pgRoles: newPolicy.roles }\n : {})\n };\n\n const existingRules = (isPostgresCollectionConfig(activeCollection) ? activeCollection.securityRules : undefined) || [];\n let newRules;\n if (editingPolicy === \"new\") {\n newRules = [...existingRules, rule];\n } else {\n newRules = existingRules.map((r: { name?: string }) => r.name === editingPolicy.policyname ? rule : r);\n }\n\n try {\n await saveSecurityRules(\n (activeCollection as { id?: string, path?: string, alias?: string }).id || (activeCollection as { id?: string, path?: string, alias?: string }).path || (activeCollection as { id?: string, path?: string, alias?: string }).alias || activeTableData.tableName,\n newRules\n );\n\n snackbarController.open({ type: \"success\",\nmessage: \"Policy saved successfully\" });\n setEditingPolicy(null);\n fetchRLSData();\n } catch (e: unknown) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n }\n } else {\n // No codebase to write to (hosted console), or an\n // unmapped table (internal/junction/other): apply\n // the policy to the database directly.\n try {\n const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(activeTableData.tableName)}`;\n const policyName = sanitizeSqlIdentifier(newPolicy.policyname || \"unnamed_policy\");\n const cmd = newPolicy.cmd || \"ALL\";\n const permissive = (newPolicy.permissive || \"PERMISSIVE\") === \"PERMISSIVE\" ? \"PERMISSIVE\" : \"RESTRICTIVE\";\n const roles = newPolicy.roles && newPolicy.roles.length > 0\n ? newPolicy.roles.map(r => sanitizeSqlIdentifier(r)).join(\", \")\n : \"public\";\n\n // Drop existing policy if editing\n if (editingPolicy !== \"new\") {\n await databaseAdmin!.executeSql!(`DROP POLICY IF EXISTS ${policyName} ON ${qualifiedTable}`);\n }\n\n let sql = `CREATE POLICY ${policyName} ON ${qualifiedTable}`;\n sql += ` AS ${permissive}`;\n sql += ` FOR ${cmd}`;\n sql += ` TO ${roles}`;\n if (newPolicy.qual) sql += ` USING (${newPolicy.qual})`;\n if (newPolicy.with_check) sql += ` WITH CHECK (${newPolicy.with_check})`;\n\n await databaseAdmin!.executeSql!(sql);\n\n snackbarController.open({ type: \"success\",\nmessage: `Policy \"${newPolicy.policyname}\" applied to the database` });\n setEditingPolicy(null);\n fetchRLSData();\n } catch (e: unknown) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n }\n }\n }}\n onCancel={() => setEditingPolicy(null)}\n />\n ) : (\n <div className=\"flex-grow flex flex-col overflow-hidden\">\n <div className=\"p-6 pt-4 flex-grow overflow-auto bg-surface-50 dark:bg-surface-900\">\n <div className=\"max-w-4xl mx-auto flex flex-col gap-6\">\n {/* Context-aware banner based on table category */}\n {activeTableData && activeTableCategory === \"internal\" && (\n <Alert color=\"info\">\n <div className=\"flex items-start gap-2\">\n <LockIcon size={16} className=\"shrink-0 mt-0.5\"/>\n <div>\n <Typography variant=\"body2\" className=\"mb-1 font-semibold\">\n Rebase System Table\n </Typography>\n <Typography variant=\"caption\" className=\"opacity-80\">\n This table is managed internally by Rebase. Its security policies are configured automatically.\n Editing policies on system tables is an advanced operation.\n </Typography>\n </div>\n </div>\n </Alert>\n )}\n {activeTableData && activeTableCategory === \"junction\" && (\n <Alert color=\"info\">\n <div className=\"flex items-start gap-2\">\n <Link2Icon size={16} className=\"shrink-0 mt-0.5\"/>\n <div>\n <Typography variant=\"body2\" className=\"mb-1 font-semibold\">\n Junction Table\n </Typography>\n <Typography variant=\"caption\" className=\"opacity-80\">\n This is an auto-generated junction table for a many-to-many relation.\n Rebase derives its policies: rows are readable when both related rows\n are, and writable following the declaring collection's update rules\n (plus the server/admin baseline). You can still add RLS policies\n directly to broaden access.\n </Typography>\n </div>\n </div>\n </Alert>\n )}\n {activeTableData && activeTableCategory === \"other\" && (\n <Alert color=\"warning\">\n <div className=\"flex items-start gap-2\">\n <AlertTriangleIcon size={16} className=\"shrink-0 mt-0.5\"/>\n <div>\n <Typography variant=\"body2\" className=\"mb-1 font-semibold\">\n Unmapped Table\n </Typography>\n <Typography variant=\"caption\" className=\"opacity-80\">\n This table exists in the database but isn't mapped to a collection definition.\n Import it into a Schema configuration file to manage security policies visually.\n </Typography>\n </div>\n </div>\n </Alert>\n )}\n\n {activeTableData && !activeTableData.rlsEnabled && (\n <div className={cls(\"p-4 sm:p-5 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-900/50 rounded-lg flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between\", defaultBorderMixin)}>\n <div className=\"flex gap-3 items-start\">\n <div className=\"mt-1 bg-yellow-100 dark:bg-yellow-900/50 p-1.5 rounded-md shrink-0 flex items-center justify-center\">\n <AlertTriangleIcon size={iconSize.smallest}/>\n </div>\n <div>\n <Typography variant=\"subtitle2\" className=\"text-yellow-800 dark:text-yellow-500\">\n Row Level Security (RLS) is disabled\n </Typography>\n <Typography variant=\"body2\" className=\"text-yellow-700 dark:text-yellow-600/90 mt-1 max-w-2xl\">\n Your table is completely readable and writable by anyone with access privileges. Enable RLS to create policies that restrict access to specific rows.\n </Typography>\n </div>\n </div>\n <Button\n size=\"medium\"\n variant=\"filled\"\n color=\"neutral\"\n onClick={() => setEditingPolicy(\"new\")}\n className=\"shrink-0 whitespace-nowrap\"\n disabled={!activeCollection}\n >\n {t(\"studio_rls_create_policy\")}\n </Button>\n </div>\n )}\n\n {activeTableData && mergedPolicies && mergedPolicies.length > 0 && (\n <div className=\"flex flex-col gap-3\">\n <Typography variant=\"subtitle2\" className=\"text-text-secondary dark:text-text-secondary-dark uppercase tracking-wider mb-1\">{t(\"studio_rls_policies\")}</Typography>\n {mergedPolicies.map(policy => (\n <Paper key={policy.policyname} className={cls(\"p-3 sm:px-4 sm:py-3 flex flex-col sm:flex-row sm:items-center justify-between gap-4 border rounded-lg\", defaultBorderMixin)}>\n <div className=\"flex flex-col gap-2 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <KeyIcon size={iconSize.smallest} className=\"text-text-secondary dark:text-text-secondary-dark shrink-0\"/>\n <Typography variant=\"body2\" className=\"truncate\">{policy.policyname}</Typography>\n {policy.status === \"code_only\" && (\n <Tooltip title=\"This policy is defined in your code but hasn't been applied to the database yet.\">\n <div className=\"px-1.5 py-0.5 rounded text-[10px] uppercase bg-primary/10 text-primary border border-primary/20 shrink-0\">\n Unapplied\n </div>\n </Tooltip>\n )}\n {/* \"DB Only\" is a *drift* signal — it means the\n codebase does not declare this policy. Where\n there is no codebase to compare against, every\n policy trivially qualifies, so the badge said\n nothing and said it about everything. */}\n {policy.status === \"live\" && hasCodebase && (\n <Tooltip title=\"This policy is live in the database but missing from your codebase schema.\">\n <div className=\"px-1.5 py-0.5 rounded text-[10px] uppercase bg-orange-500/10 text-orange-600 border border-orange-500/20 shrink-0\">\n DB Only\n </div>\n </Tooltip>\n )}\n </div>\n <div className=\"flex flex-wrap gap-1.5 text-sm\">\n {renderPolicyTag(\"Action\", policy.cmd)}\n {renderPolicyTag(\"Roles\", Array.isArray(policy.roles) ? policy.roles.join(\", \") : policy.roles)}\n </div>\n </div>\n <div className=\"flex gap-2 shrink-0 items-center\">\n {/* Writes a collection source file, so it needs a\n codebase on the other end. In the console the\n endpoint is not mounted and the button 404'd. */}\n {policy.status === \"live\" && activeCollection && hasCodebase && (\n <Button\n size=\"small\"\n variant=\"outlined\"\n color=\"primary\"\n onClick={async () => {\n const rule: Record<string, unknown> = {\n name: policy.policyname,\n operation: policy.cmd?.toLowerCase(),\n mode: policy.permissive?.toLowerCase(),\n using: policy.qual || undefined,\n withCheck: policy.with_check || undefined,\n roles: policy.roles\n };\n\n const existingRules = (isPostgresCollectionConfig(activeCollection) ? activeCollection.securityRules : undefined) || [];\n const newRules = [...existingRules, rule];\n\n try {\n await saveSecurityRules(\n (activeCollection as { id?: string, path?: string, alias?: string }).id || (activeCollection as { id?: string, path?: string, alias?: string }).path || (activeCollection as { id?: string, path?: string, alias?: string }).alias || activeTableData!.tableName,\n newRules\n );\n\n snackbarController.open({ type: \"success\",\nmessage: \"Policy imported successfully\" });\n fetchRLSData();\n } catch (e: unknown) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n }\n }}\n >\n Import to codebase\n </Button>\n )}\n <Button size=\"small\" variant=\"text\" color=\"primary\" onClick={() => setEditingPolicy(policy)}>\n {t(\"studio_rls_edit\")}\n </Button>\n {policy.status !== \"code_only\" && (\n <Tooltip title={t(\"studio_rls_delete\")} asChild={true}>\n <IconButton\n size=\"small\"\n onClick={async () => {\n const table = activeTableData!.tableName;\n const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData!.schemaName)}.${sanitizeSqlIdentifier(table)}`;\n if (!confirm(`Drop policy \"${policy.policyname}\" from table \"${table}\"?`)) return;\n try {\n await databaseAdmin!.executeSql!(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${qualifiedTable}`);\n snackbarController.open({ type: \"success\",\nmessage: `Policy \"${policy.policyname}\" dropped` });\n fetchRLSData();\n } catch (e: unknown) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n }\n }}\n >\n <Trash2Icon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n )}\n </div>\n </Paper>\n ))}\n </div>\n )}\n\n {activeTableData && mergedPolicies.length === 0 && activeTableData.rlsEnabled && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <ShieldIcon size={40} className=\"text-surface-300 dark:text-surface-600 mb-4\"/>\n <Typography variant=\"subtitle2\" className=\"text-text-secondary dark:text-text-secondary-dark mb-2\">\n No policies defined\n </Typography>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark max-w-sm mb-4\">\n RLS is enabled on this table but no policies exist. All access is denied by default (Postgres deny-all). Create a policy to allow specific access.\n </Typography>\n <Button\n size=\"small\"\n variant=\"filled\"\n color=\"primary\"\n onClick={() => setEditingPolicy(\"new\")}\n >\n {t(\"studio_rls_create_policy\")}\n </Button>\n </div>\n )}\n\n {activeTableData && mergedPolicies.length === 0 && !activeTableData.rlsEnabled && activeCollection && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <AlertTriangleIcon size={40} className=\"text-yellow-400 dark:text-yellow-600 mb-4\"/>\n <Typography variant=\"subtitle2\" className=\"text-text-secondary dark:text-text-secondary-dark mb-2\">\n No access control\n </Typography>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark max-w-sm\">\n This table has neither RLS nor policies. Enable RLS and create policies to restrict row-level access.\n </Typography>\n </div>\n )}\n\n </div>\n </div>\n </div>\n )}\n </div>\n }\n />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SchemaVisualizer-D6tMZgeC.js","names":[],"sources":["../src/components/SchemaVisualizer/schema-visualizer.utils.ts","../src/components/SchemaVisualizer/useSchemaGraph.ts","../src/components/SchemaVisualizer/useLiveRls.ts","../src/components/SchemaVisualizer/TableNode.tsx","../src/components/SchemaVisualizer/RelationEdge.tsx","../src/components/SchemaVisualizer/SchemaVisualizer.tsx"],"sourcesContent":["import dagre from \"dagre\";\nimport type { Node, Edge } from \"@xyflow/react\";\n\n// ─── Layout Constants ─────────────────────────────────────────────────\nexport const NODE_WIDTH = 280;\n/** Header with a single line of text (junction tables or tableName === collectionName). */\nconst HEADER_HEIGHT_SINGLE = 33;\n/** Header with two lines (name + subtitle when collectionName !== tableName). */\nconst HEADER_HEIGHT_DOUBLE = 47;\nconst ROW_HEIGHT = 28; // height per column row\n\n/**\n * Compute the correct header height for a table node.\n */\nexport const getHeaderHeight = (opts: {\n isJunction: boolean;\n collectionName: string;\n tableName: string;\n}): number => {\n if (opts.isJunction) return HEADER_HEIGHT_SINGLE;\n return opts.collectionName !== opts.tableName\n ? HEADER_HEIGHT_DOUBLE\n : HEADER_HEIGHT_SINGLE;\n};\n\n/**\n * Estimate the pixel height of a table node based on column count.\n */\nconst estimateNodeHeight = (columnCount: number, headerHeight: number = HEADER_HEIGHT_DOUBLE): number =>\n headerHeight + Math.max(columnCount, 1) * ROW_HEIGHT + 4; // +4 for bottom padding\n\n/**\n * Get the vertical center Y of a specific column row (0-indexed)\n * relative to the top of the node.\n */\nexport const getColumnRowY = (rowIndex: number, headerHeight: number = HEADER_HEIGHT_DOUBLE): number =>\n headerHeight + rowIndex * ROW_HEIGHT + ROW_HEIGHT / 2;\n\n// ─── Auto-Layout via Dagre ────────────────────────────────────────────\n\nexport type LayoutDirection = \"TB\" | \"LR\";\n\n/**\n * Compute node positions using the dagre graph layout engine.\n * Returns a new array of nodes with updated `position`.\n */\nexport const getLayoutedElements = (\n nodes: Node[],\n edges: Edge[],\n direction: LayoutDirection = \"LR\"\n): { nodes: Node[]; edges: Edge[] } => {\n const g = new dagre.graphlib.Graph();\n g.setGraph({\n rankdir: direction,\n nodesep: 100,\n ranksep: 180,\n edgesep: 60,\n marginx: 60,\n marginy: 60\n });\n g.setDefaultEdgeLabel(() => ({}));\n\n const nodeHeights = new Map<string, number>();\n\n nodes.forEach((node) => {\n const data = node.data as { columns?: unknown[]; isJunction?: boolean; collectionName?: string; tableName?: string };\n const columnCount = data.columns?.length ?? 3;\n const headerH = getHeaderHeight({\n isJunction: Boolean(data.isJunction),\n collectionName: data.collectionName ?? \"\",\n tableName: data.tableName ?? \"\"\n });\n const h = estimateNodeHeight(columnCount, headerH);\n nodeHeights.set(node.id, h);\n g.setNode(node.id, {\n width: NODE_WIDTH,\n height: h\n });\n });\n\n edges.forEach((edge) => {\n g.setEdge(edge.source, edge.target);\n });\n\n dagre.layout(g);\n\n const layoutedNodes = nodes.map((node) => {\n const nodeWithPosition = g.node(node.id);\n const h = nodeHeights.get(node.id) ?? estimateNodeHeight(3);\n return {\n ...node,\n data: {\n ...node.data,\n layoutDirection: direction\n },\n position: {\n x: nodeWithPosition.x - NODE_WIDTH / 2,\n y: nodeWithPosition.y - h / 2\n }\n };\n });\n\n return { nodes: layoutedNodes,\nedges };\n};\n\n// ─── Column type → display label ──────────────────────────────────────\n\nconst TYPE_LABELS: Record<string, string> = {\n string: \"varchar\",\n number: \"integer\",\n boolean: \"boolean\",\n date: \"timestamp\",\n map: \"jsonb\",\n array: \"jsonb\",\n relation: \"FK\"\n};\n\nexport const getTypeLabel = (type: string): string =>\n TYPE_LABELS[type] ?? type;\n\n// ─── Edge styling by relation type ────────────────────────────────────\n\nexport interface RelationEdgeData {\n cardinality: \"one\" | \"many\";\n direction: \"owning\" | \"inverse\";\n relationName: string;\n hasJunction: boolean;\n hasJoinPath: boolean;\n label: string;\n}\n\nexport const getCardinalityLabel = (\n cardinality: \"one\" | \"many\",\n direction: \"owning\" | \"inverse\"\n): string => {\n if (cardinality === \"many\") return \"M:N\";\n if (direction === \"inverse\") return \"1:1 ←\";\n return \"N:1\";\n};\n","import { useMemo, useState, useCallback, useEffect } from \"react\";\nimport type { Node, Edge } from \"@xyflow/react\";\nimport { MarkerType } from \"@xyflow/react\";\nimport { isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport type { Relation, ResolvedRelation } from \"@rebasepro/types\";\nimport { resolveCollectionRelations } from \"@rebasepro/common\";\nimport { getLayoutedElements, getCardinalityLabel, getTypeLabel, NODE_WIDTH } from \"./schema-visualizer.utils\";\nimport type { LayoutDirection, RelationEdgeData } from \"./schema-visualizer.utils\";\nimport type { AdminCollection, AdminPostgresCollection } from \"@rebasepro/cms-types\";\n\n// ─── Column info extracted from a collection ──────────────────────────\n\nexport interface ColumnInfo {\n name: string;\n type: string;\n typeLabel: string;\n isPrimaryKey: boolean;\n isForeignKey: boolean;\n isRequired: boolean;\n isEnum: boolean;\n enumValues?: string[];\n relationName?: string;\n}\n\n// ─── Node data shape ──────────────────────────────────────────────────\n\nexport interface TableNodeData {\n tableName: string;\n collectionName: string;\n slug: string;\n columns: ColumnInfo[];\n isJunction: boolean;\n isUnmanaged: boolean;\n rlsEnabled: boolean;\n historyEnabled: boolean;\n icon?: string;\n [key: string]: unknown;\n}\n\n// ─── Extract columns from a AdminCollection ─────────────────────────\n\nconst extractColumns = (collection: AdminCollection): ColumnInfo[] => {\n const columns: ColumnInfo[] = [];\n const properties = collection.properties ?? {};\n\n for (const [propName, prop] of Object.entries(properties)) {\n if (prop.type === \"relation\") continue; // Relations are shown as edges, not columns\n\n const isPk =\n (\"isId\" in prop && Boolean(prop.isId)) ||\n (!Object.values(properties).some(\n (p) => \"isId\" in p && Boolean(p.isId)\n ) &&\n propName === \"id\");\n\n const isEnum = prop.type === \"string\" && \"enum\" in prop && Boolean(prop.enum);\n\n let enumValues: string[] | undefined;\n if (isEnum && \"enum\" in prop && prop.enum) {\n const enumProp = prop.enum;\n if (Array.isArray(enumProp)) {\n enumValues = enumProp.map((v: unknown) =>\n typeof v === \"string\" ? v : String((v as Record<string, unknown>)?.id ?? v)\n );\n } else if (typeof enumProp === \"object\" && enumProp !== null) {\n enumValues = Object.keys(enumProp);\n }\n }\n\n columns.push({\n name: propName,\n type: prop.type,\n typeLabel: getTypeLabel(prop.type),\n isPrimaryKey: isPk,\n isForeignKey: false,\n isRequired: Boolean(prop.validation?.required),\n isEnum,\n enumValues\n });\n }\n\n // Add FK columns from owning relations\n if (isPostgresCollectionConfig(collection)) {\n try {\n const resolvedRelations = resolveCollectionRelations(collection);\n for (const rel of Object.values(resolvedRelations)) {\n if (rel.kind === \"belongsTo\" && rel.localKey) {\n // Only add if not already present as a regular column\n if (!columns.some((c) => c.name === rel.localKey)) {\n columns.push({\n name: rel.localKey,\n type: \"number\",\n typeLabel: \"FK\",\n isPrimaryKey: false,\n isForeignKey: true,\n isRequired: false,\n isEnum: false,\n relationName: rel.relationName\n });\n } else {\n // Mark existing column as FK\n const existing = columns.find((c) => c.name === rel.localKey);\n if (existing) {\n existing.isForeignKey = true;\n existing.relationName = rel.relationName;\n }\n }\n }\n }\n } catch {\n // Ignore resolution errors\n }\n }\n\n return columns;\n};\n\n// ─── Build graph from collections ─────────────────────────────────────\n\nconst buildGraph = (\n collections: AdminCollection[],\n direction: LayoutDirection,\n liveRls: Set<string> | null\n): { nodes: Node[]; edges: Edge[] } => {\n /**\n * Is RLS on for this table?\n *\n * The database is the authority whenever we could reach it. Declaring rules\n * in code is not the same as having them applied, and the hosted console\n * never sees the declarations at all — see `useLiveRlsTables`. Only when\n * there is no SQL capability do we fall back to what the config says.\n */\n const isRlsEnabled = (schema: string, tableName: string, declaresRules: boolean): boolean =>\n liveRls ? liveRls.has(`${schema}.${tableName}`) : declaresRules;\n\n const nodes: Node[] = [];\n const edges: Edge[] = [];\n const tableToNodeId = new Map<string, string>();\n // Track columns per node so we can resolve handle IDs for edges\n const nodeColumns = new Map<string, ColumnInfo[]>();\n\n // Helper: find the PK column name for a node (falls back to \"id\")\n const getPkHandle = (nodeId: string): string => {\n const cols = nodeColumns.get(nodeId);\n const pk = cols?.find((c) => c.isPrimaryKey);\n return pk ? `target-${pk.name}` : \"target-default\";\n };\n\n // Helper: find the FK column name for a node pointing to a relation\n const getFkHandle = (nodeId: string, localKey: string | undefined): string => {\n if (!localKey) return \"source-default\";\n const cols = nodeColumns.get(nodeId);\n const fk = cols?.find((c) => c.name === localKey);\n return fk ? `source-${fk.name}` : \"source-default\";\n };\n\n // 1. Create nodes for each collection\n for (const collection of collections) {\n if (!isPostgresCollectionConfig(collection)) continue;\n\n const tableName = collection.table ?? collection.slug;\n const nodeId = `table-${tableName}`;\n tableToNodeId.set(tableName, nodeId);\n\n const columns = extractColumns(collection);\n nodeColumns.set(nodeId, columns);\n\n const nodeData: TableNodeData = {\n tableName,\n collectionName: collection.name,\n slug: collection.slug,\n columns,\n isJunction: false,\n isUnmanaged: false,\n rlsEnabled: isRlsEnabled(\n collection.schema ?? \"public\",\n tableName,\n Boolean(collection.securityRules && collection.securityRules.length > 0)\n ),\n historyEnabled: Boolean(collection.history),\n icon: typeof collection.icon === \"string\" ? collection.icon : undefined\n };\n\n nodes.push({\n id: nodeId,\n type: \"tableNode\",\n position: { x: 0,\ny: 0 },\n data: nodeData\n });\n }\n\n // 2. Create junction table nodes and edges\n const processedJunctions = new Set<string>();\n\n for (const collection of collections) {\n if (!isPostgresCollectionConfig(collection)) continue;\n\n const tableName = collection.table ?? collection.slug;\n const sourceNodeId = tableToNodeId.get(tableName);\n if (!sourceNodeId) continue;\n\n let resolvedRelations: Record<string, ResolvedRelation>;\n try {\n resolvedRelations = resolveCollectionRelations(collection);\n } catch {\n continue;\n }\n\n for (const [relationKey, rel] of Object.entries(resolvedRelations)) {\n let targetCollection: AdminCollection;\n try {\n targetCollection = rel.target();\n } catch {\n continue;\n }\n\n if (!isPostgresCollectionConfig(targetCollection)) continue;\n\n const targetTable = targetCollection.table ?? targetCollection.slug;\n const targetNodeId = tableToNodeId.get(targetTable);\n if (!targetNodeId) continue;\n\n // Draw the side that owns the storage: a foreign key on this table,\n // or the junction. `hasOne`/`hasMany` are the mirror image of a\n // `belongsTo` that is drawn from the other end, and `via` has no\n // single edge to draw.\n if (rel.kind !== \"belongsTo\" && rel.kind !== \"manyToMany\") continue;\n\n const direction = \"owning\" as const;\n const edgeData: RelationEdgeData = {\n cardinality: rel.cardinality,\n direction,\n relationName: rel.relationName ?? relationKey,\n hasJunction: rel.kind === \"manyToMany\",\n hasJoinPath: false,\n label: getCardinalityLabel(rel.cardinality, direction)\n };\n\n if (rel.kind === \"manyToMany\" && !processedJunctions.has(rel.through.table)) {\n // Many-to-many: create junction node + two edges\n processedJunctions.add(rel.through.table);\n const junctionNodeId = `junction-${rel.through.table}`;\n\n const junctionColumns: ColumnInfo[] = [\n {\n name: rel.through.sourceColumn,\n type: \"number\",\n typeLabel: \"FK\",\n isPrimaryKey: true,\n isForeignKey: true,\n isRequired: true,\n isEnum: false\n },\n {\n name: rel.through.targetColumn,\n type: \"number\",\n typeLabel: \"FK\",\n isPrimaryKey: true,\n isForeignKey: true,\n isRequired: true,\n isEnum: false\n }\n ];\n nodeColumns.set(junctionNodeId, junctionColumns);\n\n nodes.push({\n id: junctionNodeId,\n type: \"tableNode\",\n position: { x: 0,\ny: 0 },\n data: {\n tableName: rel.through.table,\n collectionName: rel.through.table,\n slug: rel.through.table,\n columns: junctionColumns,\n isJunction: true,\n isUnmanaged: false,\n // Junction tables get derived policies rather than\n // declared ones, so the config can never say — but the\n // database can, and usually says yes.\n rlsEnabled: isRlsEnabled(\"public\", rel.through.table, false),\n historyEnabled: false\n } satisfies TableNodeData\n });\n\n // Source table → junction (junction references source PK)\n const sourcePk = nodeColumns.get(sourceNodeId)?.find((c) => c.isPrimaryKey);\n edges.push({\n id: `edge-${sourceNodeId}-${junctionNodeId}`,\n source: sourceNodeId,\n target: junctionNodeId,\n sourceHandle: sourcePk ? `source-${sourcePk.name}` : \"source-default\",\n targetHandle: `target-${rel.through.sourceColumn}`,\n type: \"relationEdge\",\n data: { ...edgeData,\nlabel: \"1:N\" } as Record<string, unknown>,\n markerEnd: { type: MarkerType.ArrowClosed,\nwidth: 16,\nheight: 16 }\n });\n\n // Junction → target table\n edges.push({\n id: `edge-${junctionNodeId}-${targetNodeId}`,\n source: junctionNodeId,\n target: targetNodeId,\n sourceHandle: `source-${rel.through.targetColumn}`,\n targetHandle: getPkHandle(targetNodeId),\n type: \"relationEdge\",\n data: { ...edgeData,\nlabel: \"N:1\" } as Record<string, unknown>,\n markerEnd: { type: MarkerType.ArrowClosed,\nwidth: 16,\nheight: 16 }\n });\n } else if (rel.kind === \"belongsTo\") {\n // A foreign key on this table.\n edges.push({\n id: `edge-${sourceNodeId}-${targetNodeId}-${relationKey}`,\n source: sourceNodeId,\n target: targetNodeId,\n sourceHandle: getFkHandle(sourceNodeId, rel.localKey),\n targetHandle: getPkHandle(targetNodeId),\n type: \"relationEdge\",\n data: { ...edgeData } as Record<string, unknown>,\n markerEnd: { type: MarkerType.ArrowClosed,\nwidth: 16,\nheight: 16 }\n });\n }\n }\n }\n // 3. Apply dagre layout first to get node positions\n const layoutResult = getLayoutedElements(nodes, edges, direction);\n\n // 4. Build a position lookup from the laid-out nodes\n const nodePositions = new Map<string, { x: number }>();\n for (const node of layoutResult.nodes) {\n nodePositions.set(node.id, { x: node.position.x });\n }\n\n // 5. Resolve handle sides based on relative node positions.\n // If source is left of target → source exits Right, target enters Left.\n // If source is right of target → source exits Left, target enters Right.\n for (const edge of layoutResult.edges) {\n const srcPos = nodePositions.get(edge.source);\n const tgtPos = nodePositions.get(edge.target);\n\n const srcBaseHandle = (edge.sourceHandle ?? \"source-default\") as string;\n const tgtBaseHandle = (edge.targetHandle ?? \"target-default\") as string;\n\n if (srcPos && tgtPos) {\n const srcCenterX = srcPos.x + NODE_WIDTH / 2;\n const tgtCenterX = tgtPos.x + NODE_WIDTH / 2;\n const sourceIsLeft = srcCenterX <= tgtCenterX;\n\n edge.sourceHandle = `${srcBaseHandle}-${sourceIsLeft ? \"right\" : \"left\"}`;\n edge.targetHandle = `${tgtBaseHandle}-${sourceIsLeft ? \"left\" : \"right\"}`;\n } else {\n edge.sourceHandle = `${srcBaseHandle}-right`;\n edge.targetHandle = `${tgtBaseHandle}-left`;\n }\n }\n\n return layoutResult;\n};\n\n// ─── Hook ─────────────────────────────────────────────────────────────\n\nexport interface UseSchemaGraphResult {\n nodes: Node[];\n edges: Edge[];\n direction: LayoutDirection;\n setDirection: (dir: LayoutDirection) => void;\n relayout: () => void;\n isLoading: boolean;\n tableCount: number;\n relationCount: number;\n}\n\nexport const useSchemaGraph = (\n collections: AdminCollection[] | undefined,\n /**\n * `schema.table` for every table the database reports RLS on, or `null`\n * when there is no SQL capability to ask. See {@link useLiveRlsTables}.\n */\n liveRls: Set<string> | null = null\n): UseSchemaGraphResult => {\n const [direction, setDirection] = useState<LayoutDirection>(\"LR\");\n const [version, setVersion] = useState(0);\n\n const relayout = useCallback(() => setVersion((v) => v + 1), []);\n\n // Re-layout on direction change\n useEffect(() => {\n setVersion((v) => v + 1);\n }, [direction]);\n\n const { nodes, edges, tableCount, relationCount } = useMemo(() => {\n if (!collections || collections.length === 0) {\n return { nodes: [],\nedges: [],\ntableCount: 0,\nrelationCount: 0 };\n }\n const result = buildGraph(collections, direction, liveRls);\n return {\n ...result,\n tableCount: result.nodes.length,\n relationCount: result.edges.length\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [collections, direction, version, liveRls]);\n\n return {\n nodes,\n edges,\n direction,\n setDirection,\n relayout,\n isLoading: !collections,\n tableCount,\n relationCount\n };\n};\n","/**\n * Which tables actually have row-level security switched on, according to the\n * database.\n *\n * ## Why this is not read from the collection config\n *\n * The graph used to derive \"RLS protected\" from `collection.securityRules` —\n * i.e. from what the *codebase* declares. Two problems with that:\n *\n * - **It is not the truth.** Rules in code are only live once a migration has\n * applied them, and `ALTER TABLE … DISABLE ROW LEVEL SECURITY` in a psql\n * session is invisible to it. A security indicator that reports intent\n * rather than state is worse than none: it is green precisely when you most\n * need it to be red.\n * - **The hosted console has no codebase.** There, collections arrive from the\n * tenant's `/api/meta/contract`, which deliberately strips `securityRules`\n * (they carry the raw SQL of every policy guarding the project). So the\n * console showed \"RLS protected: 0\" and no badge on any table, for every\n * project, however well secured.\n *\n * `pg_tables.rowsecurity` answers the question directly and is available\n * wherever `databaseAdmin` is — which is both hosts.\n *\n * When there is no SQL capability at all the hook reports `null`, and callers\n * fall back to the config-derived flag rather than claiming everything is\n * unprotected.\n */\nimport { useEffect, useState } from \"react\";\nimport { useRebaseContext } from \"@rebasepro/app\";\n\n/** `schema.table` for every table with RLS enabled, or `null` if unknown. */\nexport type LiveRlsTables = Set<string> | null;\n\nconst RLS_TABLES_SQL = `\n SELECT schemaname, tablename, rowsecurity\n FROM pg_tables\n WHERE schemaname NOT IN ('information_schema', 'pg_catalog');\n`;\n\n/** Postgres column names arrive lower-cased over the wire, but not on every driver. */\nfunction pick<T>(row: Record<string, unknown>, name: string): T | undefined {\n return (row[name] ?? row[name.toUpperCase()]) as T | undefined;\n}\n\nfunction extractRows(result: unknown): Record<string, unknown>[] {\n if (result && typeof result === \"object\" && \"rows\" in result) {\n const rows = (result as { rows: unknown }).rows;\n if (Array.isArray(rows)) return rows as Record<string, unknown>[];\n }\n if (Array.isArray(result)) return result as Record<string, unknown>[];\n return [];\n}\n\nexport function useLiveRlsTables(): LiveRlsTables {\n const { databaseAdmin } = useRebaseContext();\n const executeSql = databaseAdmin?.executeSql;\n const [tables, setTables] = useState<LiveRlsTables>(null);\n\n useEffect(() => {\n if (!executeSql) {\n setTables(null);\n return;\n }\n let cancelled = false;\n executeSql(RLS_TABLES_SQL)\n .then((result: unknown) => {\n if (cancelled) return;\n const enabled = new Set<string>();\n for (const row of extractRows(result)) {\n if (!pick<boolean>(row, \"rowsecurity\")) continue;\n const schema = pick<string>(row, \"schemaname\") ?? \"public\";\n const table = pick<string>(row, \"tablename\");\n if (table) enabled.add(`${schema}.${table}`);\n }\n setTables(enabled);\n })\n .catch(() => {\n // No answer is not the same as \"nothing is protected\" — leave it\n // unknown so the caller keeps its config-derived fallback.\n if (!cancelled) setTables(null);\n });\n return () => { cancelled = true; };\n }, [executeSql]);\n\n return tables;\n}\n","import React, { memo, useMemo } from \"react\";\nimport { Handle, Position } from \"@xyflow/react\";\nimport type { NodeProps } from \"@xyflow/react\";\nimport { Typography, Chip, Tooltip, cls } from \"@rebasepro/ui\";\nimport { IconForView } from \"@rebasepro/app\";\nimport type { TableNodeData, ColumnInfo } from \"./useSchemaGraph\";\nimport { getColumnRowY, getHeaderHeight } from \"./schema-visualizer.utils\";\n\n/**\n * Custom React Flow node that renders a database table as a card.\n *\n * Every PK and FK column renders handles on BOTH Left and Right sides.\n * The edge builder picks which side to use based on relative node position.\n */\nconst TableNodeInner = ({ data, selected }: NodeProps) => {\n const {\n tableName,\n collectionName,\n columns,\n isJunction,\n rlsEnabled,\n historyEnabled,\n icon\n } = data as TableNodeData;\n\n // Build handles: every PK/FK column gets Left + Right handles\n const handles = useMemo(() => {\n const result: { id: string; type: \"source\" | \"target\"; position: Position; top: number }[] = [];\n const cols = columns as ColumnInfo[];\n const headerH = getHeaderHeight({\n isJunction: Boolean(isJunction),\n collectionName: collectionName as string,\n tableName: tableName as string\n });\n const midY = cols.length > 0 ? getColumnRowY(Math.floor(cols.length / 2), headerH) : 30;\n\n cols.forEach((col, idx) => {\n const y = getColumnRowY(idx, headerH);\n\n if (col.isForeignKey && !col.isPrimaryKey) {\n // FK: source handles on both sides\n result.push({ id: `source-${col.name}-right`,\ntype: \"source\",\nposition: Position.Right,\ntop: y });\n result.push({ id: `source-${col.name}-left`,\ntype: \"source\",\nposition: Position.Left,\ntop: y });\n }\n if (col.isPrimaryKey) {\n // PK: target + source handles on both sides\n result.push({ id: `target-${col.name}-right`,\ntype: \"target\",\nposition: Position.Right,\ntop: y });\n result.push({ id: `target-${col.name}-left`,\ntype: \"target\",\nposition: Position.Left,\ntop: y });\n result.push({ id: `source-${col.name}-right`,\ntype: \"source\",\nposition: Position.Right,\ntop: y });\n result.push({ id: `source-${col.name}-left`,\ntype: \"source\",\nposition: Position.Left,\ntop: y });\n }\n });\n\n // Default handles on both sides\n result.push({ id: \"target-default-right\",\ntype: \"target\",\nposition: Position.Right,\ntop: midY });\n result.push({ id: \"target-default-left\",\ntype: \"target\",\nposition: Position.Left,\ntop: midY });\n result.push({ id: \"source-default-right\",\ntype: \"source\",\nposition: Position.Right,\ntop: midY });\n result.push({ id: \"source-default-left\",\ntype: \"source\",\nposition: Position.Left,\ntop: midY });\n\n return result;\n }, [columns, isJunction, collectionName, tableName]);\n\n return (\n <div\n className={cls(\n \"relative rounded-lg border bg-white dark:bg-surface-900 shadow-sm transition-all duration-200 min-w-[240px] max-w-[320px]\",\n selected\n ? \"border-primary ring-2 ring-primary/20 shadow-md\"\n : \"border-surface-200/40 dark:border-surface-700/40 hover:shadow-md hover:border-surface-300 dark:hover:border-surface-600\",\n isJunction && \"border-dashed\"\n )}\n >\n {/* ── Header ── */}\n <div\n className={cls(\n \"flex items-center gap-2 px-3 py-2 border-b rounded-t-lg\",\n isJunction\n ? \"bg-surface-50 dark:bg-surface-950/50 border-surface-200/30 dark:border-surface-700/30\"\n : \"bg-surface-50 dark:bg-surface-950 border-surface-200/40 dark:border-surface-700/40\"\n )}\n >\n {icon && !isJunction && (\n <div className=\"text-primary shrink-0\">\n <IconForView\n collectionOrView={{ slug: tableName,\nname: collectionName,\nicon }}\n size=\"smallest\"\n />\n </div>\n )}\n {isJunction && (\n <svg\n className=\"w-3.5 h-3.5 text-text-disabled dark:text-text-disabled-dark shrink-0\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4\"\n />\n </svg>\n )}\n <div className=\"flex flex-col min-w-0\">\n <Typography\n variant=\"caption\"\n className={cls(\n \"font-semibold truncate text-[12px]\",\n isJunction\n ? \"text-text-secondary dark:text-text-secondary-dark\"\n : \"text-text-primary dark:text-text-primary-dark\"\n )}\n >\n {tableName}\n </Typography>\n {collectionName !== tableName && !isJunction && (\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark truncate\"\n >\n {collectionName}\n </Typography>\n )}\n </div>\n\n {/* Badges */}\n <div className=\"ml-auto flex items-center gap-1 shrink-0\">\n {rlsEnabled && (\n <Tooltip title=\"RLS enabled\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-green-500\"/>\n </Tooltip>\n )}\n {historyEnabled && (\n <Tooltip title=\"History enabled\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-blue-400\"/>\n </Tooltip>\n )}\n </div>\n </div>\n\n {/* ── Columns ── */}\n <div className=\"divide-y divide-surface-100 dark:divide-surface-950/60\">\n {(columns as ColumnInfo[]).map((col: ColumnInfo) => (\n <div\n key={col.name}\n className={cls(\n \"flex items-center gap-2 px-3 py-1.5 text-xs transition-colors\",\n col.isPrimaryKey && \"bg-amber-50/50 dark:bg-amber-950/10\",\n col.isForeignKey &&\n !col.isPrimaryKey &&\n \"bg-blue-50/40 dark:bg-blue-950/10\"\n )}\n >\n <span className=\"w-3 shrink-0 text-center\">\n {col.isPrimaryKey && (\n <Tooltip title=\"Primary Key\">\n <span className=\"text-amber-500 text-[10px] font-semibold\">🔑</span>\n </Tooltip>\n )}\n {col.isForeignKey && !col.isPrimaryKey && (\n <Tooltip title={`FK → ${col.relationName ?? \"?\"}`}>\n <span className=\"text-blue-400 text-[10px] font-semibold\">🔗</span>\n </Tooltip>\n )}\n </span>\n\n <Typography\n variant=\"caption\"\n className={cls(\n \"font-mono text-[11px] truncate flex-1 min-w-0\",\n col.isPrimaryKey\n ? \"font-semibold text-amber-700 dark:text-amber-400\"\n : col.isForeignKey\n ? \"text-blue-600 dark:text-blue-400\"\n : \"text-text-primary dark:text-text-primary-dark\"\n )}\n >\n {col.name}\n </Typography>\n\n {col.isEnum ? (\n <Chip\n size=\"smallest\"\n className=\"bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400 border-violet-200 dark:border-violet-800 text-[9px] py-0\"\n >\n enum\n </Chip>\n ) : (\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark font-mono shrink-0\"\n >\n {col.typeLabel}\n </Typography>\n )}\n\n {col.isRequired && !col.isPrimaryKey && (\n <Tooltip title=\"Required\">\n <span className=\"text-red-400 text-[9px]\">•</span>\n </Tooltip>\n )}\n </div>\n ))}\n </div>\n\n {/* ── Handles at node root ── */}\n {handles.map((h) => (\n <Handle\n key={h.id}\n id={h.id}\n type={h.type}\n position={h.position}\n style={{ top: h.top }}\n className={cls(\n \"!w-2 !h-2 !border-2 !border-white dark:!border-surface-900\",\n h.type === \"source\" ? \"!bg-blue-400\" : \"!bg-amber-400\"\n )}\n />\n ))}\n </div>\n );\n};\n\nexport const TableNode = memo(TableNodeInner);\n","import React, { memo } from \"react\";\nimport { BaseEdge, getSmoothStepPath, EdgeLabelRenderer } from \"@xyflow/react\";\nimport type { EdgeProps } from \"@xyflow/react\";\nimport { cls } from \"@rebasepro/ui\";\nimport type { RelationEdgeData } from \"./schema-visualizer.utils\";\n\n/**\n * Custom React Flow edge that renders relations with style variations\n * based on cardinality and direction.\n *\n * - Owning one-to-one: solid, primary color\n * - Many-to-many (junction): solid, violet\n * - Inverse: dashed, muted\n * - Join path: dotted, muted\n */\nconst RelationEdgeInner = ({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n data,\n selected\n}: EdgeProps) => {\n const edgeData = data as RelationEdgeData | undefined;\n\n const [edgePath, labelX, labelY] = getSmoothStepPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n borderRadius: 8\n });\n\n const isInverse = edgeData?.direction === \"inverse\";\n const isJoinPath = edgeData?.hasJoinPath;\n const isJunction = edgeData?.hasJunction;\n\n // Determine stroke style\n let strokeColor = \"var(--rf-edge-stroke, #94a3b8)\";\n let strokeDasharray: string | undefined;\n let strokeWidth = 1.5;\n\n if (selected) {\n strokeColor = \"var(--rf-edge-stroke-selected, #6366f1)\";\n strokeWidth = 2.5;\n } else if (isJunction) {\n strokeColor = \"#8b5cf6\"; // violet\n } else if (isInverse) {\n strokeDasharray = \"6 3\";\n strokeColor = \"#94a3b8\";\n } else if (isJoinPath) {\n strokeDasharray = \"3 3\";\n strokeColor = \"#94a3b8\";\n } else {\n strokeColor = \"#6366f1\"; // primary/indigo\n }\n\n return (\n <>\n <BaseEdge\n id={id}\n path={edgePath}\n style={{\n stroke: strokeColor,\n strokeWidth,\n strokeDasharray\n }}\n />\n {edgeData?.label && (\n <EdgeLabelRenderer>\n <div\n style={{\n position: \"absolute\",\n transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n pointerEvents: \"all\"\n }}\n className={cls(\n \"px-1.5 py-0.5 rounded text-[9px] font-mono font-semibold leading-none\",\n \"bg-white dark:bg-surface-900 border\",\n selected\n ? \"border-primary text-primary\"\n : isJunction\n ? \"border-violet-200 dark:border-violet-800 text-violet-600 dark:text-violet-400\"\n : isInverse || isJoinPath\n ? \"border-surface-200 dark:border-surface-700 text-text-disabled dark:text-text-disabled-dark\"\n : \"border-primary/30 dark:border-primary/30 text-primary\"\n )}\n >\n {edgeData.label}\n </div>\n </EdgeLabelRenderer>\n )}\n </>\n );\n};\n\nexport const RelationEdge = memo(RelationEdgeInner);\n","import React, { useState, useCallback, useMemo, useEffect, useRef } from \"react\";\nimport {\n ReactFlow,\n Controls,\n MiniMap,\n Background,\n BackgroundVariant,\n useReactFlow,\n useNodesInitialized,\n ReactFlowProvider,\n applyNodeChanges,\n applyEdgeChanges\n} from \"@xyflow/react\";\nimport type { Node, Edge, NodeChange, EdgeChange } from \"@xyflow/react\";\nimport \"@xyflow/react/dist/style.css\";\nimport {\n Button,\n SearchBar,\n TextField,\n Tooltip,\n Alert,\n Typography,\n cls,\n defaultBorderMixin,\n CircularProgress,\n ResizablePanels,\n IconButton\n} from \"@rebasepro/ui\";\nimport {\n useStudioCollectionRegistry,\n IconForView\n} from \"@rebasepro/app\";\n\nimport { isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport { useSchemaGraph } from \"./useSchemaGraph\";\nimport { useLiveRlsTables } from \"./useLiveRls\";\nimport type { TableNodeData } from \"./useSchemaGraph\";\nimport { TableNode } from \"./TableNode\";\nimport { RelationEdge } from \"./RelationEdge\";\nimport type { AdminCollection } from \"@rebasepro/cms-types\";\n\n// ─── Custom node / edge type registrations ────────────────────────────\n\nconst nodeTypes = {\n tableNode: TableNode\n};\n\nconst edgeTypes = {\n relationEdge: RelationEdge\n};\n\n// ─── Inner component (needs ReactFlowProvider) ─────────────────────────\n\nfunction SchemaVisualizerCanvas({\n collections\n}: {\n collections: AdminCollection[];\n}) {\n const reactFlowInstance = useReactFlow();\n const liveRls = useLiveRlsTables();\n const {\n nodes: layoutedNodes,\n edges: layoutedEdges,\n direction,\n setDirection,\n relayout,\n tableCount,\n relationCount\n } = useSchemaGraph(collections, liveRls);\n\n const [nodes, setNodes] = useState<Node[]>([]);\n const [edges, setEdges] = useState<Edge[]>([]);\n const [selectedTable, setSelectedTable] = useState<string | null>(null);\n const [searchQuery, setSearchQuery] = useState(\"\");\n const initialFitDone = useRef(false);\n\n // Sync layouted data into state\n useEffect(() => {\n setNodes(layoutedNodes);\n setEdges(layoutedEdges);\n }, [layoutedNodes, layoutedEdges]);\n\n const onNodesChange = useCallback(\n (changes: NodeChange[]) => setNodes((nds) => applyNodeChanges(changes, nds)),\n []\n );\n const onEdgesChange = useCallback(\n (changes: EdgeChange[]) => setEdges((eds) => applyEdgeChanges(changes, eds)),\n []\n );\n\n /**\n * Fit the graph into view, once, as soon as it can be measured.\n *\n * This used to fire on a 200ms timer. `fitView` needs every node's real\n * width and height, which React Flow only knows after it has laid them out\n * and measured them — with a dozen tables, a cold Monaco-sized bundle and\n * a slow first paint, 200ms is often too early. The fit then ran against\n * zero-sized nodes and produced a viewport nothing lines up with, and\n * `initialFitDone` made sure it was never retried: the ERD opened as a\n * scatter of specks in a corner and stayed there until you hit \"fit\" by\n * hand. `useNodesInitialized` is the event this was approximating.\n */\n const nodesInitialized = useNodesInitialized();\n useEffect(() => {\n if (!nodesInitialized || nodes.length === 0 || initialFitDone.current) return;\n initialFitDone.current = true;\n reactFlowInstance.fitView({ padding: 0.15,\nduration: 400 });\n }, [nodesInitialized, nodes.length, reactFlowInstance]);\n\n /**\n * Re-fit after a relayout.\n *\n * Switching LR↔TB transposes the graph — a tall column becomes a wide band\n * — so the viewport that framed the old shape frames the new one badly:\n * asking for a different layout used to leave most of it off-screen until\n * you pressed \"fit\" as a second step. Skipped for the very first layout,\n * which the initial fit above already handles.\n */\n const lastFittedDirection = useRef(direction);\n useEffect(() => {\n if (!nodesInitialized || nodes.length === 0) return;\n if (lastFittedDirection.current === direction) return;\n lastFittedDirection.current = direction;\n reactFlowInstance.fitView({ padding: 0.15,\nduration: 400 });\n }, [direction, nodesInitialized, nodes, reactFlowInstance]);\n\n const handleFitView = useCallback(() => {\n reactFlowInstance.fitView({ padding: 0.15,\nduration: 400 });\n }, [reactFlowInstance]);\n\n const handleNodeClick = useCallback((_: React.MouseEvent, node: Node) => {\n setSelectedTable(node.id);\n }, []);\n\n const handlePaneClick = useCallback(() => {\n setSelectedTable(null);\n }, []);\n\n // Focus on a table from the sidebar\n const handleTableSelect = useCallback(\n (nodeId: string) => {\n setSelectedTable(nodeId);\n const node = nodes.find((n) => n.id === nodeId);\n if (node) {\n reactFlowInstance.setCenter(\n node.position.x + 140,\n node.position.y + 60,\n { zoom: 1.2,\nduration: 400 }\n );\n }\n },\n [nodes, reactFlowInstance]\n );\n\n // Sidebar panel size\n const [sidebarSize, setSidebarSize] = useState(() => {\n try {\n const saved = localStorage.getItem(\"rebase_schema_viz_sidebar_size\");\n return saved !== null ? parseFloat(saved) : 20;\n } catch {\n return 20;\n }\n });\n\n useEffect(() => {\n try {\n localStorage.setItem(\n \"rebase_schema_viz_sidebar_size\",\n sidebarSize.toString()\n );\n } catch {\n // Ignore\n }\n }, [sidebarSize]);\n\n // Group collections for sidebar\n const postgresCollections = useMemo(\n () => collections.filter(isPostgresCollectionConfig),\n [collections]\n );\n\n const filteredCollections = useMemo(() => {\n if (!searchQuery.trim()) return postgresCollections;\n const q = searchQuery.toLowerCase();\n return postgresCollections.filter(\n (c) =>\n c.name.toLowerCase().includes(q) ||\n c.table?.toLowerCase().includes(q) ||\n c.slug?.toLowerCase().includes(q)\n );\n }, [postgresCollections, searchQuery]);\n\n // Junction table nodes\n const junctionNodes = useMemo(\n () =>\n nodes.filter(\n (n) => (n.data as TableNodeData).isJunction\n ),\n [nodes]\n );\n\n /**\n * The RLS answer for one collection, from the same place the graph used.\n *\n * `nodes` already carries it, so look it up there rather than re-deriving —\n * that is what kept the sidebar dots, the node badges and the total from\n * agreeing.\n */\n const rlsEnabledFor = useCallback(\n (collection: AdminCollection): boolean => {\n const table = (isPostgresCollectionConfig(collection) ? collection.table : undefined) ?? collection.slug;\n const node = nodes.find((n) => n.id === `table-${table}`);\n return Boolean((node?.data as TableNodeData | undefined)?.rlsEnabled);\n },\n [nodes]\n );\n\n // Stats\n const stats = useMemo(\n () => ({\n tables: tableCount,\n relations: relationCount,\n junctions: junctionNodes.length,\n // Counted off the nodes, which already carry the database's answer\n // where there was one — the same number the per-table badges show.\n // Counting `securityRules` here instead made the total disagree\n // with the badges, and read 0 in the hosted console, where the\n // contract endpoint strips those rules.\n withRls: nodes.filter((n) => (n.data as TableNodeData | undefined)?.rlsEnabled).length\n }),\n [\n tableCount,\n relationCount,\n junctionNodes.length,\n nodes\n ]\n );\n\n return (\n <ResizablePanels\n orientation=\"horizontal\"\n panelSizePercent={sidebarSize}\n onPanelSizeChange={setSidebarSize}\n minPanelSizePx={220}\n firstPanel={\n <div\n className={cls(\n \"flex flex-col h-full w-full bg-white dark:bg-surface-950 border-r\",\n defaultBorderMixin\n )}\n >\n {/* Sidebar header */}\n <div\n className={cls(\n \"flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\",\n defaultBorderMixin\n )}\n >\n <Typography\n variant=\"caption\"\n className=\"font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark\"\n >\n Tables\n </Typography>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark font-mono\"\n >\n {stats.tables}\n </Typography>\n </div>\n\n {/* Search */}\n <div className=\"px-2 py-1.5 border-b border-surface-200/40 dark:border-surface-700/40\">\n <SearchBar\n size=\"smallest\"\n placeholder=\"Filter tables…\"\n onTextSearch={(val) => setSearchQuery(val ?? \"\")}\n innerClassName=\"text-xs\"\n />\n </div>\n\n {/* Table list */}\n <div className=\"flex-grow overflow-y-auto no-scrollbar p-1\">\n {/* Regular collections */}\n <div className=\"space-y-0.5\">\n {filteredCollections.map((collection) => {\n const table = collection.table ?? collection.slug;\n const nodeId = `table-${table}`;\n const isSelected = selectedTable === nodeId;\n return (\n <div\n key={nodeId}\n onClick={() =>\n handleTableSelect(nodeId)\n }\n className={cls(\n \"flex items-center p-1.5 cursor-pointer rounded transition-colors group\",\n isSelected\n ? \"bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-light\"\n : \"hover:bg-surface-100 dark:hover:bg-surface-950 text-text-secondary dark:text-text-secondary-dark\"\n )}\n >\n <div className=\"shrink-0 mr-1.5 text-text-disabled dark:text-text-disabled-dark\">\n <IconForView\n collectionOrView={{\n slug: collection.slug,\n name: collection.name,\n icon:\n typeof collection.icon ===\n \"string\"\n ? collection.icon\n : undefined\n }}\n size=\"smallest\"\n />\n </div>\n <div className=\"flex flex-col min-w-0 flex-1\">\n <Typography\n variant=\"caption\"\n className=\"text-xs truncate font-medium\"\n >\n {collection.name}\n </Typography>\n {table !== collection.name && (\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark font-mono truncate\"\n >\n {table}\n </Typography>\n )}\n </div>\n <div className=\"flex items-center gap-1 shrink-0 ml-1\">\n {/* Same source as the node badge and the\n \"RLS protected\" total: the database when\n it could be asked, the config otherwise.\n Reading `securityRules` directly here made\n the three disagree. */}\n {rlsEnabledFor(collection) && (\n <Tooltip title=\"RLS enabled\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-green-500\"/>\n </Tooltip>\n )}\n {collection.history && (\n <Tooltip title=\"History enabled\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-blue-400\"/>\n </Tooltip>\n )}\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Junction tables */}\n {junctionNodes.length > 0 && (\n <div className=\"mt-3\">\n <Typography\n variant=\"caption\"\n className=\"px-1.5 text-[10px] uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark font-medium\"\n >\n Junction Tables\n </Typography>\n <div className=\"mt-1 space-y-0.5\">\n {junctionNodes.map((node) => {\n const d =\n node.data as TableNodeData;\n const isSelected =\n selectedTable === node.id;\n return (\n <div\n key={node.id}\n onClick={() =>\n handleTableSelect(\n node.id\n )\n }\n className={cls(\n \"flex items-center p-1.5 cursor-pointer rounded transition-colors\",\n isSelected\n ? \"bg-primary/10 text-primary dark:bg-primary/20\"\n : \"hover:bg-surface-100 dark:hover:bg-surface-950 text-text-disabled dark:text-text-disabled-dark\"\n )}\n >\n <svg\n className=\"w-3.5 h-3.5 mr-1.5 shrink-0\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4\"\n />\n </svg>\n <Typography\n variant=\"caption\"\n className=\"text-xs font-mono truncate\"\n >\n {d.tableName}\n </Typography>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n\n {/* Stats footer */}\n <div\n className={cls(\n \"px-3 py-2 border-t bg-surface-50 dark:bg-surface-900 space-y-1\",\n defaultBorderMixin\n )}\n >\n <div className=\"flex items-center justify-between\">\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n Tables\n </Typography>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] font-mono font-medium\"\n >\n {stats.tables}\n </Typography>\n </div>\n <div className=\"flex items-center justify-between\">\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n Relations\n </Typography>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] font-mono font-medium\"\n >\n {stats.relations}\n </Typography>\n </div>\n <div className=\"flex items-center justify-between\">\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n RLS protected\n </Typography>\n <div className=\"flex items-center gap-1\">\n <div className=\"w-1.5 h-1.5 rounded-full bg-green-500\"/>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] font-mono font-medium\"\n >\n {stats.withRls}\n </Typography>\n </div>\n </div>\n </div>\n </div>\n }\n secondPanel={\n <div className=\"flex-grow flex flex-col min-w-0 h-full w-full bg-white dark:bg-surface-950\">\n {/* Toolbar */}\n <div\n className={cls(\n \"flex items-center justify-between pr-2 border-b bg-white dark:bg-surface-950 min-h-[46px]\",\n defaultBorderMixin\n )}\n >\n <div className=\"flex items-center gap-2 px-4\">\n <Typography\n variant=\"subtitle2\"\n className=\"font-mono text-text-secondary dark:text-text-secondary-dark\"\n >\n Schema Visualizer\n </Typography>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {/* Direction toggle */}\n <div className=\"flex items-center bg-surface-100 dark:bg-surface-950 rounded-md border border-surface-200/40 dark:border-surface-700/40\">\n <Tooltip title=\"Left to right layout\">\n <Button\n size=\"small\"\n variant={direction === \"LR\" ? \"filled\" : \"text\"}\n color={direction === \"LR\" ? \"primary\" : \"neutral\"}\n onClick={() => setDirection(\"LR\")}\n className=\"!rounded-r-none !px-2 !py-1 !text-[10px] !font-mono\"\n >\n LR\n </Button>\n </Tooltip>\n <Tooltip title=\"Top to bottom layout\">\n <Button\n size=\"small\"\n variant={direction === \"TB\" ? \"filled\" : \"text\"}\n color={direction === \"TB\" ? \"primary\" : \"neutral\"}\n onClick={() => setDirection(\"TB\")}\n className=\"!rounded-l-none !px-2 !py-1 !text-[10px] !font-mono\"\n >\n TB\n </Button>\n </Tooltip>\n </div>\n\n <div className=\"h-4 w-px bg-surface-200 dark:bg-surface-950 mx-0.5\"/>\n\n {/* Fit view */}\n <Tooltip title=\"Fit to view\">\n <IconButton\n size=\"small\"\n onClick={handleFitView}\n >\n <svg\n className=\"w-4 h-4\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4\"\n />\n </svg>\n </IconButton>\n </Tooltip>\n\n {/* Re-layout */}\n <Tooltip title=\"Re-layout\">\n <IconButton\n size=\"small\"\n onClick={relayout}\n >\n <svg\n className=\"w-4 h-4\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n </IconButton>\n </Tooltip>\n </div>\n </div>\n\n {/* Canvas */}\n <div className=\"flex-grow relative\">\n <ReactFlow\n nodes={nodes}\n edges={edges}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n onNodeClick={handleNodeClick}\n onPaneClick={handlePaneClick}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n fitView\n fitViewOptions={{ padding: 0.15 }}\n minZoom={0.1}\n maxZoom={2}\n proOptions={{ hideAttribution: true }}\n className=\"bg-surface-50 dark:bg-surface-950\"\n >\n <Background\n variant={BackgroundVariant.Dots}\n gap={20}\n size={1}\n className=\"!bg-surface-50 dark:!bg-surface-950\"\n color=\"var(--rf-bg-dot, #d4d4d8)\"\n />\n <Controls\n showInteractive={false}\n // mb clears the legend overlay pinned at bottom-left\n className=\"!mb-16 !bg-white dark:!bg-surface-900 !border !border-surface-200/40 dark:!border-surface-700/40 !shadow-sm !rounded-lg dark:[--xy-controls-button-background-color:var(--color-surface-900)] dark:[--xy-controls-button-background-color-hover:var(--color-surface-800)] dark:[--xy-controls-button-color:var(--color-surface-300)] dark:[--xy-controls-button-color-hover:var(--color-surface-50)] dark:[--xy-controls-button-border-color:var(--color-surface-700)]\"\n />\n <MiniMap\n nodeStrokeColor={(n) => {\n const d = n.data as TableNodeData;\n if (d.isJunction) return \"#a78bfa\";\n if (d.rlsEnabled) return \"#22c55e\";\n return \"#6366f1\";\n }}\n nodeColor={(n) => {\n const d = n.data as TableNodeData;\n if (d.isJunction) return \"#ede9fe\";\n return \"#eef2ff\";\n }}\n maskColor=\"rgba(0,0,0,0.08)\"\n className=\"!bg-white dark:!bg-surface-900 !border !border-surface-200/40 dark:!border-surface-700/40 !shadow-sm !rounded-lg\"\n />\n </ReactFlow>\n\n {/* Legend overlay */}\n <div className=\"absolute bottom-4 left-4 flex items-center gap-3 px-3 py-2 bg-white/90 dark:bg-surface-900/90 backdrop-blur-sm rounded-lg border border-surface-200/40 dark:border-surface-700/40 shadow-sm\">\n <div className=\"flex items-center gap-1.5\">\n <div className=\"w-6 h-0.5 bg-indigo-500 rounded\"/>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n Owning\n </Typography>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <div className=\"w-6 h-0.5 bg-violet-500 rounded\"/>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n M:N\n </Typography>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <div\n className=\"w-6 h-0.5 rounded\"\n style={{\n backgroundImage:\n \"repeating-linear-gradient(90deg, #94a3b8, #94a3b8 4px, transparent 4px, transparent 7px)\"\n }}\n />\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n Inverse\n </Typography>\n </div>\n <div className=\"h-3 w-px bg-surface-200 dark:bg-surface-700\"/>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[9px]\">🔑</span>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n PK\n </Typography>\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[9px]\">🔗</span>\n <Typography\n variant=\"caption\"\n className=\"text-[10px] text-text-disabled dark:text-text-disabled-dark\"\n >\n FK\n </Typography>\n </div>\n </div>\n </div>\n </div>\n }\n />\n );\n}\n\n// ─── Outer wrapper (provides ReactFlowProvider) ────────────────────────\n\nexport const SchemaVisualizer = () => {\n const { collections: registryCollections } =\n useStudioCollectionRegistry() as {\n collections?: AdminCollection[];\n };\n\n // Merge registry collections with any passed collections\n const collections = useMemo(() => {\n return registryCollections ?? [];\n }, [registryCollections]);\n\n if (!collections || collections.length === 0) {\n return (\n <div className=\"flex items-center justify-center h-full w-full\">\n <div className=\"text-center space-y-3\">\n <CircularProgress size=\"small\"/>\n <Typography\n variant=\"body2\"\n color=\"secondary\"\n >\n Loading schema…\n </Typography>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full w-full bg-white dark:bg-surface-950 overflow-hidden text-text-primary dark:text-text-primary-dark\">\n <ReactFlowProvider>\n <SchemaVisualizerCanvas collections={collections}/>\n </ReactFlowProvider>\n </div>\n );\n};\n"],"mappings":"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"}
|