@rebasepro/admin 0.11.1-canary.gfd39654 → 0.12.1-canary.gdfba2a1

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 (127) hide show
  1. package/README.md +1 -1
  2. package/dist/{CollectionEditorDialog-o7F_zbGY.js → CollectionEditorDialog-B8j6HArJ.js} +72 -268
  3. package/dist/CollectionEditorDialog-B8j6HArJ.js.map +1 -0
  4. package/dist/{PropertyEditView-DgB2b22q.js → PropertyEditView-D2b9_nbC.js} +12 -9
  5. package/dist/{PropertyEditView-DgB2b22q.js.map → PropertyEditView-D2b9_nbC.js.map} +1 -1
  6. package/dist/{RouterCollectionsStudioView-DaLRgPkj.js → RouterCollectionsStudioView-CXUtYIc1.js} +4 -4
  7. package/dist/{RouterCollectionsStudioView-DaLRgPkj.js.map → RouterCollectionsStudioView-CXUtYIc1.js.map} +1 -1
  8. package/dist/collection_editor/CollectionEditorDialogsContext.d.ts +1 -1
  9. package/dist/collection_editor/_cms_internals.d.ts +1 -1
  10. package/dist/collection_editor/extensibility_types.d.ts +1 -1
  11. package/dist/collection_editor/serializable_types.d.ts +40 -12
  12. package/dist/collection_editor/serializable_utils.d.ts +1 -1
  13. package/dist/collection_editor/ui/collection_editor/CollectionRLSTab.d.ts +1 -11
  14. package/dist/collection_editor_ui.js +4 -4
  15. package/dist/components/CollectionEditorDialogs.d.ts +2 -2
  16. package/dist/components/CollectionTableBinding/column_utils.d.ts +7 -1
  17. package/dist/components/CollectionViewBinding/CollectionListViewBinding.d.ts +2 -2
  18. package/dist/components/DefaultDrawer.d.ts +3 -3
  19. package/dist/components/DrawerNavigationGroup.d.ts +8 -1
  20. package/dist/components/DrawerNavigationItem.d.ts +7 -1
  21. package/dist/components/EditViewBinding.d.ts +1 -1
  22. package/dist/components/RebaseAdmin.d.ts +1 -1
  23. package/dist/components/RebaseAuthGate.d.ts +1 -1
  24. package/dist/components/RebaseLayout.d.ts +5 -1
  25. package/dist/components/RebaseNavigation.d.ts +2 -2
  26. package/dist/components/RebaseRouteDefs.d.ts +1 -1
  27. package/dist/components/RebaseShell.d.ts +6 -1
  28. package/dist/components/RelationSelector.d.ts +14 -0
  29. package/dist/components/SelectableTable/filters/RelationFilterField.d.ts +27 -0
  30. package/dist/components/SelectableTable/filters/null_filter.d.ts +46 -0
  31. package/dist/components/SidePanelProvider.d.ts +2 -2
  32. package/dist/components/app/Scaffold.d.ts +11 -0
  33. package/dist/components/field_configs.d.ts +1 -1
  34. package/dist/editor.js +18 -10
  35. package/dist/editor.js.map +1 -1
  36. package/dist/{export-BvqhjdEb.js → export-CrDJOGKn.js} +3 -3
  37. package/dist/export-CrDJOGKn.js.map +1 -0
  38. package/dist/form/EntityForm.d.ts +2 -2
  39. package/dist/form/EntityFormBinding.d.ts +5 -5
  40. package/dist/{history-Dz20hH38.js → history-s6Nzt7RF.js} +2 -2
  41. package/dist/{history-Dz20hH38.js.map → history-s6Nzt7RF.js.map} +1 -1
  42. package/dist/hooks/index.d.ts +1 -1
  43. package/dist/hooks/useAdminContext.d.ts +29 -0
  44. package/dist/hooks/useHistory.d.ts +7 -11
  45. package/dist/{import-DINSB-Vu.js → import-Ci32nU0G.js} +2 -2
  46. package/dist/{import-DINSB-Vu.js.map → import-Ci32nU0G.js.map} +1 -1
  47. package/dist/index.js +72 -44
  48. package/dist/index.js.map +1 -1
  49. package/dist/markdown-CklalUUk.js.map +1 -1
  50. package/dist/types/components/EntityFormProps.d.ts +1 -1
  51. package/dist/types/fields.d.ts +1 -1
  52. package/dist/{util-BhhfHY1M.js → util-BwZuV81o.js} +378 -214
  53. package/dist/util-BwZuV81o.js.map +1 -0
  54. package/package.json +24 -24
  55. package/src/collection_editor/CollectionEditorDialogsContext.tsx +1 -1
  56. package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
  57. package/src/collection_editor/_cms_internals.ts +1 -1
  58. package/src/collection_editor/extensibility_types.ts +1 -1
  59. package/src/collection_editor/index.ts +2 -2
  60. package/src/collection_editor/serializable_types.ts +44 -13
  61. package/src/collection_editor/serializable_utils.ts +68 -59
  62. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +1 -1
  63. package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +66 -41
  64. package/src/collection_editor/ui/collection_editor/EntityActionsEditTab.tsx +1 -1
  65. package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +1 -1
  66. package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +2 -2
  67. package/src/components/CollectionEditorDialogs.tsx +2 -2
  68. package/src/components/CollectionTableBinding/CollectionRowActions.tsx +5 -4
  69. package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +8 -2
  70. package/src/components/CollectionTableBinding/column_utils.tsx +19 -5
  71. package/src/components/CollectionTableBinding/table_bindings.tsx +10 -10
  72. package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +8 -7
  73. package/src/components/CollectionViewBinding/CollectionViewActions.tsx +2 -2
  74. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +2 -2
  75. package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +2 -2
  76. package/src/components/DefaultDrawer.tsx +19 -8
  77. package/src/components/DeleteEntityDialog.tsx +2 -2
  78. package/src/components/DetailViewBinding.tsx +21 -1
  79. package/src/components/DrawerNavigationGroup.tsx +47 -4
  80. package/src/components/DrawerNavigationItem.tsx +19 -5
  81. package/src/components/EditFormActions.tsx +4 -3
  82. package/src/components/EditViewBinding.tsx +1 -1
  83. package/src/components/HomePage/ContentHomePage.tsx +4 -4
  84. package/src/components/RebaseAdmin.tsx +3 -3
  85. package/src/components/RebaseAuthGate.tsx +1 -1
  86. package/src/components/RebaseLayout.tsx +8 -2
  87. package/src/components/RebaseNavigation.tsx +9 -9
  88. package/src/components/RebaseRouteDefs.tsx +2 -2
  89. package/src/components/RebaseShell.tsx +10 -1
  90. package/src/components/RelationSelector.tsx +18 -3
  91. package/src/components/SelectableTable/filters/FilterFieldBinding.tsx +1 -1
  92. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +36 -9
  93. package/src/components/SelectableTable/filters/RelationFilterField.tsx +93 -17
  94. package/src/components/SelectableTable/filters/null_filter.ts +63 -0
  95. package/src/components/SidePanelProvider.tsx +2 -2
  96. package/src/components/app/Scaffold.tsx +16 -1
  97. package/src/data_export/export/ExportCollectionAction.tsx +2 -2
  98. package/src/form/EntityForm.tsx +2 -2
  99. package/src/form/EntityFormActions.tsx +2 -1
  100. package/src/form/EntityFormBinding.tsx +7 -7
  101. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -3
  102. package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +16 -5
  103. package/src/form/field_bindings/ReferenceFieldBinding.tsx +3 -3
  104. package/src/form/field_bindings/RelationFieldBinding.tsx +17 -6
  105. package/src/form/field_bindings/RepeatFieldBinding.tsx +2 -2
  106. package/src/hooks/index.ts +1 -1
  107. package/src/hooks/navigation/useNavigationResolution.ts +10 -4
  108. package/src/hooks/{useCMSContext.tsx → useAdminContext.tsx} +10 -10
  109. package/src/hooks/useHistory.ts +7 -11
  110. package/src/hooks/useResolvedNavigationFrom.tsx +2 -2
  111. package/src/index.ts +4 -4
  112. package/src/preview/PropertyPreview.tsx +6 -6
  113. package/src/preview/components/RelationPreview.tsx +1 -1
  114. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  115. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -2
  116. package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +2 -2
  117. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +2 -2
  118. package/src/types/components/EntityFormProps.tsx +2 -2
  119. package/src/types/fields.tsx +1 -1
  120. package/dist/CollectionEditorDialog-o7F_zbGY.js.map +0 -1
  121. package/dist/collection_editor/pgColumnToProperty.d.ts +0 -7
  122. package/dist/editor/extensions/Image/index.d.ts +0 -6
  123. package/dist/export-BvqhjdEb.js.map +0 -1
  124. package/dist/hooks/useCMSContext.d.ts +0 -29
  125. package/dist/util-BhhfHY1M.js.map +0 -1
  126. package/src/collection_editor/pgColumnToProperty.ts +0 -291
  127. package/src/editor/extensions/Image/index.ts +0 -133
