@rebasepro/app 0.17.3 → 0.18.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 (185) hide show
  1. package/README.md +4 -1
  2. package/dist/components/index.d.ts +0 -3
  3. package/dist/contexts/AuthControllerContext.d.ts +2 -1
  4. package/dist/contexts/CustomizationControllerContext.d.ts +2 -1
  5. package/dist/contexts/DialogsProvider.d.ts +2 -1
  6. package/dist/contexts/RebaseDataContext.d.ts +10 -1
  7. package/dist/contexts/StorageSourceContext.d.ts +11 -1
  8. package/dist/core/RebaseProps.d.ts +2 -12
  9. package/dist/debug/index.d.ts +19 -0
  10. package/dist/debug.js +3631 -0
  11. package/dist/hooks/data/delete.d.ts +16 -7
  12. package/dist/hooks/data/save.d.ts +18 -4
  13. package/dist/hooks/index.d.ts +1 -0
  14. package/dist/hooks/useAdminModeController.d.ts +5 -4
  15. package/dist/hooks/useNavigationGroupLabel.d.ts +17 -0
  16. package/dist/hooks/useStudioBridge.d.ts +33 -0
  17. package/dist/index.es.js +2599 -6980
  18. package/dist/index.es.js.map +1 -1
  19. package/dist/vitePlugin.d.ts +22 -0
  20. package/dist/vitePlugin.js +40 -1
  21. package/package.json +42 -31
  22. package/src/auth/api.ts +0 -134
  23. package/src/auth/index.ts +0 -26
  24. package/src/auth/types.ts +0 -115
  25. package/src/auth/useRebaseAuthController.ts +0 -345
  26. package/src/collections/collection_view_config.ts +0 -76
  27. package/src/collections/entity-display-cache.ts +0 -195
  28. package/src/collections/entity-display.ts +0 -77
  29. package/src/collections/entity_image_preview.ts +0 -61
  30. package/src/collections/filter-operator-resolution.ts +0 -119
  31. package/src/collections/form-layout.ts +0 -394
  32. package/src/collections/index.ts +0 -13
  33. package/src/collections/navigation_from_path.ts +0 -123
  34. package/src/collections/navigation_utils.ts +0 -171
  35. package/src/collections/parent_references_from_path.ts +0 -59
  36. package/src/collections/property-path.ts +0 -30
  37. package/src/collections/property_presentation.ts +0 -264
  38. package/src/collections/summary-property.ts +0 -114
  39. package/src/collections/title-property.ts +0 -312
  40. package/src/components/AIIcon.tsx +0 -47
  41. package/src/components/AuthSimulationMenu.tsx +0 -0
  42. package/src/components/ConfirmationDialog.tsx +0 -48
  43. package/src/components/Debug/UIReferenceView.tsx +0 -1053
  44. package/src/components/Debug/UIStyleGuide.tsx +0 -159
  45. package/src/components/Debug/collection-views/CardViewDemo.tsx +0 -87
  46. package/src/components/Debug/collection-views/CollectionTableDemo.tsx +0 -107
  47. package/src/components/Debug/collection-views/KanbanBoardDemo.tsx +0 -89
  48. package/src/components/Debug/collection-views/index.ts +0 -3
  49. package/src/components/Debug/collection-views/sample_data.ts +0 -44
  50. package/src/components/Debug/crm-dashboard/CalendarWidget.tsx +0 -387
  51. package/src/components/Debug/crm-dashboard/CrmDashboardDemo.tsx +0 -1100
  52. package/src/components/Debug/crm-dashboard/DashboardMetrics.tsx +0 -131
  53. package/src/components/Debug/crm-dashboard/PipelineOverview.tsx +0 -288
  54. package/src/components/Debug/crm-dashboard/RecentActivity.tsx +0 -213
  55. package/src/components/Debug/crm-dashboard/TaskFilters.tsx +0 -182
  56. package/src/components/Debug/crm-dashboard/TaskTable.tsx +0 -425
  57. package/src/components/Debug/crm-dashboard/TasksView.tsx +0 -234
  58. package/src/components/ErrorTooltip.tsx +0 -12
  59. package/src/components/ErrorView.tsx +0 -94
  60. package/src/components/LanguageToggle.tsx +0 -59
  61. package/src/components/LoginView/LoginView.tsx +0 -1235
  62. package/src/components/LoginView/index.ts +0 -2
  63. package/src/components/LoginView/oauth-redirect-flow.ts +0 -184
  64. package/src/components/NotFoundPage.tsx +0 -27
  65. package/src/components/RebaseAuth.tsx +0 -27
  66. package/src/components/RebaseLogo.tsx +0 -88
  67. package/src/components/SchemaDriftBanner.tsx +0 -102
  68. package/src/components/UnsavedChangesDialog.tsx +0 -46
  69. package/src/components/UserDisplay.tsx +0 -54
  70. package/src/components/UserSelectPopover.tsx +0 -399
  71. package/src/components/UserSettingsView.tsx +0 -318
  72. package/src/components/common/index.ts +0 -6
  73. package/src/components/common/table_height.tsx +0 -21
  74. package/src/components/common/types.tsx +0 -66
  75. package/src/components/common/useColumnsIds.tsx +0 -261
  76. package/src/components/common/useDataTableController.tsx +0 -571
  77. package/src/components/common/useDebouncedData.ts +0 -49
  78. package/src/components/common/useScrollRestoration.tsx +0 -68
  79. package/src/components/index.tsx +0 -26
  80. package/src/contexts/AdminModeController.tsx +0 -11
  81. package/src/contexts/AnalyticsContext.tsx +0 -4
  82. package/src/contexts/AuthControllerContext.tsx +0 -4
  83. package/src/contexts/CollectionResolverContext.tsx +0 -24
  84. package/src/contexts/CollectionScopeContext.tsx +0 -68
  85. package/src/contexts/ComponentOverrideContext.tsx +0 -81
  86. package/src/contexts/CustomizationControllerContext.tsx +0 -4
  87. package/src/contexts/DataDriverContext.tsx +0 -4
  88. package/src/contexts/DataSourcesContext.tsx +0 -36
  89. package/src/contexts/DatabaseAdminContext.tsx +0 -4
  90. package/src/contexts/DialogsProvider.tsx +0 -56
  91. package/src/contexts/EffectiveRoleController.tsx +0 -12
  92. package/src/contexts/ModeController.tsx +0 -11
  93. package/src/contexts/RebaseClientInstanceContext.tsx +0 -7
  94. package/src/contexts/RebaseDataContext.tsx +0 -4
  95. package/src/contexts/SnackbarProvider.tsx +0 -15
  96. package/src/contexts/StorageSourceContext.tsx +0 -4
  97. package/src/contexts/StorageSourcesContext.tsx +0 -30
  98. package/src/contexts/UserConfigurationPersistenceContext.tsx +0 -4
  99. package/src/contexts/index.ts +0 -18
  100. package/src/core/PluginLifecycleManager.tsx +0 -96
  101. package/src/core/PluginProviderStack.tsx +0 -40
  102. package/src/core/Rebase.tsx +0 -488
  103. package/src/core/RebaseProps.tsx +0 -333
  104. package/src/core/RebaseRouter.tsx +0 -18
  105. package/src/core/RebaseRoutes.tsx +0 -24
  106. package/src/core/index.tsx +0 -5
  107. package/src/hooks/ApiConfigContext.tsx +0 -83
  108. package/src/hooks/data/collectionQuery.ts +0 -55
  109. package/src/hooks/data/delete.ts +0 -56
  110. package/src/hooks/data/save.ts +0 -78
  111. package/src/hooks/data/useCollection.tsx +0 -183
  112. package/src/hooks/data/useData.tsx +0 -18
  113. package/src/hooks/data/useDataOrder.ts +0 -26
  114. package/src/hooks/data/useFetch.tsx +0 -156
  115. package/src/hooks/data/useRelationSelector.tsx +0 -280
  116. package/src/hooks/index.tsx +0 -45
  117. package/src/hooks/useAdminModeController.tsx +0 -23
  118. package/src/hooks/useAnalyticsController.tsx +0 -8
  119. package/src/hooks/useAuthController.tsx +0 -15
  120. package/src/hooks/useAuthSubscription.ts +0 -130
  121. package/src/hooks/useBackendStorageSource.ts +0 -295
  122. package/src/hooks/useBridgeRegistration.tsx +0 -32
  123. package/src/hooks/useBrowserTitleAndIcon.tsx +0 -23
  124. package/src/hooks/useBuildAdminModeController.tsx +0 -29
  125. package/src/hooks/useBuildEffectiveRoleController.tsx +0 -30
  126. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +0 -83
  127. package/src/hooks/useBuildModeController.tsx +0 -73
  128. package/src/hooks/useClipboard.tsx +0 -158
  129. package/src/hooks/useCollapsedGroups.ts +0 -116
  130. package/src/hooks/useComponentOverride.tsx +0 -59
  131. package/src/hooks/useCustomizationController.tsx +0 -14
  132. package/src/hooks/useDialogsController.tsx +0 -15
  133. package/src/hooks/useEffectiveRoleController.tsx +0 -10
  134. package/src/hooks/useLargeLayout.tsx +0 -65
  135. package/src/hooks/useModeController.tsx +0 -23
  136. package/src/hooks/useNavigationBlocker.tsx +0 -108
  137. package/src/hooks/usePermissions.ts +0 -45
  138. package/src/hooks/useRebaseClient.tsx +0 -10
  139. package/src/hooks/useRebaseContext.tsx +0 -84
  140. package/src/hooks/useRebaseRegistry.tsx +0 -91
  141. package/src/hooks/useResolvedComponent.tsx +0 -195
  142. package/src/hooks/useSlot.tsx +0 -64
  143. package/src/hooks/useSnackbarController.tsx +0 -80
  144. package/src/hooks/useStorageSource.tsx +0 -11
  145. package/src/hooks/useStudioBridge.tsx +0 -258
  146. package/src/hooks/useTranslation.ts +0 -38
  147. package/src/hooks/useUnsavedChangesDialog.tsx +0 -67
  148. package/src/hooks/useUserConfigurationPersistence.tsx +0 -11
  149. package/src/i18n/RebaseI18nProvider.tsx +0 -159
  150. package/src/index.ts +0 -32
  151. package/src/internal/common.tsx +0 -28
  152. package/src/internal/useRestoreScroll.tsx +0 -69
  153. package/src/locales/de.ts +0 -941
  154. package/src/locales/en.ts +0 -1021
  155. package/src/locales/es.ts +0 -962
  156. package/src/locales/fr.ts +0 -941
  157. package/src/locales/hi.ts +0 -941
  158. package/src/locales/it.ts +0 -941
  159. package/src/locales/pt.ts +0 -949
  160. package/src/util/constants.ts +0 -7
  161. package/src/util/createFormexStub.tsx +0 -66
  162. package/src/util/entity_cache.ts +0 -242
  163. package/src/util/enums.ts +0 -34
  164. package/src/util/icon_list.ts +0 -22
  165. package/src/util/icons.tsx +0 -129
  166. package/src/util/index.ts +0 -13
  167. package/src/util/local_storage.ts +0 -66
  168. package/src/util/previews.ts +0 -127
  169. package/src/util/useStorageUploadController.tsx +0 -354
  170. package/src/vitePlugin.ts +0 -215
  171. /package/dist/{components/Debug → debug}/UIReferenceView.d.ts +0 -0
  172. /package/dist/{components/Debug → debug}/UIStyleGuide.d.ts +0 -0
  173. /package/dist/{components/Debug → debug}/collection-views/CardViewDemo.d.ts +0 -0
  174. /package/dist/{components/Debug → debug}/collection-views/CollectionTableDemo.d.ts +0 -0
  175. /package/dist/{components/Debug → debug}/collection-views/KanbanBoardDemo.d.ts +0 -0
  176. /package/dist/{components/Debug → debug}/collection-views/index.d.ts +0 -0
  177. /package/dist/{components/Debug → debug}/collection-views/sample_data.d.ts +0 -0
  178. /package/dist/{components/Debug → debug}/crm-dashboard/CalendarWidget.d.ts +0 -0
  179. /package/dist/{components/Debug → debug}/crm-dashboard/CrmDashboardDemo.d.ts +0 -0
  180. /package/dist/{components/Debug → debug}/crm-dashboard/DashboardMetrics.d.ts +0 -0
  181. /package/dist/{components/Debug → debug}/crm-dashboard/PipelineOverview.d.ts +0 -0
  182. /package/dist/{components/Debug → debug}/crm-dashboard/RecentActivity.d.ts +0 -0
  183. /package/dist/{components/Debug → debug}/crm-dashboard/TaskFilters.d.ts +0 -0
  184. /package/dist/{components/Debug → debug}/crm-dashboard/TaskTable.d.ts +0 -0
  185. /package/dist/{components/Debug → debug}/crm-dashboard/TasksView.d.ts +0 -0
