@rebasepro/studio 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/ApiKeysView-D-_FSlNL.js +684 -0
  2. package/dist/ApiKeysView-D-_FSlNL.js.map +1 -0
  3. package/dist/{BranchesView-DncIRcZt.js → BranchesView-Dlg78EQ8.js} +4 -5
  4. package/dist/{BranchesView-DncIRcZt.js.map → BranchesView-Dlg78EQ8.js.map} +1 -1
  5. package/dist/{JSEditor-BhAbEjCP.js → JSEditor-DSucz6wV.js} +4 -4
  6. package/dist/{JSEditor-BhAbEjCP.js.map → JSEditor-DSucz6wV.js.map} +1 -1
  7. package/dist/{LogsExplorer-CqtKILj8.js → LogsExplorer-J4xfsuv3.js} +49 -83
  8. package/dist/LogsExplorer-J4xfsuv3.js.map +1 -0
  9. package/dist/{RLSEditor-CTxYbBdW.js → RLSEditor-BM64laoW.js} +483 -289
  10. package/dist/RLSEditor-BM64laoW.js.map +1 -0
  11. package/dist/{SQLEditor-BLuq_zDM.js → SQLEditor-CuAhR-zr.js} +4 -4
  12. package/dist/{SQLEditor-BLuq_zDM.js.map → SQLEditor-CuAhR-zr.js.map} +1 -1
  13. package/dist/{SchemaVisualizer-BJK2u3C0.js → SchemaVisualizer-OibKoD3g.js} +2 -2
  14. package/dist/{SchemaVisualizer-BJK2u3C0.js.map → SchemaVisualizer-OibKoD3g.js.map} +1 -1
  15. package/dist/{StorageView-nDaC2foF.js → StorageView-CvrnHmDG.js} +89 -76
  16. package/dist/StorageView-CvrnHmDG.js.map +1 -0
  17. package/dist/components/ApiKeys/ApiKeysView.d.ts +2 -0
  18. package/dist/index.es.js +38 -19
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +1358 -489
  21. package/dist/index.umd.js.map +1 -1
  22. package/package.json +17 -17
  23. package/src/components/ApiKeys/ApiKeysView.tsx +580 -0
  24. package/src/components/Branches/BranchesView.tsx +3 -4
  25. package/src/components/JSEditor/JSEditorSidebar.tsx +3 -3
  26. package/src/components/LogsExplorer/LogsExplorer.tsx +46 -84
  27. package/src/components/RLSEditor/PolicyEditor.tsx +1 -0
  28. package/src/components/RLSEditor/RLSEditor.tsx +354 -117
  29. package/src/components/RebaseStudio.tsx +10 -1
  30. package/src/components/SQLEditor/SchemaBrowser.tsx +3 -3
  31. package/src/components/StorageView/StorageView.tsx +3 -3
  32. package/src/components/StudioHomePage.tsx +5 -1
  33. package/dist/LogsExplorer-CqtKILj8.js.map +0 -1
  34. package/dist/RLSEditor-CTxYbBdW.js.map +0 -1
  35. package/dist/StorageView-nDaC2foF.js.map +0 -1
@@ -1,9 +1,10 @@
1
1
  import { t as MonacoEditor } from "./MonacoEditor-COZqrIJ1.js";
2
2
  import { ErrorView, useRebaseContext, useSnackbarController, useStudioCollectionRegistry, useTranslation } from "@rebasepro/core";
3
3
  import { useCallback, useEffect, useMemo, useState } from "react";