package/README.md CHANGED
@@ -71,7 +71,7 @@ pnpm add @rebasepro/admin
71
71
  | `useSelectionController` | Row selection state for tables |
72
72
  | `useHistory` | Snapshot change history and version revert |
73
73
  | `useBreadcrumbsController` | Breadcrumb navigation state |
74
- | `useCMSContext` | Access the CMS-level context |
74
+ | `useAdminContext` | Access the admin-level context |
75
75
  | `useResolvedNavigationFrom` | Resolve navigation tree from collection configs |
76
76
 
77
77
  ### Data Import/Export
@@ -1,10 +1,10 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { $t as CollectionTableBinding, A as getFieldId, Ct as convertDataToEntity, Dt as useImportConfig, Et as getInferenceType, Ft as FieldCaption, In as useCollectionRegistryController, Nt as PropertyConfigBadge, Ot as ImportSaveInProgress, Pt as SearchIconsView, St as DataNewPropertiesMapping, Zt as useSelectionController, _ as useCollectionsConfigController, i as namespaceToPropertiesOrderPath, in as useUrlController, k as getFieldConfig, kt as ImportFileUpload, l as validateCollectionJson, m as toSerializableCollectionConfig, n as getFullIdPath, o as CollectionGenerationApiError, on as useNavigationStateController, r as idToPropertiesPath, t as getFullId, vt as useCollectionEditorController, xt as ImportNewPropertyFieldPreview } from "./util-BhhfHY1M.js";
3
- import { a as updatePropertyFromWidget, c as useAIModifiedPaths, i as supportedFields, l as useSafeSnackbarController, n as PropertyForm, o as PropertyTree, r as PropertyFormDialog, s as AIModifiedPathsProvider } from "./PropertyEditView-DgB2b22q.js";
2
+ import { $t as CollectionTableBinding, A as getFieldId, Ct as convertDataToEntity, Dt as useImportConfig, Et as getInferenceType, Ft as FieldCaption, In as useCollectionRegistryController, Nt as PropertyConfigBadge, Ot as ImportSaveInProgress, Pt as SearchIconsView, St as DataNewPropertiesMapping, Zt as useSelectionController, _ as useCollectionsConfigController, i as namespaceToPropertiesOrderPath, in as useUrlController, k as getFieldConfig, kt as ImportFileUpload, l as validateCollectionJson, m as toSerializableCollectionConfig, n as getFullIdPath, o as CollectionGenerationApiError, on as useNavigationStateController, r as idToPropertiesPath, t as getFullId, vt as useCollectionEditorController, xt as ImportNewPropertyFieldPreview } from "./util-BwZuV81o.js";
3
+ import { a as updatePropertyFromWidget, c as useAIModifiedPaths, i as supportedFields, l as useSafeSnackbarController, n as PropertyForm, o as PropertyTree, r as PropertyFormDialog, s as AIModifiedPathsProvider } from "./PropertyEditView-D2b9_nbC.js";
4
4
  import * as React$1 from "react";