@@ -1,488 +0,0 @@
1
- "use client";
2
- import type { RebaseProps } from "./RebaseProps";
3
- import type { CustomizationController, RebasePlugin, SlotContribution } from "@rebasepro/cms-types";
4
- import type { ComponentOverrideMap } from "@rebasepro/cms-types";
5
-
6
- import React, { useEffect, useMemo, useRef, useState } from "react";
7
- import { CenteredView, Typography } from "@rebasepro/ui";
8
- import { User, CollectionRegistryController, DataDriver, DataSourceDefinition, RebaseData, DEFAULT_DATA_SOURCE_KEY, StorageSource, StorageSourceDefinition, DEFAULT_STORAGE_SOURCE_KEY } from "@rebasepro/types";
9
- import { RebaseContext, UNRENDERED_SLOTS } from "@rebasepro/cms-types";
10
- import { PluginProviderStack } from "./PluginProviderStack";
11
- import { PluginLifecycleManager } from "./PluginLifecycleManager";
12
- import { AuthControllerContext, CollectionResolverRegistrationContext, CollectionResolver } from "../contexts";
13
- import { useCustomizationController, useRebaseContext, useAuthSubscription } from "../hooks";
14
- import { ApiConfigProvider, useApiConfig } from "../hooks/ApiConfigContext";
15
- import { ErrorView } from "../components";
16
- import { StorageSourceContext } from "../contexts/StorageSourceContext";
17
- import { UserConfigurationPersistenceContext } from "../contexts/UserConfigurationPersistenceContext";
18
- import { RebaseDataContext } from "../contexts/RebaseDataContext";
19
- import { DataSourcesContext, DataSourcesContextValue } from "../contexts/DataSourcesContext";
20
- import { StorageSourcesContext, StorageSourcesContextValue } from "../contexts/StorageSourcesContext";
21
- import { DatabaseAdminContext } from "../contexts/DatabaseAdminContext";
22
- import { ModeControllerProvider, AdminModeControllerProvider, SnackbarProvider } from "../contexts";
23
- import { RebaseI18nProvider } from "../i18n/RebaseI18nProvider";
24
- import { RebaseRegistryProvider } from "../hooks/useRebaseRegistry";
25
- import { SchemaDriftProvider } from "../components/SchemaDriftBanner";
26
- import { GlobalComponentOverrideProvider } from "../contexts/ComponentOverrideContext";
27
- import { useBuildModeController } from "../hooks/useBuildModeController";
28
- import { useBuildAdminModeController } from "../hooks/useBuildAdminModeController";
29
- import { RebaseClientInstanceContext } from "../contexts/RebaseClientInstanceContext";
30
- import { DialogsProvider } from "../contexts/DialogsProvider";
31
- import { buildRebaseData, wrapAsEntityData, CollectionRegistry } from "@rebasepro/common";
32
- import { CustomizationControllerContext } from "../contexts/CustomizationControllerContext";
33
- import { AnalyticsContext } from "../contexts/AnalyticsContext";
34
-
35
- import { EffectiveRoleControllerContext } from "../contexts/EffectiveRoleController";
36
- import { useBuildEffectiveRoleController } from "../hooks/useBuildEffectiveRoleController";
37
-
38
- /**
39
- * If you are using independent components of the admin
40
- * you need to wrap them with this main component, so the internal hooks work.
41
- *
42
- * This is the main component of Rebase. It acts as the provider of all the
43
- * internal contexts and hooks.
44
- *
45
- * You only need to use this component if you are building a custom app.
46
- *
47
- * @group Core
48
- */
49
- export function Rebase<USER extends User, DB = unknown>(props: RebaseProps<USER, DB>) {
50
-
51
- const {
52
- children,
53
- entityLinkBuilder,
54
- userConfigPersistence,
55
- dateTimeFormat,
56
- locale,
57
- client,
58
- authController: authControllerProp,
59
- storageSource: storageSourceProp,
60
- dataSources: dataSourcesProp,
61
- storageSources: storageSourcesProp,
62
-
63
- databaseAdmin,
64
- plugins: pluginsProp,
65
- slots: directSlots = [],
66
- onAnalyticsEvent,
67
- propertyConfigs,
68
- entityViews,
69
- collectionViews,
70
- entityActions,
71
-
72
- effectiveRoleController,
73
- apiUrl,
74
- translations,
75
- components: componentsProp
76
- } = props;
77
-
78
- const plugins = pluginsProp;
79
-
80
- // Validate plugin key uniqueness
81
- if (plugins) {
82
- const keys = plugins.map((p) => p.key);
83
- if (new Set(keys).size !== keys.length) {
84
- console.error("Duplicate plugin keys detected:", keys.filter((k, i) => keys.indexOf(k) !== i));
85
- }
86
- }
87
-
88
- // Merge direct slots with plugin slots.
89
- const resolvedSlots: SlotContribution[] = useMemo(() => [
90
- ...directSlots,
91
- ...((plugins ?? []).flatMap((p) => p.slots ?? []))
92
- ], [directSlots, plugins]);
93
-
94
- // Seven of the twenty-nine declared slots are rendered nowhere. They have
95
- // props interfaces and rows in the public slot reference, so registering
96
- // for one looks exactly like registering for one that works — and then
97
- // nothing happens, with no way to tell whether the fault is the plugin's.
98
- // `UNRENDERED_SLOTS` is kept honest by `slot-render-sites.test.ts`, which
99
- // derives the same set by scanning for render sites.
100
- useEffect(() => {
101
- const dead = new Set<string>(UNRENDERED_SLOTS);
102
- const registered = new Set(resolvedSlots.map(s => s.slot).filter(slot => dead.has(slot)));
103
- for (const slot of registered) {
104
- console.warn(
105
- `[Rebase] A component is registered for the "${slot}" slot, which this ` +
106
- "build declares but renders nowhere — nothing will appear. This is a gap " +
107
- "in Rebase, not in your plugin."
108
- );
109
- }
110
- }, [resolvedSlots]);
111
-
112
-
113
- // Auth fallback logic
114
- const clientAuthController = useAuthSubscription(authControllerProp ? undefined : client?.auth);
115
- const authController = authControllerProp ?? clientAuthController;
116
-
117
- const normalizedDataSources = useMemo(() => {
118
- return dataSourcesProp ?? [];
119
- }, [dataSourcesProp]);
120
-
121
- // Rows arrive as columns only; their address is derived from the
122
- // collection's primary keys. The collections live below us (in RebaseCMS),
123
- // so they register a resolver here and we read it lazily, per row. Headless
124
- // apps register nothing and are unaffected.
125
- const collectionResolverRef = useRef<CollectionResolver | undefined>(undefined);
126
- const registerCollectionResolver = React.useCallback((resolver: CollectionResolver | undefined) => {
127
- collectionResolverRef.current = resolver;
128
- }, []);
129
- const entityDataOptions = useMemo(() => ({
130
- resolveCollection: (slug: string) => collectionResolverRef.current?.(slug)
131
- }), []);
132
-
133
- // Build the data-source context: the declared registry plus a RebaseData
134
- // per direct/custom source (those carrying a client-side driver). Server-
135
- // mediated sources have no entry — they ride the default client.
136
- //
137
- // Shallow-compared against the previous build (key/engine/transport +
138
- // driver instances) so inline `dataSources` literals don't rebuild
139
- // RebaseData instances every render and thrash data-fetch effects that
140
- // key off the data identity.
141
- const dataSourcesRef = useRef<{
142
- sig: { key: string; engine: string; transport?: string; driver?: DataDriver }[];
143
- value: DataSourcesContextValue;
144
- } | null>(null);
145
- const dataSourcesValue = useMemo<DataSourcesContextValue>(() => {
146
- const sig = normalizedDataSources.map((d) => ({ key: d.key, engine: d.engine, transport: d.transport, driver: d.driver }));
147
- const prev = dataSourcesRef.current;
148
- if (prev
149
- && prev.sig.length === sig.length
150
- && sig.every((s, i) => prev.sig[i].key === s.key
151
- && prev.sig[i].engine === s.engine
152
- && prev.sig[i].transport === s.transport
153
- && prev.sig[i].driver === s.driver)) {
154
- return prev.value;
155
- }
156
- const registry: Record<string, DataSourceDefinition> = {};
157
- const sources: Record<string, RebaseData> = {};
158
- for (const ds of normalizedDataSources) {
159
- const { driver, ...definition } = ds;
160
- // Materialize the inferred transport so downstream consumers
161
- // (routing, editor capabilities) always see a concrete value.
162
- registry[ds.key] = {
163
- ...definition,
164
- transport: ds.transport ?? (driver ? "direct" : "server")
165
- };
166
- if (driver) sources[ds.key] = buildRebaseData(driver, entityDataOptions);
167
- }
168
- const value: DataSourcesContextValue = { registry, sources };
169
- dataSourcesRef.current = { sig, value };
170
- return value;
171
- }, [normalizedDataSources, entityDataOptions]);
172
-
173
- // Default data source — serves every collection not routed to a
174
- // registered direct/custom source. Resolution:
175
- // 1. A "(default)"-keyed data source with a driver.
176
- // 2. `client.data`.
177
- // 3. The sole registered source, when it is the only candidate.
178
- const resolvedData = useMemo(() => {
179
- const registeredDefault = dataSourcesValue.sources[DEFAULT_DATA_SOURCE_KEY];
180
- if (registeredDefault) return registeredDefault;
181
- // admin boundary: the SDK client returns flat rows; wrap them into the
182
- // Entity view-model the admin (`useData()`) renders.
183
- if (client?.data) return wrapAsEntityData(client.data, entityDataOptions);
184
- const built = Object.values(dataSourcesValue.sources);
185
- if (built.length === 1) return built[0];
186
- if (built.length > 1) {
187
- throw new Error(
188
- "[Rebase] Several data sources are registered but none is the default. " +
189
- "Pass a `client`, or key one `dataSources` entry \"(default)\"."
190
- );
191
- }
192
- throw new Error("Rebase requires either `client` or a `dataSources` entry with a driver to be provided");
193
- }, [client, dataSourcesValue, entityDataOptions]);
194
-
195
- // Storage fallback logic
196
- const resolvedStorage = storageSourceProp ?? client?.storage;
197
-
198
- // ── Storage sources (mirrors the dataSources pattern) ────────────
199
- // Storage-source definitions discovered from the backend via
200
- // `GET /api/storage/sources`. The backend is the single source of truth;
201
- // server-transport sources are auto-wired on the client, `direct` sources
202
- // are completed by the `storageSources` prop (which holds live instances).
203
- const [remoteStorageSources, setRemoteStorageSources] = useState<StorageSourceDefinition[]>([]);
204
- const authUser = authController.user;
205
- const loginSkipped = authController.loginSkipped;
206
- useEffect(() => {
207
- if (!client?.fetchStorageSources) return;
208
- let cancelled = false;
209
- client.fetchStorageSources()
210
- .then((defs) => { if (!cancelled) setRemoteStorageSources(defs); })
211
- .catch((e) => {
212
- // A 401 before auth is expected; the effect re-runs once the
213
- // user logs in (authUser/loginSkipped change) and retries.
214
- //
215
- // Guarded on `cancelled` like the success path: once this
216
- // provider is gone (or the effect re-ran) the outcome of the
217
- // request describes nothing that still exists, and the request
218
- // outlives the provider by however long the network takes to
219
- // give up — so an unguarded write here lands at an arbitrary
220
- // later moment. In a test worker that moment is after the file
221
- // finished, which closes the run's console channel mid-write
222
- // and fails an otherwise green suite.
223
- if (!cancelled) console.debug("[Rebase] Could not load storage sources", e);
224
- });
225
- return () => { cancelled = true; };
226
- }, [client, authUser, loginSkipped]);
227
-
228
- // Normalize the prop + discovered definitions into a registry + sources map.
229
- const storageSourcesRef = useRef<{
230
- propList: { key: string; source?: StorageSource; transport?: string }[];
231
- remote: StorageSourceDefinition[];
232
- resolvedStorage?: StorageSource;
233
- client?: typeof client;
234
- value: StorageSourcesContextValue;
235
- } | null>(null);
236
- const storageSourcesValue = useMemo<StorageSourcesContextValue>(() => {
237
- const list = storageSourcesProp ?? [];
238
- const prev = storageSourcesRef.current;
239
- // Stable identity unless an input actually changed (the prop may be a
240
- // fresh array literal on every render; sources are compared by identity).
241
- if (prev
242
- && prev.remote === remoteStorageSources
243
- && prev.resolvedStorage === resolvedStorage
244
- && prev.client === client
245
- && prev.propList.length === list.length
246
- && list.every((s, i) => prev.propList[i].key === s.key
247
- && prev.propList[i].source === s.source
248
- && prev.propList[i].transport === s.transport)) {
249
- return prev.value;
250
- }
251
- const registry: Record<string, StorageSourceDefinition> = {};
252
- const sources: Record<string, StorageSource> = {};
253
-
254
- // 1. Definitions discovered from the backend (labels, engine, transport).
255
- for (const def of remoteStorageSources) {
256
- registry[def.key] = def;
257
- }
258
-
259
- // 2. Server-backed named sources wired on the client registry: the
260
- // default, `createRebaseClient({ storageSources })`, and any
261
- // server-transport sources auto-registered by `fetchStorageSources`.
262
- if (client?.storageRegistry) {
263
- for (const key of client.storageRegistry.list()) {
264
- const source = client.storageRegistry.get(key);
265
- if (source) sources[key] = source;
266
- }
267
- }
268
-
269
- // 3. App-provided definitions: `direct` live sources and overrides.
270
- for (const ss of list) {
271
- const { source, ...definition } = ss;
272
- registry[ss.key] = definition;
273
- if (source) {
274
- // `direct` transport: app-provided live source (e.g. Firebase).
275
- sources[ss.key] = source;
276
- } else if (!sources[ss.key]
277
- && ss.transport === "server"
278
- && ss.key !== DEFAULT_STORAGE_SOURCE_KEY
279
- && client?.createStorageSource) {
280
- // `server` transport declared only on the prop: build a
281
- // backend-routed source so requests carry `?storageId=key`.
282
- sources[ss.key] = client.createStorageSource(ss.key);
283
- }
284
- }
285
- // 4. Default/server storage from the client.
286
- if (resolvedStorage && !sources[DEFAULT_STORAGE_SOURCE_KEY]) {
287
- sources[DEFAULT_STORAGE_SOURCE_KEY] = resolvedStorage;
288
- }
289
- const value: StorageSourcesContextValue = { registry, sources };
290
- storageSourcesRef.current = {
291
- propList: list.map((s) => ({ key: s.key, source: s.source, transport: s.transport })),
292
- remote: remoteStorageSources,
293
- resolvedStorage,
294
- client,
295
- value
296
- };
297
- return value;
298
- }, [storageSourcesProp, remoteStorageSources, resolvedStorage, client]);
299
-
300
- // Database admin — use explicit prop, or derive from client.ws / driver when available.
301
- const resolvedDatabaseAdmin = useMemo(() => {
302
- if (databaseAdmin) return databaseAdmin;
303
-
304
- // 1. The default source's DataDriver exposes `.admin` capability object
305
- const defaultDriver = normalizedDataSources.find((d) => d.key === DEFAULT_DATA_SOURCE_KEY && d.driver)?.driver
306
- ?? (!client && normalizedDataSources.length === 1 ? normalizedDataSources[0].driver : undefined);
307
- if (defaultDriver?.admin) {
308
- return defaultDriver.admin;
309
- }
310
-
311
- // 2. Auto-derive from the client's WebSocket connection (Rebase backend)
312
- const ws = client?.ws;
313
- if (ws && typeof (ws as unknown as Record<string, unknown>).executeSql === "function") {
314
- const wsAdmin = ws as unknown as import("@rebasepro/types").DatabaseAdmin;
315
- return {
316
- executeSql: wsAdmin.executeSql!.bind(wsAdmin),
317
- fetchAvailableDatabases: wsAdmin.fetchAvailableDatabases?.bind(wsAdmin),
318
- fetchAvailableRoles: wsAdmin.fetchAvailableRoles?.bind(wsAdmin),
319
- fetchApplicationRoles: wsAdmin.fetchApplicationRoles?.bind(wsAdmin),
320
- fetchCurrentDatabase: wsAdmin.fetchCurrentDatabase?.bind(wsAdmin),
321
- fetchUnmappedTables: wsAdmin.fetchUnmappedTables?.bind(wsAdmin),
322
- fetchTableMetadata: wsAdmin.fetchTableMetadata?.bind(wsAdmin),
323
- // Branch admin capabilities
324
- ...(typeof wsAdmin.createBranch === "function" ? {
325
- createBranch: wsAdmin.createBranch.bind(wsAdmin),
326
- deleteBranch: wsAdmin.deleteBranch!.bind(wsAdmin),
327
- listBranches: wsAdmin.listBranches!.bind(wsAdmin)
328
- } : {})
329
- };
330
- }
331
-
332
- return undefined;
333
- }, [databaseAdmin, client, normalizedDataSources]);
334
-
335
- if (!resolvedStorage && typeof console !== "undefined") {
336
- console.warn(
337
- "[Rebase] No storageSource provided. File upload features will not work. " +
338
- "Provide storageSource via props or through a RebaseClient."
339
- );
340
- }
341
-
342
- const pluginsLoading = plugins?.some((p) => p.loading) ?? false;
343
-
344
- const loading = authController.initialLoading || pluginsLoading;
345
-
346
- const customizationController: CustomizationController = useMemo(() => ({
347
- dateTimeFormat,
348
- locale,
349
- entityLinkBuilder,
350
- plugins,
351
- resolvedSlots,
352
- entityViews: entityViews ?? [],
353
- collectionViews: collectionViews ?? [],
354
- entityActions: entityActions ?? [],
355
- propertyConfigs: propertyConfigs ?? {},
356
- components: componentsProp
357
- }), [dateTimeFormat, locale, entityLinkBuilder, plugins, resolvedSlots, entityViews, collectionViews, entityActions, propertyConfigs, componentsProp]);
358
-
359
- const analyticsController = useMemo(() => ({
360
- onAnalyticsEvent
361
- }), []);
362
-
363
- const fallbackEffectiveRoleController = useBuildEffectiveRoleController();
364
- const activeEffectiveRoleController = effectiveRoleController ?? fallbackEffectiveRoleController;
365
-
366
- const modeController = useBuildModeController();
367
- const adminModeController = useBuildAdminModeController();
368
-
369
- if (authController.authError) {
370
- return (
371
- <CenteredView maxWidth={"md"}>
372
- <ErrorView
373
- title={"Error loading auth"}
374
- error={authController.authError as Error | string}/>
375
- </CenteredView>
376
- );
377
- }
378
-
379
- const content = (
380
- <RebaseI18nProvider locale={locale} translations={translations}>
381
- <GlobalComponentOverrideProvider overrides={componentsProp}>
382
- <SnackbarProvider>
383
- <ModeControllerProvider value={modeController}>
384
- <AdminModeControllerProvider value={adminModeController}>
385
- <RebaseClientInstanceContext.Provider value={client}>
386
- <AnalyticsContext.Provider value={analyticsController}>
387
- <CustomizationControllerContext.Provider value={customizationController}>
388
- <UserConfigurationPersistenceContext.Provider
389
- value={userConfigPersistence}>
390
- <StorageSourcesContext.Provider
391
- value={storageSourcesValue}>
392
- <StorageSourceContext.Provider
393
- value={resolvedStorage!}>
394
- <CollectionResolverRegistrationContext.Provider
395
- value={registerCollectionResolver}>
396
- <DataSourcesContext.Provider
397
- value={dataSourcesValue}>
398
- <RebaseDataContext.Provider
399
- value={resolvedData}>
400
- <DatabaseAdminContext.Provider
401
- value={resolvedDatabaseAdmin}>
402
- <AuthControllerContext.Provider
403
- value={authController}>
404
- <EffectiveRoleControllerContext.Provider value={activeEffectiveRoleController}>
405
- <DialogsProvider>
406
- <SchemaDriftProvider>
407
- <RebaseRegistryProvider>
408
- <RebaseInternal
409
- loading={loading}>
410
- {children}
411
- </RebaseInternal>
412
- </RebaseRegistryProvider>
413
- </SchemaDriftProvider>
414
- </DialogsProvider>
415
- </EffectiveRoleControllerContext.Provider>
416
- </AuthControllerContext.Provider>
417
- </DatabaseAdminContext.Provider>
418
- </RebaseDataContext.Provider>
419
- </DataSourcesContext.Provider>
420
- </CollectionResolverRegistrationContext.Provider>
421
- </StorageSourceContext.Provider>
422
- </StorageSourcesContext.Provider>
423
- </UserConfigurationPersistenceContext.Provider>
424
- </CustomizationControllerContext.Provider>
425
- </AnalyticsContext.Provider>
426
- </RebaseClientInstanceContext.Provider>
427
- </AdminModeControllerProvider>
428
- </ModeControllerProvider>
429
- </SnackbarProvider>
430
- </GlobalComponentOverrideProvider>
431
- </RebaseI18nProvider>
432
- );
433
-
434
- const resolvedApiUrl = apiUrl || client?.baseUrl || (typeof window !== "undefined" ? window.location.origin : "");
435
-
436
- if (resolvedApiUrl) {
437
- return (
438
- <ApiConfigProvider
439
- apiUrl={resolvedApiUrl}
440
- apiPath={client?.apiPath}
441
- getAuthToken={authController.getAuthToken}
442
- >
443
- {content}
444
- </ApiConfigProvider>
445
- );
446
- }
447
-
448
- return content;
449
-
450
- }
451
-
452
- function RebaseInternal({
453
- loading,
454
- children
455
- }: {
456
- loading: boolean;
457
- children: React.ReactNode | ((props: { context: RebaseContext; loading: boolean; }) => React.ReactNode);
458
- }) {
459
-
460
- const context = useRebaseContext();
461
- const customizationController = useCustomizationController();
462
-
463
- const childrenResult = typeof children === "function" ? children({
464
- context,
465
- loading
466
- }) : children;
467
-
468
- const plugins = customizationController.plugins;
469
- const authController = context.authController;
470
- const authReady = !loading
471
- && !authController.authLoading
472
- && (Boolean(authController.user) || authController.loginSkipped);
473
-
474
- if (plugins && plugins.length > 0) {
475
- return (
476
- <PluginProviderStack
477
- plugins={plugins}
478
- scope="root"
479
- scopeProps={{ context }}>
480
- {authReady && <PluginLifecycleManager plugins={plugins} context={context}/>}
481
- {childrenResult}
482
- </PluginProviderStack>
483
- );
484
- }
485
-
486
- return childrenResult;
487
- }
488
-