4
- import { Alert, AlertTriangleIcon, Button, Chip, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, HelpCircleIcon, IconButton, KeyIcon, MultiSelect, MultiSelectItem, Paper, RefreshCwIcon, ResizablePanels, Select, SelectItem, ShieldIcon, Tab, Tabs, TextField, Tooltip, Trash2Icon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
4
+ import { Alert, AlertTriangleIcon, Button, Chip, CircularProgress, DatabaseIcon, Dialog, DialogActions, DialogContent, DialogTitle, HelpCircleIcon, IconButton, KeyIcon, Link2Icon, LockIcon, MultiSelect, MultiSelectItem, Paper, RefreshCwIcon, ResizablePanels, Select, SelectItem, ShieldIcon, Tab, Tabs, TextField, Tooltip, Trash2Icon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { isPostgresCollection } from "@rebasepro/types";
7
+ import { JUNCTION_TABLES_SQL, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS } from "@rebasepro/common";
7
8
  //#region src/components/RLSEditor/PolicyEditor.tsx
8
9
  var COMMAND_OPTIONS = [
9
10
  "ALL",
@@ -373,199 +374,212 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
373
374
  open: helpOpen,
374
375
  onOpenChange: setHelpOpen,
375
376
  maxWidth: "3xl",
376
- children: [/* @__PURE__ */ jsxs(DialogContent, {
377
- className: "p-4 sm:p-6 lg:p-8 flex flex-col gap-6",
378
- children: [
379
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
380
- variant: "h5",
381
- className: "mb-2",
382
- children: t("studio_policy_help_title")
383
- }), /* @__PURE__ */ jsx(Typography, {
384
- className: "text-text-secondary dark:text-text-secondary-dark",
385
- children: t("studio_policy_help_intro")
386
- })] }),
387
- /* @__PURE__ */ jsxs("div", {
388
- className: "flex flex-col gap-4",
389
- children: [
390
- /* @__PURE__ */ jsxs(Paper, {
391
- className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
392
- children: [/* @__PURE__ */ jsx(Typography, {
393
- variant: "subtitle2",
394
- className: "text-primary dark:text-primary-light font-medium",
395
- children: t("studio_policy_help_step1_title")
396
- }), /* @__PURE__ */ jsx(Typography, {
397
- variant: "body2",
398
- className: "text-text-secondary dark:text-text-secondary-dark",
399
- children: t("studio_policy_help_step1_desc")
400
- })]
401
- }),
402
- /* @__PURE__ */ jsxs(Paper, {
403
- className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
404
- children: [
405
- /* @__PURE__ */ jsx(Typography, {
406
- variant: "subtitle2",
407
- className: "text-primary dark:text-primary-light font-medium",
408
- children: t("studio_policy_help_step2_title")
409
- }),
410
- /* @__PURE__ */ jsx(Typography, {
411
- variant: "body2",
412
- className: "text-text-secondary dark:text-text-secondary-dark mb-1",
413
- children: t("studio_policy_help_step2_desc")
414
- }),
415
- /* @__PURE__ */ jsxs("ul", {
416
- className: "list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark",
417
- children: [
418
- /* @__PURE__ */ jsxs("li", { children: [
419
- /* @__PURE__ */ jsx("strong", { children: "public" }),
420
- ": ",
421
- t("studio_policy_help_role_public")
422
- ] }),
423
- /* @__PURE__ */ jsxs("li", { children: [
424
- /* @__PURE__ */ jsx("strong", { children: "authenticated" }),
425
- ": ",
426
- t("studio_policy_help_role_authenticated")
427
- ] }),
428
- /* @__PURE__ */ jsxs("li", { children: [
429
- /* @__PURE__ */ jsx("strong", { children: "anon" }),
430
- ": ",
431
- t("studio_policy_help_role_anon")
432
- ] })
433
- ]
434
- })
435
- ]
436
- }),
437
- /* @__PURE__ */ jsxs(Paper, {
438
- className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
439
- children: [
440
- /* @__PURE__ */ jsx(Typography, {
441
- variant: "subtitle2",
442
- className: "text-primary dark:text-primary-light font-medium",
443
- children: t("studio_policy_help_step3_title")
444
- }),
445
- /* @__PURE__ */ jsx(Typography, {
446
- variant: "body2",
447
- className: "text-text-secondary dark:text-text-secondary-dark mb-1",
448
- children: t("studio_policy_help_step3_desc")
449
- }),
450
- /* @__PURE__ */ jsx("div", {
451
- className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
452
- children: "Example: auth.uid() = user_id"
453
- }),
454
- /* @__PURE__ */ jsx(Typography, {
455
- variant: "caption",
456
- className: "text-text-secondary dark:text-text-secondary-dark",
457
- children: t("studio_policy_help_step3_example")
458
- })
459
- ]
460
- }),
461
- /* @__PURE__ */ jsxs(Paper, {
462
- className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
463
- children: [
464
- /* @__PURE__ */ jsx(Typography, {
465
- variant: "subtitle2",
466
- className: "text-primary dark:text-primary-light font-medium",
467
- children: t("studio_policy_help_step4_title")
468
- }),
469
- /* @__PURE__ */ jsx(Typography, {
470
- variant: "body2",
471
- className: "text-text-secondary dark:text-text-secondary-dark mb-1",
472
- children: t("studio_policy_help_step4_desc")
473
- }),
474
- /* @__PURE__ */ jsx("div", {
475
- className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
476
- children: "Example: auth.uid() = user_id"
477
- }),
478
- /* @__PURE__ */ jsx(Typography, {
479
- variant: "caption",
480
- className: "text-text-secondary dark:text-text-secondary-dark",
481
- children: t("studio_policy_help_step4_example")
482
- })
483
- ]
484
- }),
485
- /* @__PURE__ */ jsxs(Paper, {
486
- className: cls("p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10", defaultBorderMixin),
487
- children: [
488
- /* @__PURE__ */ jsx(Typography, {
377
+ children: [
378
+ /* @__PURE__ */ jsx(DialogTitle, {
379
+ hidden: true,
380
+ children: "Row-Level Security Help"
381
+ }),
382
+ /* @__PURE__ */ jsxs(DialogContent, {
383
+ className: "p-4 sm:p-6 lg:p-8 flex flex-col gap-6",
384
+ children: [
385
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
386
+ variant: "h5",
387
+ className: "mb-2",
388
+ children: t("studio_policy_help_title")
389
+ }), /* @__PURE__ */ jsx(Typography, {
390
+ className: "text-text-secondary dark:text-text-secondary-dark",
391
+ children: t("studio_policy_help_intro")
392
+ })] }),
393
+ /* @__PURE__ */ jsxs("div", {
394
+ className: "flex flex-col gap-4",
395
+ children: [
396
+ /* @__PURE__ */ jsxs(Paper, {
397
+ className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
398
+ children: [/* @__PURE__ */ jsx(Typography, {
489
399
  variant: "subtitle2",
490
400
  className: "text-primary dark:text-primary-light font-medium",
491
- children: t("studio_policy_help_auth_vars_title")
492
- }),
493
- /* @__PURE__ */ jsx(Typography, {
401
+ children: t("studio_policy_help_step1_title")
402
+ }), /* @__PURE__ */ jsx(Typography, {
494
403
  variant: "body2",
495
404
  className: "text-text-secondary dark:text-text-secondary-dark",
496
- children: t("studio_policy_help_auth_vars_desc")
497
- }),
498
- /* @__PURE__ */ jsxs("ul", {
499
- className: "list-disc pl-5 space-y-2 text-sm text-text-secondary dark:text-text-secondary-dark font-normal",
500
- children: [
501
- /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
502
- className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
503
- children: "auth.uid()"
504
- }), /* @__PURE__ */ jsxs("span", {
505
- className: "block mt-0.5",
506
- children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */ jsx("code", {
507
- className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
508
- children: "auth.uid() = user_id"
509
- })]
510
- })] }),
511
- /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
512
- className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
513
- children: "auth.jwt()"
514
- }), /* @__PURE__ */ jsxs("span", {
515
- className: "block mt-0.5",
516
- children: ["Returns the full JWT payload as JSONB so you can check custom claims. Example: ", /* @__PURE__ */ jsx("code", {
517
- className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
518
- children: "auth.jwt() ->> 'email' = 'admin@example.com'"
519
- })]
520
- })] }),
521
- /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
522
- className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
523
- children: "auth.roles()"
524
- }), /* @__PURE__ */ jsxs("span", {
525
- className: "block mt-0.5",
526
- children: ["Returns the user's role IDs as a comma-separated string. Best used with: ", /* @__PURE__ */ jsx("code", {
527
- className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
528
- children: "string_to_array(auth.roles(), ',') @> ARRAY['admin']"
529
- })]
530
- })] })
531
- ]
532
- })
533
- ]
534
- })
535
- ]
536
- }),
537
- /* @__PURE__ */ jsxs("div", {
538
- 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),
539
- children: [/* @__PURE__ */ jsx(Typography, {
540
- variant: "body2",
541
- className: "text-primary dark:text-primary-light mb-4 sm:mb-0 max-w-md",
542
- children: t("studio_policy_help_docs_cta")
543
- }), /* @__PURE__ */ jsx(Button, {
544
- component: "a",
545
- href: "https://www.postgresql.org/docs/current/sql-createpolicy.html",
546
- target: "_blank",
547
- variant: "outlined",
548
- color: "primary",
549
- size: "small",
550
- className: "whitespace-nowrap flex-shrink-0",
551
- children: t("studio_policy_help_read_docs")
552
- })]
405
+ children: t("studio_policy_help_step1_desc")
406
+ })]
407
+ }),
408
+ /* @__PURE__ */ jsxs(Paper, {
409
+ className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
410
+ children: [
411
+ /* @__PURE__ */ jsx(Typography, {
412
+ variant: "subtitle2",
413
+ className: "text-primary dark:text-primary-light font-medium",
414
+ children: t("studio_policy_help_step2_title")
415
+ }),
416
+ /* @__PURE__ */ jsx(Typography, {
417
+ variant: "body2",
418
+ className: "text-text-secondary dark:text-text-secondary-dark mb-1",
419
+ children: t("studio_policy_help_step2_desc")
420
+ }),
421
+ /* @__PURE__ */ jsxs("ul", {
422
+ className: "list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark",
423
+ children: [
424
+ /* @__PURE__ */ jsxs("li", { children: [
425
+ /* @__PURE__ */ jsx("strong", { children: "public" }),
426
+ ": ",
427
+ t("studio_policy_help_role_public")
428
+ ] }),
429
+ /* @__PURE__ */ jsxs("li", { children: [
430
+ /* @__PURE__ */ jsx("strong", { children: "authenticated" }),
431
+ ": ",
432
+ t("studio_policy_help_role_authenticated")
433
+ ] }),
434
+ /* @__PURE__ */ jsxs("li", { children: [
435
+ /* @__PURE__ */ jsx("strong", { children: "anon" }),
436
+ ": ",
437
+ t("studio_policy_help_role_anon")
438
+ ] })
439
+ ]
440
+ })
441
+ ]
442
+ }),
443
+ /* @__PURE__ */ jsxs(Paper, {
444
+ className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
445
+ children: [
446
+ /* @__PURE__ */ jsx(Typography, {
447
+ variant: "subtitle2",
448
+ className: "text-primary dark:text-primary-light font-medium",
449
+ children: t("studio_policy_help_step3_title")
450
+ }),
451
+ /* @__PURE__ */ jsx(Typography, {
452
+ variant: "body2",
453
+ className: "text-text-secondary dark:text-text-secondary-dark mb-1",
454
+ children: t("studio_policy_help_step3_desc")
455
+ }),
456
+ /* @__PURE__ */ jsx("div", {
457
+ className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
458
+ children: "Example: auth.uid() = user_id"
459
+ }),
460
+ /* @__PURE__ */ jsx(Typography, {
461
+ variant: "caption",
462
+ className: "text-text-secondary dark:text-text-secondary-dark",
463
+ children: t("studio_policy_help_step3_example")
464
+ })
465
+ ]
466
+ }),
467
+ /* @__PURE__ */ jsxs(Paper, {
468
+ className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
469
+ children: [
470
+ /* @__PURE__ */ jsx(Typography, {
471
+ variant: "subtitle2",
472
+ className: "text-primary dark:text-primary-light font-medium",
473
+ children: t("studio_policy_help_step4_title")
474
+ }),
475
+ /* @__PURE__ */ jsx(Typography, {
476
+ variant: "body2",
477
+ className: "text-text-secondary dark:text-text-secondary-dark mb-1",
478
+ children: t("studio_policy_help_step4_desc")
479
+ }),
480
+ /* @__PURE__ */ jsx("div", {
481
+ className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
482
+ children: "Example: auth.uid() = user_id"
483
+ }),
484
+ /* @__PURE__ */ jsx(Typography, {
485
+ variant: "caption",
486
+ className: "text-text-secondary dark:text-text-secondary-dark",
487
+ children: t("studio_policy_help_step4_example")
488
+ })
489
+ ]
490
+ }),
491
+ /* @__PURE__ */ jsxs(Paper, {
492
+ className: cls("p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10", defaultBorderMixin),
493
+ children: [
494
+ /* @__PURE__ */ jsx(Typography, {
495
+ variant: "subtitle2",
496
+ className: "text-primary dark:text-primary-light font-medium",
497
+ children: t("studio_policy_help_auth_vars_title")
498
+ }),
499
+ /* @__PURE__ */ jsx(Typography, {
500
+ variant: "body2",
501
+ className: "text-text-secondary dark:text-text-secondary-dark",
502
+ children: t("studio_policy_help_auth_vars_desc")
503
+ }),
504
+ /* @__PURE__ */ jsxs("ul", {
505
+ className: "list-disc pl-5 space-y-2 text-sm text-text-secondary dark:text-text-secondary-dark font-normal",
506
+ children: [
507
+ /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
508
+ className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
509
+ children: "auth.uid()"
510
+ }), /* @__PURE__ */ jsxs("span", {
511
+ className: "block mt-0.5",
512
+ children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */ jsx("code", {
513
+ className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
514
+ children: "auth.uid() = user_id"
515
+ })]
516
+ })] }),
517
+ /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
518
+ className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
519
+ children: "auth.jwt()"
520
+ }), /* @__PURE__ */ jsxs("span", {
521
+ className: "block mt-0.5",
522
+ children: ["Returns the full JWT payload as JSONB so you can check custom claims. Example: ", /* @__PURE__ */ jsx("code", {
523
+ className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
524
+ children: "auth.jwt() ->> 'email' = 'admin@example.com'"
525
+ })]
526
+ })] }),
527
+ /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
528
+ className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
529
+ children: "auth.roles()"
530
+ }), /* @__PURE__ */ jsxs("span", {
531
+ className: "block mt-0.5",
532
+ children: ["Returns the user's role IDs as a comma-separated string. Best used with: ", /* @__PURE__ */ jsx("code", {
533
+ className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
534
+ children: "string_to_array(auth.roles(), ',') @> ARRAY['admin']"
535
+ })]
536
+ })] })
537
+ ]
538
+ })
539
+ ]
540
+ })
541
+ ]
542
+ }),
543
+ /* @__PURE__ */ jsxs("div", {
544
+ 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),
545
+ children: [/* @__PURE__ */ jsx(Typography, {
546
+ variant: "body2",
547
+ className: "text-primary dark:text-primary-light mb-4 sm:mb-0 max-w-md",
548
+ children: t("studio_policy_help_docs_cta")
549
+ }), /* @__PURE__ */ jsx(Button, {
550
+ component: "a",
551
+ href: "https://www.postgresql.org/docs/current/sql-createpolicy.html",
552
+ target: "_blank",
553
+ variant: "outlined",
554
+ color: "primary",
555
+ size: "small",
556
+ className: "whitespace-nowrap flex-shrink-0",
557
+ children: t("studio_policy_help_read_docs")
558
+ })]
559
+ })
560
+ ]
561
+ }),
562
+ /* @__PURE__ */ jsx(DialogActions, {
563
+ className: "p-4 sm:px-6 sm:pb-6 pt-0 border-t-0",
564
+ children: /* @__PURE__ */ jsx(Button, {
565
+ onClick: () => setHelpOpen(false),
566
+ variant: "filled",
567
+ color: "primary",
568
+ children: t("studio_policy_help_got_it")
553
569
  })
554
- ]
555
- }), /* @__PURE__ */ jsx(DialogActions, {
556
- className: "p-4 sm:px-6 sm:pb-6 pt-0 border-t-0",
557
- children: /* @__PURE__ */ jsx(Button, {
558
- onClick: () => setHelpOpen(false),
559
- variant: "filled",
560
- color: "primary",
561
- children: t("studio_policy_help_got_it")
562
570
  })
563
- })]
571
+ ]
564
572
  })