5
5
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
6
  import { Alert, AppWindow, ArrowLeftIcon, BooleanSwitchWithLabel, Button, Card, CheckIcon, Chip, CircularProgress, CircularProgressCenter, CodeIcon, ColumnsIcon, Container, CopyIcon, DebouncedTextField, Dialog, DialogActions, DialogContent, DialogTitle, ErrorBoundary, FileIcon, FileSearchIcon, FileTextIcon, IconButton, KanbanIcon, KeyIcon, LayoutGridIcon, ListIcon, LoadingButton, Menu, MultiSelect, MultiSelectItem, PanelLeftIcon, Paper, PlusIcon, Select, SelectItem, ShoppingCartIcon, SquareIcon, Tab, TableIcon, Tabs, TextField, ToggleButtonGroup, Tooltip, Trash2Icon, Typography, UserIcon, XIcon, cls, coolIconKeys, defaultBorderMixin, iconSize } from "@rebasepro/ui";
7
- import { getEffectiveSecurityRules, getSubcollections, getTableName, isPropertyBuilder } from "@rebasepro/common";
7
+ import { buildCollectionFromTableMetadata, getEffectiveSecurityRules, getSubcollections, getTableName, isPropertyBuilder } from "@rebasepro/common";
8
8
  import { AIIcon, ConfirmationDialog, ErrorView, IconForView, UnsavedChangesDialog, removeInitialAndTrailingSlashes, useAuthController, useCustomizationController, useLargeLayout, useRebaseContext, useUnsavedChangesDialog } from "@rebasepro/app";
