@rebasepro/studio 0.8.0 → 0.9.1-canary.09aaf62
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/README.md +4 -4
- package/dist/{ApiExplorer-CdIwR9Ga.js → ApiExplorer-B9M9-uy_.js} +3 -3
- package/dist/ApiExplorer-B9M9-uy_.js.map +1 -0
- package/dist/{ApiKeysView-D-_FSlNL.js → ApiKeysView-vQKqt2wl.js} +45 -3
- package/dist/ApiKeysView-vQKqt2wl.js.map +1 -0
- package/dist/{AuthSimulationSelector-iEZ-Or_1.js → AuthSimulationSelector-Bougd521.js} +2 -2
- package/dist/AuthSimulationSelector-Bougd521.js.map +1 -0
- package/dist/BackupsView-DNS6LdVg.js +227 -0
- package/dist/BackupsView-DNS6LdVg.js.map +1 -0
- package/dist/{BranchesView-Dlg78EQ8.js → BranchesView-DH0qnU9J.js} +2 -2
- package/dist/BranchesView-DH0qnU9J.js.map +1 -0
- package/dist/{CronJobsView-4gdtJvoe.js → CronJobsView-B59i3Mis.js} +2 -2
- package/dist/CronJobsView-B59i3Mis.js.map +1 -0
- package/dist/{JSEditor-Ca4XYGRp.js → JSEditor-BeHHuxhA.js} +28 -32
- package/dist/JSEditor-BeHHuxhA.js.map +1 -0
- package/dist/LogsExplorer-BCXoGREr.js +298 -0
- package/dist/LogsExplorer-BCXoGREr.js.map +1 -0
- package/dist/{MonacoEditor-COZqrIJ1.js → MonacoEditor-D0_QljcX.js} +2 -2
- package/dist/MonacoEditor-D0_QljcX.js.map +1 -0
- package/dist/{RLSEditor-BM64laoW.js → RLSEditor-D6adwB6e.js} +38 -34
- package/dist/RLSEditor-D6adwB6e.js.map +1 -0
- package/dist/{SQLEditor-CuAhR-zr.js → SQLEditor-_dCqVAN_.js} +8 -8
- package/dist/SQLEditor-_dCqVAN_.js.map +1 -0
- package/dist/{SchemaVisualizer-OibKoD3g.js → SchemaVisualizer-C_ErehfH.js} +11 -11
- package/dist/SchemaVisualizer-C_ErehfH.js.map +1 -0
- package/dist/{StorageView-BMhD29YO.js → StorageView-UvyM2xdW.js} +2 -2
- package/dist/StorageView-UvyM2xdW.js.map +1 -0
- package/dist/components/AuthSimulationSelector.d.ts +1 -1
- package/dist/components/Backups/BackupsView.d.ts +2 -0
- package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.es.js +74 -111
- package/dist/index.es.js.map +1 -1
- package/dist/utils/pgColumnToProperty.d.ts +3 -3
- package/dist/utils/sql_utils.d.ts +3 -3
- package/package.json +11 -12
- package/src/components/ApiExplorer/ApiExplorer.tsx +1 -1
- package/src/components/ApiExplorer/TryItPanel.tsx +1 -1
- package/src/components/ApiKeys/ApiKeysView.tsx +43 -3
- package/src/components/AuthSimulationSelector.tsx +1 -1
- package/src/components/Backups/BackupsView.tsx +185 -0
- package/src/components/Branches/BranchesView.tsx +1 -1
- package/src/components/CronJobs/CronJobsView.tsx +1 -1
- package/src/components/JSEditor/JSEditor.tsx +12 -12
- package/src/components/JSEditor/JSMonacoEditor.tsx +21 -25
- package/src/components/LogsExplorer/LogsExplorer.tsx +168 -46
- package/src/components/RLSEditor/PolicyEditor.tsx +5 -3
- package/src/components/RLSEditor/RLSEditor.tsx +51 -34
- package/src/components/RebaseStudio.tsx +12 -3
- package/src/components/SQLEditor/MonacoEditor.tsx +1 -1
- package/src/components/SQLEditor/SQLEditor.tsx +6 -6
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +1 -1
- package/src/components/SQLEditor/SchemaBrowser.tsx +3 -3
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +10 -9
- package/src/components/SchemaVisualizer/TableNode.tsx +1 -1
- package/src/components/SchemaVisualizer/useSchemaGraph.ts +11 -11
- package/src/components/StorageView/StorageView.tsx +1 -1
- package/src/components/StudioHomePage.tsx +62 -87
- package/src/index.ts +4 -4
- package/src/utils/pgColumnToProperty.ts +3 -3
- package/src/utils/sql_utils.test.ts +6 -6
- package/src/utils/sql_utils.ts +3 -3
- package/dist/ApiExplorer-CdIwR9Ga.js.map +0 -1
- package/dist/ApiKeysView-D-_FSlNL.js.map +0 -1
- package/dist/AuthSimulationSelector-iEZ-Or_1.js.map +0 -1
- package/dist/BranchesView-Dlg78EQ8.js.map +0 -1
- package/dist/CronJobsView-4gdtJvoe.js.map +0 -1
- package/dist/JSEditor-Ca4XYGRp.js.map +0 -1
- package/dist/LogsExplorer-J4xfsuv3.js +0 -206
- package/dist/LogsExplorer-J4xfsuv3.js.map +0 -1
- package/dist/MonacoEditor-COZqrIJ1.js.map +0 -1
- package/dist/RLSEditor-BM64laoW.js.map +0 -1
- package/dist/SQLEditor-CuAhR-zr.js.map +0 -1
- package/dist/SchemaVisualizer-OibKoD3g.js.map +0 -1
- package/dist/StorageView-BMhD29YO.js.map +0 -1
- package/dist/index.umd.js +0 -11491
- package/dist/index.umd.js.map +0 -1
- package/src/vite-env.d.ts +0 -1
|
@@ -28,9 +28,9 @@ import {
|
|
|
28
28
|
import {
|
|
29
29
|
useStudioCollectionRegistry,
|
|
30
30
|
IconForView
|
|
31
|
-
} from "@rebasepro/
|
|
32
|
-
import type {
|
|
33
|
-
import {
|
|
31
|
+
} from "@rebasepro/app";
|
|
32
|
+
import type { CollectionConfig } from "@rebasepro/types";
|
|
33
|
+
import { isPostgresCollectionConfig } from "@rebasepro/types";
|
|
34
34
|
import { useSchemaGraph } from "./useSchemaGraph";
|
|
35
35
|
import type { TableNodeData } from "./useSchemaGraph";
|
|
36
36
|
import { TableNode } from "./TableNode";
|
|
@@ -52,7 +52,7 @@ const edgeTypes = {
|
|
|
52
52
|
function SchemaVisualizerCanvas({
|
|
53
53
|
collections
|
|
54
54
|
}: {
|
|
55
|
-
collections:
|
|
55
|
+
collections: CollectionConfig[];
|
|
56
56
|
}) {
|
|
57
57
|
const reactFlowInstance = useReactFlow();
|
|
58
58
|
const {
|
|
@@ -152,7 +152,7 @@ duration: 400 }
|
|
|
152
152
|
|
|
153
153
|
// Group collections for sidebar
|
|
154
154
|
const postgresCollections = useMemo(
|
|
155
|
-
() => collections.filter(
|
|
155
|
+
() => collections.filter(isPostgresCollectionConfig),
|
|
156
156
|
[collections]
|
|
157
157
|
);
|
|
158
158
|
|
|
@@ -184,7 +184,7 @@ duration: 400 }
|
|
|
184
184
|
junctions: junctionNodes.length,
|
|
185
185
|
withRls: postgresCollections.filter(
|
|
186
186
|
(c) =>
|
|
187
|
-
|
|
187
|
+
isPostgresCollectionConfig(c) &&
|
|
188
188
|
c.securityRules &&
|
|
189
189
|
c.securityRules.length > 0
|
|
190
190
|
).length
|
|
@@ -293,7 +293,7 @@ duration: 400 }
|
|
|
293
293
|
)}
|
|
294
294
|
</div>
|
|
295
295
|
<div className="flex items-center gap-1 shrink-0 ml-1">
|
|
296
|
-
{
|
|
296
|
+
{isPostgresCollectionConfig(collection) &&
|
|
297
297
|
collection.securityRules &&
|
|
298
298
|
collection.securityRules
|
|
299
299
|
.length > 0 && (
|
|
@@ -543,7 +543,8 @@ duration: 400 }
|
|
|
543
543
|
/>
|
|
544
544
|
<Controls
|
|
545
545
|
showInteractive={false}
|
|
546
|
-
|
|
546
|
+
// mb clears the legend overlay pinned at bottom-left
|
|
547
|
+
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)]"
|
|
547
548
|
/>
|
|
548
549
|
<MiniMap
|
|
549
550
|
nodeStrokeColor={(n) => {
|
|
@@ -629,7 +630,7 @@ duration: 400 }
|
|
|
629
630
|
export const SchemaVisualizer = () => {
|
|
630
631
|
const { collections: registryCollections } =
|
|
631
632
|
useStudioCollectionRegistry() as {
|
|
632
|
-
collections?:
|
|
633
|
+
collections?: CollectionConfig[];
|
|
633
634
|
};
|
|
634
635
|
|
|
635
636
|
// Merge registry collections with any passed collections
|
|
@@ -2,7 +2,7 @@ import React, { memo, useMemo } from "react";
|
|
|
2
2
|
import { Handle, Position } from "@xyflow/react";
|
|
3
3
|
import type { NodeProps } from "@xyflow/react";
|
|
4
4
|
import { Typography, Chip, Tooltip, cls } from "@rebasepro/ui";
|
|
5
|
-
import { IconForView } from "@rebasepro/
|
|
5
|
+
import { IconForView } from "@rebasepro/app";
|
|
6
6
|
import type { TableNodeData, ColumnInfo } from "./useSchemaGraph";
|
|
7
7
|
import { getColumnRowY, getHeaderHeight } from "./schema-visualizer.utils";
|
|
8
8
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useMemo, useState, useCallback, useEffect } from "react";
|
|
2
2
|
import type { Node, Edge } from "@xyflow/react";
|
|
3
3
|
import { MarkerType } from "@xyflow/react";
|
|
4
|
-
import {
|
|
5
|
-
import type {
|
|
4
|
+
import { isPostgresCollectionConfig } from "@rebasepro/types";
|
|
5
|
+
import type { CollectionConfig, PostgresCollectionConfig, Relation } from "@rebasepro/types";
|
|
6
6
|
import { resolveCollectionRelations } from "@rebasepro/common";
|
|
7
7
|
import { getLayoutedElements, getCardinalityLabel, getTypeLabel, NODE_WIDTH } from "./schema-visualizer.utils";
|
|
8
8
|
import type { LayoutDirection, RelationEdgeData } from "./schema-visualizer.utils";
|
|
@@ -36,9 +36,9 @@ export interface TableNodeData {
|
|
|
36
36
|
[key: string]: unknown;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// ─── Extract columns from
|
|
39
|
+
// ─── Extract columns from a CollectionConfig ─────────────────────────
|
|
40
40
|
|
|
41
|
-
const extractColumns = (collection:
|
|
41
|
+
const extractColumns = (collection: CollectionConfig): ColumnInfo[] => {
|
|
42
42
|
const columns: ColumnInfo[] = [];
|
|
43
43
|
const properties = collection.properties ?? {};
|
|
44
44
|
|
|
@@ -79,7 +79,7 @@ const extractColumns = (collection: EntityCollection): ColumnInfo[] => {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// Add FK columns from owning relations
|
|
82
|
-
if (
|
|
82
|
+
if (isPostgresCollectionConfig(collection)) {
|
|
83
83
|
try {
|
|
84
84
|
const resolvedRelations = resolveCollectionRelations(collection);
|
|
85
85
|
for (const rel of Object.values(resolvedRelations)) {
|
|
@@ -117,7 +117,7 @@ const extractColumns = (collection: EntityCollection): ColumnInfo[] => {
|
|
|
117
117
|
// ─── Build graph from collections ─────────────────────────────────────
|
|
118
118
|
|
|
119
119
|
const buildGraph = (
|
|
120
|
-
collections:
|
|
120
|
+
collections: CollectionConfig[],
|
|
121
121
|
direction: LayoutDirection
|
|
122
122
|
): { nodes: Node[]; edges: Edge[] } => {
|
|
123
123
|
const nodes: Node[] = [];
|
|
@@ -143,7 +143,7 @@ const buildGraph = (
|
|
|
143
143
|
|
|
144
144
|
// 1. Create nodes for each collection
|
|
145
145
|
for (const collection of collections) {
|
|
146
|
-
if (!
|
|
146
|
+
if (!isPostgresCollectionConfig(collection)) continue;
|
|
147
147
|
|
|
148
148
|
const tableName = collection.table ?? collection.slug;
|
|
149
149
|
const nodeId = `table-${tableName}`;
|
|
@@ -177,7 +177,7 @@ y: 0 },
|
|
|
177
177
|
const processedJunctions = new Set<string>();
|
|
178
178
|
|
|
179
179
|
for (const collection of collections) {
|
|
180
|
-
if (!
|
|
180
|
+
if (!isPostgresCollectionConfig(collection)) continue;
|
|
181
181
|
|
|
182
182
|
const tableName = collection.table ?? collection.slug;
|
|
183
183
|
const sourceNodeId = tableToNodeId.get(tableName);
|
|
@@ -191,14 +191,14 @@ y: 0 },
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
for (const [relationKey, rel] of Object.entries(resolvedRelations)) {
|
|
194
|
-
let targetCollection:
|
|
194
|
+
let targetCollection: CollectionConfig;
|
|
195
195
|
try {
|
|
196
196
|
targetCollection = rel.target();
|
|
197
197
|
} catch {
|
|
198
198
|
continue;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
if (!
|
|
201
|
+
if (!isPostgresCollectionConfig(targetCollection)) continue;
|
|
202
202
|
|
|
203
203
|
const targetTable = targetCollection.table ?? targetCollection.slug;
|
|
204
204
|
const targetNodeId = tableToNodeId.get(targetTable);
|
|
@@ -356,7 +356,7 @@ export interface UseSchemaGraphResult {
|
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
export const useSchemaGraph = (
|
|
359
|
-
collections:
|
|
359
|
+
collections: CollectionConfig[] | undefined
|
|
360
360
|
): UseSchemaGraphResult => {
|
|
361
361
|
const [direction, setDirection] = useState<LayoutDirection>("LR");
|
|
362
362
|
const [version, setVersion] = useState(0);
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
VideoIcon,
|
|
42
42
|
XIcon
|
|
43
43
|
} from "@rebasepro/ui";
|
|
44
|
-
import { useStorageSource, useStorageSources, useSnackbarController, ErrorView, useApiConfig } from "@rebasepro/
|
|
44
|
+
import { useStorageSource, useStorageSources, useSnackbarController, ErrorView, useApiConfig } from "@rebasepro/app";
|
|
45
45
|
import { DEFAULT_STORAGE_SOURCE_KEY, type StorageListResult } from "@rebasepro/types";
|
|
46
46
|
import { useSearchParams } from "react-router-dom";
|
|
47
47
|
import { useDropzone } from "react-dropzone";
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import type { HomePageSection, PluginGenericProps } from "@rebasepro/types";
|
|
1
|
+
import type { AppView, HomePageSection, PluginGenericProps } from "@rebasepro/types";
|
|
2
2
|
import React, { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { Card, cls, Container, ExpandablePanel, Typography } from "@rebasepro/ui";
|
|
4
|
-
import { IconForView, useRebaseContext, useRestoreScroll, useSlot } from "@rebasepro/
|
|
4
|
+
import { IconForView, useRebaseContext, useRebaseRegistry, useRestoreScroll, useSlot } from "@rebasepro/app";
|
|
5
5
|
import { useNavigate } from "react-router-dom";
|
|
6
|
-
import { useStudioBreadcrumbs, SchemaDriftBanner } from "@rebasepro/
|
|
6
|
+
import { useStudioBreadcrumbs, SchemaDriftBanner } from "@rebasepro/app";
|
|
7
7
|
|
|
8
8
|
/* ═══════════════════════════════════════════════════════════════
|
|
9
|
-
|
|
9
|
+
Studio tool sections, derived from the registered Studio views
|
|
10
10
|
═══════════════════════════════════════════════════════════════ */
|
|
11
11
|
|
|
12
12
|
interface StudioTool {
|
|
13
13
|
path: string;
|
|
14
14
|
name: string;
|
|
15
15
|
description: string;
|
|
16
|
-
icon
|
|
16
|
+
icon?: string | React.ReactNode;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface StudioSection {
|
|
@@ -21,85 +21,44 @@ interface StudioSection {
|
|
|
21
21
|
tools: StudioTool[];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
]
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
label: "Compute",
|
|
56
|
-
tools: [
|
|
57
|
-
{ path: "/js",
|
|
58
|
-
name: "JS Console",
|
|
59
|
-
description: "Run JavaScript with the Rebase SDK in a live sandbox",
|
|
60
|
-
icon: "code" },
|
|
61
|
-
{ path: "/cron",
|
|
62
|
-
name: "Cron Jobs",
|
|
63
|
-
description: "Monitor and manage scheduled background tasks",
|
|
64
|
-
icon: "Clock" }
|
|
65
|
-
]
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
label: "API",
|
|
69
|
-
tools: [
|
|
70
|
-
{ path: "/api",
|
|
71
|
-
name: "API Explorer",
|
|
72
|
-
description: "Interactive API documentation with live request testing",
|
|
73
|
-
icon: "BookOpen" }
|
|
74
|
-
]
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
label: "Storage",
|
|
78
|
-
tools: [
|
|
79
|
-
{ path: "/storage",
|
|
80
|
-
name: "Storage",
|
|
81
|
-
description: "Browse, upload, and manage files in your storage bucket",
|
|
82
|
-
icon: "HardDrive" }
|
|
83
|
-
]
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
label: "Access Control",
|
|
87
|
-
tools: [
|
|
88
|
-
{ path: "/users",
|
|
89
|
-
name: "Users",
|
|
90
|
-
description: "Manage developers and assign roles in your workspace",
|
|
91
|
-
icon: "group" },
|
|
92
|
-
{ path: "/roles",
|
|
93
|
-
name: "Roles",
|
|
94
|
-
description: "Create and configure fine-grained access permissions",
|
|
95
|
-
icon: "ShieldEllipsis" },
|
|
96
|
-
{ path: "/api-keys",
|
|
97
|
-
name: "API Keys",
|
|
98
|
-
description: "Create and manage scoped keys for machine-to-machine access",
|
|
99
|
-
icon: "KeyRound" }
|
|
100
|
-
]
|
|
24
|
+
/** The view metadata the cards need — not the rendered `view` itself. */
|
|
25
|
+
type StudioViewMeta = Pick<AppView, "slug" | "name" | "group" | "description" | "icon" | "hideFromNavigation">;
|
|
26
|
+
|
|
27
|
+
/** Group order for the home page; unknown groups are appended in view order. */
|
|
28
|
+
const GROUP_ORDER = ["Database", "Compute", "Storage", "API", "Access Control"];
|
|
29
|
+
|
|
30
|
+
const UNGROUPED_LABEL = "Tools";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Build the home page sections from the Studio views that are actually
|
|
34
|
+
* registered, so a card exists if and only if its route does. A hand-written
|
|
35
|
+
* list drifts: it used to advertise Users and Roles pages that 404, while
|
|
36
|
+
* omitting the Backups view that does exist.
|
|
37
|
+
*/
|
|
38
|
+
function buildSections(views: StudioViewMeta[]): StudioSection[] {
|
|
39
|
+
const byGroup = new Map<string, StudioTool[]>();
|
|
40
|
+
|
|
41
|
+
for (const view of views) {
|
|
42
|
+
if (view.hideFromNavigation) continue;
|
|
43
|
+
const label = view.group ?? UNGROUPED_LABEL;
|
|
44
|
+
const tools = byGroup.get(label) ?? [];
|
|
45
|
+
tools.push({
|
|
46
|
+
path: `/${view.slug}`,
|
|
47
|
+
name: view.name,
|
|
48
|
+
description: view.description ?? "",
|
|
49
|
+
icon: view.icon
|
|
50
|
+
});
|
|
51
|
+
byGroup.set(label, tools);
|
|
101
52
|
}
|
|
102
|
-
|
|
53
|
+
|
|
54
|
+
const ordered = [
|
|
55
|
+
...GROUP_ORDER.filter(g => byGroup.has(g)),
|
|
56
|
+
...[...byGroup.keys()].filter(g => !GROUP_ORDER.includes(g))
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
return ordered.map(label => ({ label,
|
|
60
|
+
tools: byGroup.get(label)! }));
|
|
61
|
+
}
|
|
103
62
|
|
|
104
63
|
/* ═══════════════════════════════════════════════════════════════ */
|
|
105
64
|
|
|
@@ -151,6 +110,7 @@ export function StudioHomePage({
|
|
|
151
110
|
const context = useRebaseContext();
|
|
152
111
|
const breadcrumbs = useStudioBreadcrumbs();
|
|
153
112
|
const navigate = useNavigate();
|
|
113
|
+
const registry = useRebaseRegistry();
|
|
154
114
|
|
|
155
115
|
useEffect(() => {
|
|
156
116
|
breadcrumbs.set({ breadcrumbs: [] });
|
|
@@ -162,10 +122,25 @@ export function StudioHomePage({
|
|
|
162
122
|
|
|
163
123
|
const pluginActions = useSlot("home.actions", sectionProps);
|
|
164
124
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
);
|
|
125
|
+
// The collection editor ("schema") is not part of `devViews` — RebaseNavigation
|
|
126
|
+
// injects it when the CMS enables a collection editor. Mirror that condition so
|
|
127
|
+
// the card tracks the route.
|
|
128
|
+
const schemaEnabled = Boolean(registry.studioConfig && registry.cmsConfig?.collectionEditor);
|
|
129
|
+
|
|
130
|
+
const filteredSections = useMemo(() => {
|
|
131
|
+
const views: StudioViewMeta[] = [];
|
|
132
|
+
if (schemaEnabled) {
|
|
133
|
+
views.push({
|
|
134
|
+
slug: "schema",
|
|
135
|
+
name: "Collections",
|
|
136
|
+
group: "Database",
|
|
137
|
+
icon: "LayoutList",
|
|
138
|
+
description: "Define and manage your data model and collection schemas"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
views.push(...(registry.studioConfig?.devViews ?? []));
|
|
142
|
+
return buildSections(views).filter(s => !hiddenGroups?.includes(s.label));
|
|
143
|
+
}, [registry.studioConfig?.devViews, schemaEnabled, hiddenGroups]);
|
|
169
144
|
|
|
170
145
|
const groupNames = useMemo(
|
|
171
146
|
() => filteredSections.map(s => s.label),
|
package/src/index.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
// ─── Bridge ─────────────────────────────────────────────────────────
|
|
2
|
-
// Re-export the Studio Bridge from @rebasepro/
|
|
2
|
+
// Re-export the Studio Bridge from @rebasepro/app.
|
|
3
3
|
// The bridge lives in core so both studio and CMS can access it
|
|
4
4
|
// without circular dependencies.
|
|
5
5
|
export {
|
|
6
6
|
StudioBridgeProvider,
|
|
7
7
|
StudioBridgeContext,
|
|
8
8
|
useStudioCollectionRegistry,
|
|
9
|
-
|
|
9
|
+
useStudioSidePanelController,
|
|
10
10
|
useStudioUrlController,
|
|
11
11
|
useStudioNavigationState,
|
|
12
12
|
useStudioBreadcrumbs
|
|
13
|
-
} from "@rebasepro/
|
|
13
|
+
} from "@rebasepro/app";
|
|
14
14
|
export type {
|
|
15
15
|
StudioBridge,
|
|
16
16
|
BreadcrumbEntry,
|
|
17
17
|
BreadcrumbsController
|
|
18
|
-
} from "@rebasepro/
|
|
18
|
+
} from "@rebasepro/app";
|
|
19
19
|
|
|
20
20
|
// ─── Studio Entry Point ─────────────────────────────────────────────
|
|
21
21
|
// Only export the lightweight orchestrator & home page.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CollectionConfig, PostgresProperties, Property, StringProperty, NumberProperty, ArrayProperty, TableColumnInfo, TableMetadata, SecurityOperation, SecurityRule } from "@rebasepro/types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Maps a PostgreSQL column data type to a Rebase property type.
|
|
@@ -194,13 +194,13 @@ label: v.replace(/_/g, " ").replace(/\b\w/g, (c: string) => c.toUpperCase()) }))
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
/**
|
|
197
|
-
* Builds a partial
|
|
197
|
+
* Builds a partial CollectionConfig from PostgreSQL table metadata.
|
|
198
198
|
* This is used when creating a new collection from an existing database table.
|
|
199
199
|
*/
|
|
200
200
|
export function buildCollectionFromTableMetadata(
|
|
201
201
|
tableName: string,
|
|
202
202
|
metadata: TableMetadata
|
|
203
|
-
): Partial<
|
|
203
|
+
): Partial<CollectionConfig> {
|
|
204
204
|
const properties: Record<string, Property> = {};
|
|
205
205
|
const propertiesOrder: string[] = [];
|
|
206
206
|
const relations: Array<{
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { describe, it, expect } from "@jest/globals";
|
|
3
3
|
import { determineTableAndPK, extractTablesFromQuery, resolveQueryCollections } from "./sql_utils";
|
|
4
4
|
import { TableInfo } from "../components/SQLEditor/SQLEditor";
|
|
5
|
-
import {
|
|
5
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
6
6
|
|
|
7
7
|
const mockSchemas: Record<string, TableInfo[]> = {
|
|
8
8
|
"public": [
|
|
@@ -81,31 +81,31 @@ isPrimaryKey: false }
|
|
|
81
81
|
]
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
const mockCollections:
|
|
84
|
+
const mockCollections: CollectionConfig[] = [
|
|
85
85
|
{
|
|
86
86
|
slug: "users",
|
|
87
87
|
name: "Users",
|
|
88
88
|
table: "users",
|
|
89
89
|
properties: {}
|
|
90
|
-
} as
|
|
90
|
+
} as CollectionConfig,
|
|
91
91
|
{
|
|
92
92
|
slug: "roles",
|
|
93
93
|
name: "Roles",
|
|
94
94
|
table: "roles",
|
|
95
95
|
properties: {}
|
|
96
|
-
} as
|
|
96
|
+
} as CollectionConfig,
|
|
97
97
|
{
|
|
98
98
|
slug: "blogPosts",
|
|
99
99
|
name: "Blog Posts",
|
|
100
100
|
table: "blog_posts",
|
|
101
101
|
properties: {}
|
|
102
|
-
} as
|
|
102
|
+
} as CollectionConfig,
|
|
103
103
|
{
|
|
104
104
|
slug: "blog-entries", // slug with hyphen, no table → falls back to snake_case "blog_entries"
|
|
105
105
|
name: "Blog Entries",
|
|
106
106
|
table: "",
|
|
107
107
|
properties: {}
|
|
108
|
-
} as
|
|
108
|
+
} as CollectionConfig
|
|
109
109
|
];
|
|
110
110
|
|
|
111
111
|
describe("determineTableAndPK", () => {
|
package/src/utils/sql_utils.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseFirst } from "pgsql-ast-parser";
|
|
2
2
|
import { TableInfo } from "../components/SQLEditor/SQLEditor";
|
|
3
|
-
import {
|
|
3
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A table extracted from a SQL query's FROM/JOIN clauses.
|
|
@@ -19,7 +19,7 @@ export interface ResolvedQueryCollection {
|
|
|
19
19
|
/** SQL alias if present (e.g. "bp") */
|
|
20
20
|
tableAlias?: string;
|
|
21
21
|
/** The matched collection */
|
|
22
|
-
collection:
|
|
22
|
+
collection: CollectionConfig;
|
|
23
23
|
/** Columns from this table that are present in the result set */
|
|
24
24
|
columns: string[];
|
|
25
25
|
/** The result column name that holds the primary key for this table (e.g. "id", "author_id") */
|
|
@@ -77,7 +77,7 @@ import { toSnakeCase } from "@rebasepro/utils";
|
|
|
77
77
|
export function resolveQueryCollections(
|
|
78
78
|
sqlString: string,
|
|
79
79
|
schemas: Record<string, TableInfo[]>,
|
|
80
|
-
collections:
|
|
80
|
+
collections: CollectionConfig[],
|
|
81
81
|
resultColumns?: string[]
|
|
82
82
|
): ResolvedQueryCollection[] {
|
|
83
83
|
const tables = extractTablesFromQuery(sqlString);
|