565
573
  ] });
566
574
  };
567
575
  //#endregion
568
576
  //#region src/components/RLSEditor/RLSEditor.tsx
577
+ function classifyTableClient(tableName, schemaName, junctionTableNames, isMappedToCollection) {
578
+ if (REBASE_INTERNAL_SCHEMAS.includes(schemaName) || REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))) return "internal";
579
+ if (isMappedToCollection) return "collection";
580
+ if (junctionTableNames.has(tableName)) return "junction";
581
+ return "other";
582
+ }
569
583
  /**
570
584
  * Validates and double-quotes a SQL identifier to prevent injection.
571
585
  * Only allows safe Postgres identifiers (letters, digits, underscores).
@@ -575,6 +589,81 @@ function sanitizeSqlIdentifier(name) {
575
589
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) throw new Error(`Invalid SQL identifier: "${name}". Only letters, digits, and underscores are allowed.`);
576
590
  return `"${name}"`;
577
591
  }
592
+ function SidebarSection({ title, icon, expanded, onToggle, count, children }) {
593
+ return /* @__PURE__ */ jsxs("div", {
594
+ className: "mb-2",
595
+ children: [/* @__PURE__ */ jsxs("div", {
596
+ className: "flex items-center p-1.5 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors",
597
+ onClick: onToggle,
598
+ children: [
599
+ /* @__PURE__ */ jsx("svg", {
600
+ className: cls("w-3 h-3 mr-1.5 transition-transform text-text-disabled dark:text-text-disabled-dark", expanded ? "rotate-90" : ""),
601
+ fill: "currentColor",
602
+ viewBox: "0 0 20 20",
603
+ children: /* @__PURE__ */ jsx("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" })
604
+ }),
605
+ icon,
606
+ /* @__PURE__ */ jsx(Typography, {
607
+ variant: "body2",
608
+ className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow ml-1.5",
609
+ children: title
610
+ }),
611
+ count !== void 0 && /* @__PURE__ */ jsx("span", {
612
+ className: "text-[10px] text-text-disabled dark:text-text-disabled-dark font-medium tabular-nums mr-1",
613
+ children: count
614
+ })
615
+ ]
616
+ }), expanded && /* @__PURE__ */ jsx("div", {
617
+ className: "ml-3 mt-0.5 space-y-0.5",
618
+ children
619
+ })]
620
+ });
621
+ }
622
+ function SidebarTableRow({ table, isSelected, onSelect, badge, dimmed, t }) {
623
+ return /* @__PURE__ */ jsxs("div", {
624
+ onClick: onSelect,
625
+ className: cls("flex items-center p-1 cursor-pointer rounded transition-colors group relative", isSelected ? "bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-light" : "hover:bg-surface-100 dark:hover:bg-surface-900 text-text-secondary dark:text-text-secondary-dark", dimmed && !isSelected && "opacity-60"),
626
+ children: [
627
+ /* @__PURE__ */ jsx("svg", {
628
+ className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark",
629
+ fill: "none",
630
+ stroke: "currentColor",
631
+ viewBox: "0 0 24 24",
632
+ children: /* @__PURE__ */ jsx("path", {
633
+ strokeLinecap: "round",
634
+ strokeLinejoin: "round",
635
+ strokeWidth: 2,
636
+ 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"
637
+ })
638
+ }),
639
+ /* @__PURE__ */ jsx(Typography, {
640
+ variant: "body2",
641
+ className: "text-xs truncate flex-1 min-w-0",
642
+ children: table.tableName
643
+ }),
644
+ /* @__PURE__ */ jsxs("div", {
645
+ className: "flex items-center gap-1.5 shrink-0 ml-2",
646
+ children: [
647
+ badge && /* @__PURE__ */ jsx("span", {
648
+ 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",
649
+ children: badge
650
+ }),
651
+ table.rlsEnabled ? /* @__PURE__ */ jsx(Tooltip, {
652
+ title: t("studio_rls_enabled"),
653
+ children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
654
+ }) : /* @__PURE__ */ jsx(Tooltip, {
655
+ title: t("studio_rls_disabled"),
656
+ children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50" })
657
+ }),
658
+ /* @__PURE__ */ jsx("span", {
659
+ className: "text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium",
660
+ children: table.policies.length
661
+ })
662
+ ]
663
+ })
664
+ ]
665
+ });
666
+ }
578
667
  var RLSEditor = ({ apiUrl = "" }) => {
579
668
  const { databaseAdmin } = useRebaseContext();
580
669
  const snackbarController = useSnackbarController();
@@ -585,6 +674,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
585
674
  const [tables, setTables] = useState([]);
586
675
  const [selectedTable, setSelectedTable] = useState(null);
587
676
  const [activeTab, setActiveTab] = useState(0);
677
+ const [junctionTableNames, setJunctionTableNames] = useState(/* @__PURE__ */ new Set());
588
678
  const [editingPolicy, setEditingPolicy] = useState(null);
589
679
  const [sidebarSize, setSidebarSize] = useState(() => {
590
680
  try {
@@ -594,7 +684,11 @@ var RLSEditor = ({ apiUrl = "" }) => {
594
684
  return 20;
595
685
  }
596
686
  });
597
- const [expandedSchemas, setExpandedSchemas] = useState({ public: true });
687
+ const [expandedSections, setExpandedSections] = useState({
688
+ collection: true,
689
+ other: true,
690
+ internal: false
691
+ });
598
692
  const [sidebarTab, setSidebarTab] = useState("tables");
599
693
  useEffect(() => {
600
694
  try {
@@ -676,7 +770,15 @@ var RLSEditor = ({ apiUrl = "" }) => {
676
770
  });
677
771
  const sortedTables = Object.values(tableMap).sort((a, b) => a.tableName.localeCompare(b.tableName));
678
772
  setTables(sortedTables);
679
- if (sortedTables.length > 0 && !selectedTable) setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);
773
+ try {
774
+ const jRows = extractRows(await databaseAdmin.executeSql(JUNCTION_TABLES_SQL));
775
+ setJunctionTableNames(new Set(jRows.map((r) => r.table_name).filter(Boolean)));
776
+ } catch {}
777
+ if (sortedTables.length > 0 && !selectedTable) {
778
+ const firstUserTable = sortedTables.find((t) => !REBASE_INTERNAL_SCHEMAS.includes(t.schemaName));
779
+ if (firstUserTable) setSelectedTable(`${firstUserTable.schemaName}.${firstUserTable.tableName}`);
780
+ else setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);
781
+ }
680
782
  } catch (e) {
681
783
  console.error("RLS fetch error:", e);
682
784
  setError("Failed to fetch RLS policies: " + (e instanceof Error ? e.message : String(e)));
@@ -694,18 +796,38 @@ var RLSEditor = ({ apiUrl = "" }) => {
694
796
  if (!selectedTable) return null;
695
797
  return tables.find((t) => `${t.schemaName}.${t.tableName}` === selectedTable) || null;
696
798
  }, [selectedTable, tables]);
697
- const groupedTables = useMemo(() => {
698
- const groups = {};
799
+ /** Categorize tables into 4 buckets for the sidebar. */
800
+ const categorizedTables = useMemo(() => {
801
+ const groups = {
802
+ collection: [],
803
+ junction: [],
804
+ internal: [],
805
+ other: []
806
+ };
699
807
  tables.forEach((table) => {
700
- if (!groups[table.schemaName]) groups[table.schemaName] = [];
701
- groups[table.schemaName].push(table);
808
+ const isMapped = !!collectionRegistry.collections?.find((c) => c.id === table.tableName || c.path === table.tableName || c.table === table.tableName || c.slug === table.tableName || c.collectionId === table.tableName);
809
+ groups[classifyTableClient(table.tableName, table.schemaName, junctionTableNames, isMapped)].push(table);
702
810
  });
703
811
  return groups;
704
- }, [tables]);
812
+ }, [
813
+ tables,
814
+ junctionTableNames,
815
+ collectionRegistry.collections
816
+ ]);
705
817
  const activeCollection = useMemo(() => {
706
818
  if (!activeTableData) return null;
707
819
  return collectionRegistry.collections?.find((c) => c.id === activeTableData.tableName || c.path === activeTableData.tableName || c.table === activeTableData.tableName || c.slug === activeTableData.tableName || c.collectionId === activeTableData.tableName) || null;
708
820
  }, [activeTableData, collectionRegistry.collections]);
821
+ /** The category of the currently selected table. */
822
+ const activeTableCategory = useMemo(() => {
823
+ if (!activeTableData) return null;
824
+ const isMapped = !!activeCollection;
825
+ return classifyTableClient(activeTableData.tableName, activeTableData.schemaName, junctionTableNames, isMapped);
826
+ }, [
827
+ activeTableData,
828
+ activeCollection,
829
+ junctionTableNames
830
+ ]);
709
831
  const mergedPolicies = useMemo(() => {
710
832
  if (!activeTableData) return [];
711
833
  const policiesMap = {};
@@ -803,74 +925,78 @@ var RLSEditor = ({ apiUrl = "" }) => {
803
925
  children: isLoading && tables.length === 0 ? /* @__PURE__ */ jsx("div", {
804
926
  className: "flex justify-center p-4",
805
927
  children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" })
806
- }) : Object.keys(groupedTables).length === 0 ? /* @__PURE__ */ jsx("div", {
928
+ }) : tables.length === 0 ? /* @__PURE__ */ jsx("div", {
807
929
  className: "p-4 text-center",
808
930
  children: /* @__PURE__ */ jsx(Typography, {
809
931
  variant: "caption",
810
932
  className: "text-text-disabled dark:text-text-disabled-dark italic",
811
933
  children: t("studio_rls_no_tables")
812
934
  })
813
- }) : Object.entries(groupedTables).map(([schemaName, schemaTables]) => /* @__PURE__ */ jsxs("div", {
814
- className: "mb-2",
815
- children: [/* @__PURE__ */ jsxs("div", {
816
- className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors",
817
- onClick: () => setExpandedSchemas((prev) => ({
935
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
936
+ categorizedTables.collection.length > 0 && /* @__PURE__ */ jsx(SidebarSection, {
937
+ title: "Schema Collections",
938
+ icon: /* @__PURE__ */ jsx(DatabaseIcon, {
939
+ size: 12,
940
+ className: "text-primary dark:text-primary-light"
941
+ }),
942
+ expanded: expandedSections.collection ?? true,
943
+ onToggle: () => setExpandedSections((prev) => ({
818
944
  ...prev,
819
- [schemaName]: !prev[schemaName]
945
+ collection: !(prev.collection ?? true)
820
946
  })),
821
- children: [/* @__PURE__ */ jsx("svg", {
822
- className: cls("w-3 h-3 mr-1 transition-transform", expandedSchemas[schemaName] ? "rotate-90" : ""),
823
- fill: "currentColor",
824
- viewBox: "0 0 20 20",
825
- children: /* @__PURE__ */ jsx("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" })
826
- }), /* @__PURE__ */ jsx(Typography, {
827
- variant: "body2",
828
- className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow",
829
- children: schemaName
830
- })]
831
- }), expandedSchemas[schemaName] && /* @__PURE__ */ jsx("div", {
832
- className: "ml-3 mt-1 space-y-0.5",
833
- children: schemaTables.map((table) => {
834
- const key = `${table.schemaName}.${table.tableName}`;
835
- return /* @__PURE__ */ jsxs("div", {
836
- onClick: () => setSelectedTable(key),
837
- className: cls("flex items-center p-1 cursor-pointer rounded transition-colors group relative", selectedTable === key ? "bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-light" : "hover:bg-surface-100 dark:hover:bg-surface-950 text-text-secondary dark:text-text-secondary-dark"),
838
- children: [
839
- /* @__PURE__ */ jsx("svg", {
840
- className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark",
841
- fill: "none",
842
- stroke: "currentColor",
843
- viewBox: "0 0 24 24",
844
- children: /* @__PURE__ */ jsx("path", {
845
- strokeLinecap: "round",
846
- strokeLinejoin: "round",
847
- strokeWidth: 2,
848
- 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"
849
- })
850
- }),
851
- /* @__PURE__ */ jsx(Typography, {
852
- variant: "body2",
853
- className: "text-xs truncate flex-1 min-w-0",
854
- children: table.tableName
855
- }),
856
- /* @__PURE__ */ jsxs("div", {
857
- className: "flex items-center gap-1.5 shrink-0 ml-2",
858
- children: [table.rlsEnabled ? /* @__PURE__ */ jsx(Tooltip, {
859
- title: t("studio_rls_enabled"),
860
- children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
861
- }) : /* @__PURE__ */ jsx(Tooltip, {
862
- title: t("studio_rls_disabled"),
863
- children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50" })
864
- }), /* @__PURE__ */ jsx("span", {
865
- className: "text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium",
866
- children: table.policies.length
867
- })]
868
- })
869
- ]
870
- }, key);
871
- })
872
- })]
873
- }, schemaName))
947
+ children: categorizedTables.collection.map((table) => /* @__PURE__ */ jsx(SidebarTableRow, {
948
+ table,
949
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
950
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
951
+ t
952
+ }, `${table.schemaName}.${table.tableName}`))
953
+ }),
954
+ (categorizedTables.other.length > 0 || categorizedTables.junction.length > 0) && /* @__PURE__ */ jsxs(SidebarSection, {
955
+ title: "Other Tables",
956
+ icon: /* @__PURE__ */ jsx(AlertTriangleIcon, {
957
+ size: 12,
958
+ className: "text-yellow-500 dark:text-yellow-400"
959
+ }),
960
+ expanded: expandedSections.other ?? true,
961
+ onToggle: () => setExpandedSections((prev) => ({
962
+ ...prev,
963
+ other: !(prev.other ?? true)
964
+ })),
965
+ children: [categorizedTables.other.map((table) => /* @__PURE__ */ jsx(SidebarTableRow, {
966
+ table,
967
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
968
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
969
+ t
970
+ }, `${table.schemaName}.${table.tableName}`)), categorizedTables.junction.map((table) => /* @__PURE__ */ jsx(SidebarTableRow, {
971
+ table,
972
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
973
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
974
+ badge: "Junction",
975
+ dimmed: true,
976
+ t
977
+ }, `${table.schemaName}.${table.tableName}`))]
978
+ }),
979
+ categorizedTables.internal.length > 0 && /* @__PURE__ */ jsx(SidebarSection, {
980
+ title: "Rebase Internal",
981
+ icon: /* @__PURE__ */ jsx(LockIcon, {
982
+ size: 12,
983
+ className: "text-text-disabled dark:text-text-disabled-dark"
984
+ }),
985
+ expanded: expandedSections.internal ?? false,
986
+ onToggle: () => setExpandedSections((prev) => ({
987
+ ...prev,
988
+ internal: !(prev.internal ?? false)
989
+ })),
990
+ count: categorizedTables.internal.length,
991
+ children: categorizedTables.internal.map((table) => /* @__PURE__ */ jsx(SidebarTableRow, {
992
+ table,
993
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
994
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
995
+ dimmed: true,
996
+ t
997
+ }, `${table.schemaName}.${table.tableName}`))
998
+ })
999
+ ] })
874
1000
  })]