9
9
  import { Field, Formex, clone, getIn, useCreateFormex, useFormex } from "@rebasepro/forms";
10
10
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -696,7 +696,7 @@ function GetCodeDialog({ collection, onOpenChange, open }) {
696
696
  variant: "body2",
697
697
  className: "my-4 mb-8",
698
698
  children: [
699
- "If you want to customise the collection in code, you can add this collection code to your CMS app configuration. More info in the ",
699
+ "If you want to customise the collection in code, you can add this collection code to your admin app configuration. More info in the ",
700
700
  /* @__PURE__ */ jsx("a", {
701
701
  rel: "noopener noreferrer",
702
702
  href: "https://rebase.pro/docs/cloud/quickstart",
@@ -875,7 +875,8 @@ function CollectionPropertiesEditorForm({ showErrors, isNewCollection, propertyE
875
875
  return currentPropertiesOrderRef.current[namespace] ?? getIn(values, namespaceToPropertiesOrderPath(namespace));
876
876
  };
877
877
  const updatePropertiesOrder = (newPropertiesOrder, namespace) => {
878
- setFieldValue(namespaceToPropertiesOrderPath(namespace), newPropertiesOrder, false);
878
+ const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
879
+ setFieldValue(propertiesOrderPath, newPropertiesOrder, false);
879
880
  currentPropertiesOrderRef.current[namespace ?? ""] = newPropertiesOrder;
880
881
  };
881
882
  const deleteProperty = (propertyKey, namespace) => {
@@ -884,7 +885,10 @@ function CollectionPropertiesEditorForm({ showErrors, isNewCollection, propertyE
884
885
  if (!fullId) throw Error("collection editor miss config");
885
886
  setFieldValue(idToPropertiesPath(fullId), void 0, false);
886
887
  const currentPropertiesOrder = getCurrentPropertiesOrder(namespace);
887
- if (currentPropertiesOrder) updatePropertiesOrder(currentPropertiesOrder.filter((p) => p !== propertyKey), namespace);
888
+ if (currentPropertiesOrder) {
889
+ const newPropertiesOrder = currentPropertiesOrder.filter((p) => p !== propertyKey);
890
+ updatePropertiesOrder(newPropertiesOrder, namespace);
891
+ }
888
892
  setNewPropertyDialogOpen(false);
889
893
  setSelectedPropertyIndex(void 0);
890
894
  setSelectedPropertyKey(void 0);
@@ -1177,7 +1181,8 @@ function CollectionJsonImportDialog({ open, onClose, onImport }) {
1177
1181
  setErrors([]);
1178
1182
  return;
1179
1183
  }
1180
- setErrors(validateCollectionJson(value).errors);
1184
+ const result = validateCollectionJson(value);
1185
+ setErrors(result.errors);
1181
1186
  }, []);
1182
1187
  const handleImport = useCallback(() => {
1183
1188
  const result = validateCollectionJson(jsonValue);
@@ -2077,7 +2082,8 @@ function CollectionEditorImportMapping({ importConfig, propertyConfigs }) {
2077
2082
  return currentPropertiesOrderRef.current[namespace] ?? getIn(values, namespaceToPropertiesOrderPath(namespace));
2078
2083
  };
2079
2084
  const updatePropertiesOrder = (newPropertiesOrder, namespace) => {
2080
- setFieldValue(namespaceToPropertiesOrderPath(namespace), newPropertiesOrder, false);
2085
+ const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
2086
+ setFieldValue(propertiesOrderPath, newPropertiesOrder, false);
2081
2087
  currentPropertiesOrderRef.current[namespace ?? ""] = newPropertiesOrder;
2082
2088
  };
2083
2089
  if (previousId && previousId !== id) {
@@ -2329,6 +2335,53 @@ function sanitizeSqlIdentifier(name) {
2329
2335
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) throw new Error(`Invalid SQL identifier: "${name}". Only letters, digits, and underscores are allowed.`);
2330
2336
  return `"${name}"`;
2331
2337
  }
2338
+ /**
2339
+ * `pg_policies` reports the operation and the permissive flag in upper case;
2340
+ * `SecurityRule` names them in lower case. These two used to be `.toLowerCase()`
2341
+ * assigned straight into a `SecurityRule`, which typechecks only against a
2342
+ * `string` field — and this file declared its own local `SecurityRule` with
2343
+ * `operation?: string`, so it did. Against the real type it does not: an
2344
+ * unrecognised `cmd` would have been written into the collection as a policy
2345
+ * operation that compiles to nothing.
2346
+ */
2347
+ function toSecurityOperation(cmd) {
2348
+ switch (cmd) {
2349
+ case "SELECT": return "select";
2350
+ case "INSERT": return "insert";
2351
+ case "UPDATE": return "update";
2352
+ case "DELETE": return "delete";
2353
+ case "ALL": return "all";
2354
+ default: return;
2355
+ }
2356
+ }
2357
+ function toSecurityMode(permissive) {
2358
+ if (!permissive) return void 0;
2359
+ return permissive === "RESTRICTIVE" ? "restrictive" : "permissive";
2360
+ }
2361
+ /**
2362
+ * Build a {@link SecurityRule} from a `pg_policies` row.
2363
+ *
2364
+ * `SecurityRule` is a discriminated union and this is the part the local shadow
2365
+ * type flattened away: a raw-SQL rule *requires* `using`, so a policy with only
2366
+ * a `WITH CHECK` clause — every INSERT-only policy — is not one. It is a
2367
+ * roles-only rule carrying the check. Assembling the object field-by-field
2368
+ * against `operation?: string` accepted all of these and told the compiler
2369
+ * nothing.
2370
+ */
2371
+ function toSecurityRule(policy) {
2372
+ const base = {
2373
+ name: policy.policyname ?? "",
2374
+ operation: toSecurityOperation(policy.cmd),
2375
+ mode: toSecurityMode(policy.permissive),
2376
+ roles: policy.roles ? [...policy.roles] : void 0
2377
+ };
2378
+ if (policy.qual) return {
2379
+ ...base,
2380
+ using: policy.qual,
2381
+ withCheck: policy.with_check || void 0
2382
+ };
2383
+ return base;
2384
+ }
2332
2385
  function CollectionRLSTab() {
2333
2386
  const { values, setFieldValue } = useFormex();
2334
2387
  const [editingPolicy, setEditingPolicy] = useState(null);
@@ -2353,7 +2406,7 @@ function CollectionRLSTab() {
2353
2406
  if (Array.isArray(res)) return res;
2354
2407
  return [];
2355
2408
  };
2356
- setDbPolicies(extractRows(result).map((p) => {
2409
+ const policies = extractRows(result).map((p) => {
2357
2410
  let parsedRoles = [];
2358
2411
  const r = p.roles || p.ROLES;
2359
2412
  if (Array.isArray(r)) parsedRoles = r;
@@ -2367,7 +2420,8 @@ function CollectionRLSTab() {
2367
2420
  qual: p.qual || p.QUAL || null,
2368
2421
  with_check: p.with_check || p.WITH_CHECK || null
2369
2422
  };
2370
- }));
2423
+ });
2424
+ setDbPolicies(policies);
2371
2425
  } catch (e) {
2372
2426
  console.error("Failed to fetch DB policies", e);
2373
2427
  } finally {
@@ -2437,14 +2491,7 @@ function CollectionRLSTab() {
2437
2491
  ]);
2438
2492
  const unmappedPolicies = dbPolicies.filter((dp) => !generatedPolicyNames.has(dp.policyname));
2439
2493
  const handleSave = async (newPolicy) => {
2440
- const rule = {
2441
- name: newPolicy.policyname ?? "",
2442
- operation: newPolicy.cmd?.toLowerCase(),
2443
- mode: newPolicy.permissive?.toLowerCase(),
2444
- using: newPolicy.qual || void 0,
2445
- withCheck: newPolicy.with_check || void 0,
2446
- roles: newPolicy.roles
2447
- };
2494
+ const rule = toSecurityRule(newPolicy);
2448
2495
  let newRules;
2449
2496
  if (editingPolicy === "new") newRules = [...rules, rule];
2450
2497
  else newRules = rules.map((r) => r.name === editingPolicy.policyname ? rule : r);
@@ -2511,11 +2558,11 @@ function CollectionRLSTab() {
2511
2558
  size: "small",
2512
2559
  variant: "text",
2513
2560
  onClick: () => setEditingPolicy({
2514
- policyname: rule.name,
2561
+ policyname: rule.name ?? "",
2515
2562
  tablename: values.id || values.table || values.alias || "your_table",
2516
2563
  permissive: (rule.mode || "permissive").toUpperCase(),
2517
2564
  cmd: (rule.operation || "ALL").toUpperCase(),
2518
- roles: rule.roles || [],
2565
+ roles: [...rule.roles ?? []],
2519
2566
  qual: rule.using || null,
2520
2567
  with_check: rule.withCheck || null
2521
2568
  }),
@@ -2595,14 +2642,7 @@ function CollectionRLSTab() {
2595
2642
  variant: "outlined",
2596
2643
  color: "primary",
2597
2644
  onClick: () => {
2598
- const rule = {
2599
- name: dp.policyname,
2600
- operation: dp.cmd?.toLowerCase(),
2601
- mode: dp.permissive?.toLowerCase(),
2602
- using: dp.qual || void 0,
2603
- withCheck: dp.with_check || void 0,
2604
- roles: dp.roles
2605
- };
2645
+ const rule = toSecurityRule(dp);
2606
2646
  setFieldValue("securityRules", [...rules, rule]);
2607
2647
  },
2608
2648
  children: "Import to codebase"
@@ -2651,7 +2691,7 @@ function InlinePolicyEditor({ policy, table, availableRoles, rolesUnavailable, o
2651
2691
  * from what users hold, so a role that is referenced here but assigned to
2652
2692
  * nobody would otherwise vanish from its own policy on the next save.
2653
2693
  */
2654
- const roleOptions = useMemo(() => Array.from(new Set([...availableRoles, ...roles])).sort(), [availableRoles, roles]);
2694
+ const roleOptions = useMemo(() => Array.from(/* @__PURE__ */ new Set([...availableRoles, ...roles])).sort(), [availableRoles, roles]);
2655
2695
  const addCustomRole = () => {
2656
2696
  const trimmed = customRole.trim();
2657
2697
  if (!trimmed || roles.includes(trimmed)) {
@@ -2843,243 +2883,6 @@ function InlinePolicyEditor({ policy, table, availableRoles, rolesUnavailable, o
2843
2883
  ] });
2844
2884
  }
2845
2885
  //#endregion
2846
- //#region src/collection_editor/pgColumnToProperty.ts
2847
- /**
2848
- * Maps a PostgreSQL column data type to a Rebase property type.
2849
- */
2850
- function pgTypeToRebaseProperty(column) {
2851
- const { column_name, data_type, udt_name, is_nullable, column_default, enum_values } = column;
2852
- const required = is_nullable === "NO";
2853
- const prettifiedName = prettifyIdentifier(column_name);
2854
- const isAutoId = column_default != null && (column_default.includes("nextval") || column_default.includes("gen_random_uuid") || column_default.includes("uuid_generate") || column_default.includes("identity"));
2855
- if (data_type === "USER-DEFINED" && enum_values && enum_values.length > 0) return {
2856
- type: "string",
2857
- name: prettifiedName,
2858
- enum: enum_values.map((v) => ({
2859
- id: v,
2860
- label: prettifyIdentifier(v)
2861
- })),
2862
- validation: required ? { required: true } : void 0
2863
- };
2864
- const dt = data_type.toLowerCase();
2865
- switch (dt) {
2866
- case "character varying":
2867
- case "varchar":
2868
- case "text":
2869
- case "char":
2870
- case "character":
2871
- case "citext": {
2872
- let colType = "varchar";
2873
- if (dt === "text" || dt === "citext") colType = "text";
2874
- if (dt === "char" || dt === "character") colType = "char";
2875
- const prop = {
2876
- type: "string",
2877
- name: prettifiedName,
2878
- columnType: colType,
2879
- validation: required ? { required: true } : void 0
2880
- };
2881
- if (isAutoId) prop.isId = "manual";
2882
- return prop;
2883
- }
2884
- case "uuid": {
2885
- const prop = {
2886
- type: "string",
2887
- name: prettifiedName,
2888
- validation: required ? { required: true } : void 0
2889
- };
2890
- if (isAutoId) prop.isId = "uuid";
2891
- return prop;
2892
- }
2893
- case "integer":
2894
- case "bigint":
2895
- case "smallint": {
2896
- const prop = {
2897
- type: "number",
2898
- name: prettifiedName,
2899
- columnType: dt === "bigint" ? "bigint" : "integer",
2900
- validation: {
2901
- ...required ? { required: true } : {},
2902
- integer: true
2903
- }
2904
- };
2905
- if (isAutoId) prop.isId = "increment";
2906
- return prop;
2907
- }
2908
- case "serial":
2909
- case "bigserial":
2910
- case "smallserial": return {
2911
- type: "number",
2912
- name: prettifiedName,
2913
- columnType: dt === "bigserial" ? "bigserial" : "serial",
2914
- isId: "increment",
2915
- validation: {
2916
- ...required ? { required: true } : {},
2917
- integer: true
2918
- }
2919
- };
2920
- case "numeric":
2921
- case "decimal":
2922
- case "real":
2923
- case "double precision": {
2924
- let colType = "numeric";
2925
- if (dt === "real") colType = "real";
2926
- if (dt === "double precision") colType = "double precision";
2927
- return {
2928
- type: "number",
2929
- name: prettifiedName,
2930
- columnType: colType,
2931
- validation: required ? { required: true } : void 0
2932
- };
2933
- }
2934
- case "boolean": return {
2935
- type: "boolean",
2936
- name: prettifiedName,
2937
- validation: required ? { required: true } : void 0
2938
- };
2939
- case "timestamp with time zone":
2940
- case "timestamp without time zone":
2941
- case "timestamp":
2942
- case "timestamptz":
2943
- case "date":
2944
- case "time with time zone":
2945
- case "time without time zone":
2946
- case "time": {
2947
- let colType = "timestamp";
2948
- if (dt.startsWith("date")) colType = "date";
2949
- if (dt.startsWith("time ") || dt === "time") colType = "time";
2950
- return {
2951
- type: "date",
2952
- name: prettifiedName,
2953
- columnType: colType,
2954
- validation: required ? { required: true } : void 0
2955
- };
2956
- }
2957
- case "jsonb":
2958
- case "json": return {
2959
- type: "map",
2960
- name: prettifiedName,
2961
- columnType: dt === "jsonb" ? "jsonb" : "json",
2962
- keyValue: true,
2963
- properties: {}
2964
- };
2965
- case "array":
2966
- case "ARRAY": {
2967
- let innerType = "string";
2968
- let colType = void 0;
2969
- if (udt_name === "_text" || udt_name === "_varchar") {
2970
- innerType = "string";
2971
- colType = "text[]";
2972
- } else if (udt_name === "_int4" || udt_name === "_int2" || udt_name === "_int8") {
2973
- innerType = "number";
2974
- colType = "integer[]";
2975
- } else if (udt_name === "_bool") {
2976
- innerType = "boolean";
2977
- colType = "boolean[]";
2978
- } else if (udt_name === "_numeric") {
2979
- innerType = "number";
2980
- colType = "numeric[]";
2981
- }
2982
- return {
2983
- type: "array",
2984
- name: prettifiedName,
2985
- columnType: colType,
2986
- of: { type: innerType }
2987
- };
2988
- }
2989
- default: return {
2990
- type: "string",
2991
- name: prettifiedName,
2992
- validation: required ? { required: true } : void 0
2993
- };
2994
- }
2995
- }
2996
- /**
2997
- * Builds a partial AdminCollection from PostgreSQL table metadata.
2998
- * This is used when creating a new collection from an existing database table.
2999
- */
3000
- function buildCollectionFromTableMetadata(tableName, metadata) {
3001
- const properties = {};
3002
- const propertiesOrder = [];
3003
- const relations = [];
3004
- const securityRules = [];
3005
- for (const column of metadata.columns) {
3006
- const property = pgTypeToRebaseProperty(column);
3007
- if (property) {
3008
- const propRecord = property;
3009
- Object.keys(propRecord).forEach((key) => propRecord[key] === void 0 && delete propRecord[key]);
3010
- properties[column.column_name] = property;
3011
- propertiesOrder.push(column.column_name);
3012
- }
3013
- }
3014
- if (metadata.foreignKeys) for (const fk of metadata.foreignKeys) {
3015
- const relName = fk.column_name.endsWith("_id") ? fk.column_name.substring(0, fk.column_name.length - 3) : fk.column_name;
3016
- relations.push({
3017
- id: fk.column_name,
3018
- relationName: relName,
3019
- target: fk.foreign_table_name,
3020
- cardinality: "one",
3021
- direction: "owning",
3022
- localKey: fk.column_name
3023
- });
3024
- }
3025
- if (metadata.junctions) for (const junction of metadata.junctions) {
3026
- const relName = junction.target_table_name;
3027
- relations.push({
3028
- id: junction.target_table_name + "_relation",
3029
- relationName: relName,
3030
- target: junction.target_table_name,
3031
- cardinality: "many",
3032
- direction: "owning",
3033
- through: {
3034
- table: junction.junction_table_name,
3035
- sourceColumn: junction.source_column_name,
3036
- targetColumn: junction.target_column_name
3037
- }
3038
- });
3039
- }
3040
- if (metadata.policies) for (const policy of metadata.policies) {
3041
- let operations = [];
3042
- switch (policy.cmd) {
3043
- case "ALL":
3044
- operations = [
3045
- "read",
3046
- "create",
3047
- "update",
3048
- "delete"
3049
- ];
3050
- break;
3051
- case "SELECT":
3052
- operations = ["read"];
3053
- break;
3054
- case "INSERT":
3055
- operations = ["create"];
3056
- break;
3057
- case "UPDATE":
3058
- operations = ["update"];
3059
- break;
3060
- case "DELETE":
3061
- operations = ["delete"];
3062
- break;
3063
- }
3064
- securityRules.push({
3065
- name: policy.policy_name,
3066
- operations,
3067
- roles: policy.roles ?? [],
3068
- qual: policy.qual,
3069
- with_check: policy.with_check
3070
- });
3071
- }
3072
- return {
3073
- name: prettifyIdentifier(tableName),
3074
- slug: tableName,
3075
- table: tableName,
3076
- properties,
3077
- propertiesOrder,
3078
- ...relations.length > 0 ? { relations } : {},
3079
- ...securityRules.length > 0 ? { securityRules } : {}
3080
- };
3081
- }
3082
- //#endregion
3083
2886
  //#region src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx
3084
2887
  var CollectionEditorDialog_exports = /* @__PURE__ */ __exportAll({
3085
2888
  CollectionEditor: () => CollectionEditor,
@@ -3575,10 +3378,11 @@ function CollectionEditorInternal({ isNewCollection, configController, editedCol
3575
3378
  showErrors: submitCount > 0,
3576
3379
  isNewCollection,
3577
3380
  onPropertyError: (propertyKey, namespace, error) => {
3578
- propertyErrorsRef.current = removeUndefined({
3381
+ const current = removeUndefined({
3579
3382
  ...propertyErrorsRef.current,
3580
3383
  [getFullIdPath(propertyKey, namespace)]: removeUndefined(error, true)
3581
3384
  }, true);
3385
+ propertyErrorsRef.current = current;
3582
3386
  formController.validate();
3583
3387
  },
3584
3388
  getUser,
@@ -3741,4 +3545,4 @@ var validateId = (value, isNewCollection, existingPaths, existingIds) => {
3741
3545
  //#endregion
3742
3546
  export { CollectionEditorDialog as n, CollectionEditorDialog_exports as r, CollectionEditor as t };
3743
3547
 
3744
- //# sourceMappingURL=CollectionEditorDialog-o7F_zbGY.js.map
3548
+ //# sourceMappingURL=CollectionEditorDialog-B8j6HArJ.js.map