875
1001
  }), sidebarTab === "info" && /* @__PURE__ */ jsxs("div", {
876
1002
  className: "flex flex-col h-full",
@@ -1033,10 +1159,11 @@ var RLSEditor = ({ apiUrl = "" }) => {
1033
1159
  size: "small",
1034
1160
  onClick: async () => {
1035
1161
  const table = activeTableData.tableName;
1162
+ const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
1036
1163
  const action = activeTableData.rlsEnabled ? "DISABLE" : "ENABLE";
1037
1164
  if (!confirm(`Are you sure you want to ${action.toLowerCase()} Row Level Security on "${table}"?`)) return;
1038
1165
  try {
1039
- await databaseAdmin.executeSql(`ALTER TABLE ${sanitizeSqlIdentifier(table)} ${action} ROW LEVEL SECURITY`);
1166
+ await databaseAdmin.executeSql(`ALTER TABLE ${qualifiedTable} ${action} ROW LEVEL SECURITY`);
1040
1167
  snackbarController.open({
1041
1168
  type: "success",
1042
1169
  message: `RLS ${action.toLowerCase()}d on ${table}`
@@ -1063,7 +1190,6 @@ var RLSEditor = ({ apiUrl = "" }) => {
1063
1190
  /* @__PURE__ */ jsx(Button, {
1064
1191
  size: "small",
1065
1192
  color: "primary",
1066
- disabled: !activeCollection,
1067
1193
  onClick: () => setEditingPolicy("new"),
1068
1194
  children: t("studio_rls_create_policy")
1069
1195
  })
@@ -1104,31 +1230,57 @@ var RLSEditor = ({ apiUrl = "" }) => {
1104
1230
  schema: activeTableData.schemaName,
1105
1231
  table: activeTableData.tableName,
1106
1232
  onSave: async (newPolicy) => {
1107
- if (!activeCollection) return;
1108
- const rule = {
1109
- name: newPolicy.policyname,
1110
- operation: newPolicy.cmd?.toLowerCase(),
1111
- mode: newPolicy.permissive?.toLowerCase(),
1112
- using: newPolicy.qual || void 0,
1113
- withCheck: newPolicy.with_check || void 0,
1114
- roles: newPolicy.roles
1115
- };
1116
- const existingRules = (isPostgresCollection(activeCollection) ? activeCollection.securityRules : void 0) || [];
1117
- let newRules;
1118
- if (editingPolicy === "new") newRules = [...existingRules, rule];
1119
- else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
1120
- try {
1121
- if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
1122
- method: "POST",
1123
- headers: { "Content-Type": "application/json" },
1124
- body: JSON.stringify({
1125
- collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
1126
- collectionData: { securityRules: newRules }
1127
- })
1128
- })).ok) throw new Error("Failed to save policy");
1233
+ if (activeCollection) {
1234
+ const rule = {
1235
+ name: newPolicy.policyname,
1236
+ operation: newPolicy.cmd?.toLowerCase(),
1237
+ mode: newPolicy.permissive?.toLowerCase(),
1238
+ using: newPolicy.qual || void 0,
1239
+ withCheck: newPolicy.with_check || void 0,
1240
+ roles: newPolicy.roles
1241
+ };
1242
+ const existingRules = (isPostgresCollection(activeCollection) ? activeCollection.securityRules : void 0) || [];
1243
+ let newRules;
1244
+ if (editingPolicy === "new") newRules = [...existingRules, rule];
1245
+ else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
1246
+ try {
1247
+ if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
1248
+ method: "POST",
1249
+ headers: { "Content-Type": "application/json" },
1250
+ body: JSON.stringify({
1251
+ collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
1252
+ collectionData: { securityRules: newRules }
1253
+ })
1254
+ })).ok) throw new Error("Failed to save policy");
1255
+ snackbarController.open({
1256
+ type: "success",
1257
+ message: "Policy saved successfully"
1258
+ });
1259
+ setEditingPolicy(null);
1260
+ fetchRLSData();
1261
+ } catch (e) {
1262
+ snackbarController.open({
1263
+ type: "error",
1264
+ message: e instanceof Error ? e.message : String(e)
1265
+ });
1266
+ }
1267
+ } else try {
1268
+ const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(activeTableData.tableName)}`;
1269
+ const policyName = sanitizeSqlIdentifier(newPolicy.policyname || "unnamed_policy");
1270
+ const cmd = newPolicy.cmd || "ALL";
1271
+ const permissive = (newPolicy.permissive || "PERMISSIVE") === "PERMISSIVE" ? "PERMISSIVE" : "RESTRICTIVE";
1272
+ const roles = newPolicy.roles && newPolicy.roles.length > 0 ? newPolicy.roles.map((r) => sanitizeSqlIdentifier(r)).join(", ") : "public";
1273
+ if (editingPolicy !== "new") await databaseAdmin.executeSql(`DROP POLICY IF EXISTS ${policyName} ON ${qualifiedTable}`);
1274
+ let sql = `CREATE POLICY ${policyName} ON ${qualifiedTable}`;
1275
+ sql += ` AS ${permissive}`;
1276
+ sql += ` FOR ${cmd}`;
1277
+ sql += ` TO ${roles}`;
1278
+ if (newPolicy.qual) sql += ` USING (${newPolicy.qual})`;
1279
+ if (newPolicy.with_check) sql += ` WITH CHECK (${newPolicy.with_check})`;
1280
+ await databaseAdmin.executeSql(sql);
1129
1281
  snackbarController.open({
1130
1282
  type: "success",
1131
- message: "Policy saved successfully"
1283
+ message: `Policy "${newPolicy.policyname}" applied directly via SQL`
1132
1284
  });
1133
1285
  setEditingPolicy(null);
1134
1286
  fetchRLSData();
@@ -1147,17 +1299,59 @@ var RLSEditor = ({ apiUrl = "" }) => {
1147
1299
  children: /* @__PURE__ */ jsxs("div", {
1148
1300
  className: "max-w-4xl mx-auto flex flex-col gap-6",
1149
1301
  children: [
1150
- activeTableData && !activeCollection && /* @__PURE__ */ jsxs(Alert, {
1302
+ activeTableData && activeTableCategory === "internal" && /* @__PURE__ */ jsx(Alert, {
1303
+ color: "info",
1304
+ children: /* @__PURE__ */ jsxs("div", {
1305
+ className: "flex items-start gap-2",
1306
+ children: [/* @__PURE__ */ jsx(LockIcon, {
1307
+ size: 16,
1308
+ className: "shrink-0 mt-0.5"
1309
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
1310
+ variant: "body2",
1311
+ className: "mb-1 font-semibold",
1312
+ children: "Rebase System Table"
1313
+ }), /* @__PURE__ */ jsx(Typography, {
1314
+ variant: "caption",
1315
+ className: "opacity-80",
1316
+ children: "This table is managed internally by Rebase. Its security policies are configured automatically. Editing policies on system tables is an advanced operation."
1317
+ })] })]
1318
+ })
1319
+ }),
1320
+ activeTableData && activeTableCategory === "junction" && /* @__PURE__ */ jsx(Alert, {
1321
+ color: "info",
1322
+ children: /* @__PURE__ */ jsxs("div", {
1323
+ className: "flex items-start gap-2",
1324
+ children: [/* @__PURE__ */ jsx(Link2Icon, {
1325
+ size: 16,
1326
+ className: "shrink-0 mt-0.5"
1327
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
1328
+ variant: "body2",
1329
+ className: "mb-1 font-semibold",
1330
+ children: "Junction Table"
1331
+ }), /* @__PURE__ */ jsx(Typography, {
1332
+ variant: "caption",
1333
+ className: "opacity-80",
1334
+ children: "This is an auto-generated junction table for a many-to-many relation. Its access is typically managed through the related collections' policies. You can still add RLS policies directly if needed."
1335
+ })] })]
1336
+ })
1337
+ }),
1338
+ activeTableData && activeTableCategory === "other" && /* @__PURE__ */ jsx(Alert, {
1151
1339
  color: "warning",
1152
- children: [/* @__PURE__ */ jsx(Typography, {
1153
- variant: "body2",
1154
- className: "mb-1",
1155
- children: "Table not managed by Rebase"
1156
- }), /* @__PURE__ */ jsx(Typography, {
1157
- variant: "caption",
1158
- className: "opacity-80",
1159
- children: "This table is not mapped to a Rebase Schema via code. To edit security policies visually, you must first import this table into a Schema configuration file."
1160
- })]
1340
+ children: /* @__PURE__ */ jsxs("div", {
1341
+ className: "flex items-start gap-2",
1342
+ children: [/* @__PURE__ */ jsx(AlertTriangleIcon, {
1343
+ size: 16,
1344
+ className: "shrink-0 mt-0.5"
1345
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
1346
+ variant: "body2",
1347
+ className: "mb-1 font-semibold",
1348
+ children: "Unmapped Table"
1349
+ }), /* @__PURE__ */ jsx(Typography, {
1350
+ variant: "caption",
1351
+ className: "opacity-80",
1352
+ children: "This table exists in the database but isn't mapped to a collection definition. Import it into a Schema configuration file to manage security policies visually."
1353
+ })] })]
1354
+ })
1161
1355
  }),
1162
1356
  activeTableData && !activeTableData.rlsEnabled && /* @__PURE__ */ jsxs("div", {
1163
1357
  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),
@@ -1271,7 +1465,6 @@ var RLSEditor = ({ apiUrl = "" }) => {
1271
1465
  variant: "text",
1272
1466
  color: "primary",
1273
1467
  onClick: () => setEditingPolicy(policy),
1274
- disabled: !activeCollection,
1275
1468
  children: t("studio_rls_edit")
1276
1469
  }),
1277
1470
  policy.status !== "code_only" && /* @__PURE__ */ jsx(Tooltip, {
@@ -1281,9 +1474,10 @@ var RLSEditor = ({ apiUrl = "" }) => {
1281
1474
  size: "small",
1282
1475
  onClick: async () => {
1283
1476
  const table = activeTableData.tableName;
1477
+ const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
1284
1478
  if (!confirm(`Drop policy "${policy.policyname}" from table "${table}"?`)) return;
1285
1479
  try {
1286
- await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${sanitizeSqlIdentifier(table)}`);
1480
+ await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${qualifiedTable}`);
1287
1481
  snackbarController.open({
1288
1482
  type: "success",
1289
1483
  message: `Policy "${policy.policyname}" dropped`
@@ -1320,7 +1514,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
1320
1514
  className: "text-text-disabled dark:text-text-disabled-dark max-w-sm mb-4",
1321
1515
  children: "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."
1322
1516
  }),
1323
- activeCollection && /* @__PURE__ */ jsx(Button, {
1517
+ /* @__PURE__ */ jsx(Button, {
1324
1518
  size: "small",
1325
1519
  variant: "filled",
1326
1520
  color: "primary",
@@ -1359,4 +1553,4 @@ var RLSEditor = ({ apiUrl = "" }) => {
1359
1553
  //#endregion
1360
1554
  export { RLSEditor };
1361
1555
 
1362
- //# sourceMappingURL=RLSEditor-CTxYbBdW.js.map
1556
+ //# sourceMappingURL=RLSEditor-BM64laoW.js.map