@rebasepro/app 0.12.0 → 0.12.1-canary.g35be8cb

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 CHANGED
@@ -8,7 +8,7 @@ Framework-agnostic runtime for data-driven admin panels — React hooks, provide
8
8
  pnpm add @rebasepro/app
9
9
  ```
10
10
 
11
- **Peer dependencies:** `react >= 19`, `react-dom >= 19`, `react-router ^7`, `react-router-dom ^7`
11
+ **Peer dependencies:** `react >= 19.2.7`, `react-dom >= 19.2.7`, `react-router ^8`
12
12
 
13
13
  ## What This Package Does
14
14
 
@@ -59,7 +59,9 @@ export declare function createAuthConfigCache(): AuthConfigCache;
59
59
  * Concurrent calls are deduplicated: only one network request is made
60
60
  * and all callers share the same promise.
61
61
  */
62
- export declare function fetchAuthConfig(apiUrl: string, cache: AuthConfigCache): Promise<AuthConfigResponse>;
62
+ export declare function fetchAuthConfig(apiUrl: string, cache: AuthConfigCache,
63
+ /** The backend's `basePath`; only needed if it is not the default. */
64
+ apiPath?: string): Promise<AuthConfigResponse>;
63
65
  /**
64
66
  * Clear the cached auth config (e.g. on logout or for testing).
65
67
  */
@@ -8,17 +8,51 @@ import React from "react";
8
8
  */
9
9
  export interface ApiConfig {
10
10
  apiUrl: string;
11
+ /**
12
+ * The path the backend mounts its API under, appended to {@link apiUrl}.
13
+ *
14
+ * `"/api"` by default, which is the server's default `basePath`. It is
15
+ * carried here because a good deal of UI builds request URLs by hand
16
+ * instead of going through the client's typed methods, and every one of
17
+ * those sites used to write `/api` as a literal — so a backend configured
18
+ * with any other `basePath` served an admin panel whose auth-provider
19
+ * discovery, storage browser, history panel, user picker and Studio tools
20
+ * all requested paths that did not exist.
21
+ */
22
+ apiPath: string;
11
23
  getAuthToken?: () => Promise<string | null>;
12
24
  }
25
+ /** What the server uses when `basePath` is not configured. */
26
+ export declare const DEFAULT_API_PATH = "/api";
13
27
  /**
14
28
  * Read the API config from context. Returns `undefined` if no provider is present,
15
29
  * allowing hooks to fall back to their own props.
16
30
  */
17
31
  export declare function useApiConfig(): ApiConfig | undefined;
18
32
  /**
19
- * Provide API configuration (apiUrl, getAuthToken) to the entire subtree.
33
+ * `apiUrl` and `apiPath` joined, with no trailing slash — the prefix to build a
34
+ * request URL from.
35
+ *
36
+ * Returns `undefined` when there is no provider, so a caller can keep its own
37
+ * fallback rather than silently requesting a relative path.
38
+ */
39
+ export declare function useApiBase(): string | undefined;
40
+ /**
41
+ * The same prefix, derived from a client rather than from context.
42
+ *
43
+ * For the code that holds a `RebaseClient` but sits outside (or above) an
44
+ * `ApiConfigProvider`. Returns `undefined` when the client has no base URL,
45
+ * which is the signal every caller already treats as "cannot build a request".
46
+ */
47
+ export declare function apiBaseOf(client?: {
48
+ baseUrl?: string;
49
+ apiPath?: string;
50
+ }): string | undefined;
51
+ /**
52
+ * Provide API configuration (apiUrl, apiPath, getAuthToken) to the entire subtree.
20
53
  * Typically rendered inside `<Rebase>` or at the app root.
21
54
  */
22
- export declare function ApiConfigProvider({ apiUrl, getAuthToken, children }: ApiConfig & {
55
+ export declare function ApiConfigProvider({ apiUrl, apiPath, getAuthToken, children }: Omit<ApiConfig, "apiPath"> & {
56
+ apiPath?: string;
23
57
  children: React.ReactNode;
24
58
  }): React.JSX.Element;
@@ -7,6 +7,11 @@ export interface BackendStorageSourceProps {
7
7
  * Backend API URL (e.g., 'http://localhost:3001')
8
8
  */
9
9
  apiUrl: string;
10
+ /**
11
+ * The path the backend mounts its API under. Defaults to the server's own
12
+ * default; pass the backend's `basePath` if it was configured otherwise.
13
+ */
14
+ apiPath?: string;
10
15
  /**
11
16
  * Function to get the current auth token
12
17
  */
@@ -27,4 +32,4 @@ export interface BackendStorageSourceProps {
27
32
  * <Rebase storageSource={storageSource} ... />
28
33
  * ```
29
34
  */
30
- export declare function useBackendStorageSource({ apiUrl, getAuthToken }: BackendStorageSourceProps): StorageSource;
35
+ export declare function useBackendStorageSource({ apiUrl, apiPath, getAuthToken }: BackendStorageSourceProps): StorageSource;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Blocker, BlockerFunction } from "react-router-dom";
2
+ import { Blocker, BlockerFunction } from "react-router";
3
3
  /**
4
4
  * Owns the single React Router blocker for the whole app.
5
5
  *
@@ -13,6 +13,31 @@ export interface StudioBridge {
13
13
  urlController: UrlController;
14
14
  navigationState: NavigationStateController;
15
15
  breadcrumbs: BreadcrumbsController;
16
+ capabilities: StudioCapabilities;
17
+ }
18
+ /**
19
+ * What the *host* of these tools can do, as opposed to what the backend can.
20
+ *
21
+ * Studio is mounted in two very different places. In a project's own admin
22
+ * panel it runs next to the collection source files and can edit them through
23
+ * the schema-editor routes. In the hosted console it runs against somebody
24
+ * else's deployed container: there is no source to edit — the container is
25
+ * rebuilt from the customer's repository on every deploy — and the routes that
26
+ * would edit it are not mounted at all, because the framework switches the
27
+ * schema editor off under `NODE_ENV=production`.
28
+ *
29
+ * Tools that would otherwise offer a write into the codebase read this to
30
+ * decide whether that write is even meaningful.
31
+ */
32
+ export interface StudioCapabilities {
33
+ /**
34
+ * Whether the host has the project's collection source at hand and can
35
+ * write to it.
36
+ *
37
+ * Defaults to `true`, which is what an admin panel running beside its own
38
+ * `collectionsDir` has always assumed.
39
+ */
40
+ codebase: boolean;
16
41
  }
17
42
  export declare const StudioBridgeContext: React.Context<StudioBridge>;
18
43
  /**
@@ -46,6 +71,8 @@ export declare function useStudioUrlController(): UrlController;
46
71
  export declare function useStudioNavigationState(): NavigationStateController;
47
72
  /** Breadcrumbs controller — returns noop if the admin is not present. */
48
73
  export declare function useStudioBreadcrumbs(): BreadcrumbsController;
74
+ /** What the host can do — see {@link StudioCapabilities}. */
75
+ export declare function useStudioCapabilities(): StudioCapabilities;
49
76
  /**
50
77
  * Registry that controllers use to self-register their implementations
51
78
  * into the Studio bridge. Each controller calls `register(key, value)`
package/dist/index.es.js CHANGED
@@ -5,11 +5,12 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  import { SnackbarProvider as SnackbarProvider$1, useSnackbar } from "notistack";
6
6
  import { buildRebaseData, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, evaluateCondition, getEntityChildViews, getLabelOrConfigFrom, getPrimaryKeys, getSubcollections, isPropertyBuilder, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, stripCollectionPath, wrapAsEntityData, wrapAsSdkData } from "@rebasepro/common";
7
7
  import { getTitlePropertyKey as getTitlePropertyKey$1, removeInitialAndTrailingSlashes as removeInitialAndTrailingSlashes$1 } from "@rebasepro/app";
8
- import { Link, RouterProvider, Routes, createBrowserRouter, useBlocker, useLocation } from "react-router-dom";
8
+ import { Link, Routes, createBrowserRouter, useBlocker, useLocation } from "react-router";
9
9
  import { generateForeignKeyName, hashString, isObject, isPlainObject, mergeDeep, randomString, slugify } from "@rebasepro/utils";
10
10
  import { I18nextProvider, initReactI18next, useTranslation as useTranslation$1 } from "react-i18next";
11
11
  import { deepEqual } from "fast-equals";
12
12
  import i18next from "i18next";
13
+ import { RouterProvider } from "react-router/dom";
13
14
  import Fuse from "fuse.js";
14
15
  import Compressor from "compressorjs";
15
16
  import { resolveAdminCollection } from "@rebasepro/admin-types";
@@ -162,12 +163,15 @@ var DialogsProvider = ({ children }) => {
162
163
  };
163
164
  const close = useCallback(() => {
164
165
  if (dialogEntries.length === 0) return;
165
- updateDialogEntries([...dialogEntriesRef.current.slice(0, -1)]);
166
+ const updatedPanels = [...dialogEntriesRef.current.slice(0, -1)];
167
+ updateDialogEntries(updatedPanels);
166
168
  }, [dialogEntries]);
167
169
  const open = useCallback((dialogEntry) => {
168
- updateDialogEntries([...dialogEntriesRef.current, dialogEntry]);
170
+ const updatedPanels = [...dialogEntriesRef.current, dialogEntry];
171
+ updateDialogEntries(updatedPanels);
169
172
  return { closeDialog: () => {
170
- updateDialogEntries(dialogEntriesRef.current.filter((e) => e.key !== dialogEntry.key));
173
+ const updatedPanels = dialogEntriesRef.current.filter((e) => e.key !== dialogEntry.key);
174
+ updateDialogEntries(updatedPanels);
171
175
  } };
172
176
  }, [dialogEntries]);
173
177
  const dialogsController = useMemo(() => ({
@@ -908,7 +912,8 @@ function useRelationSelector({ path, collection, fixedFilter, pageSize = DEFAULT
908
912
  setLoading(true);
909
913
  const whereParams = fixedFilter && Object.keys(fixedFilter).length > 0 ? fixedFilter : void 0;
910
914
  const onEntitiesUpdate = (res) => {
911
- setItems(res.data.map((e) => entityToRelationItem(e)));
915
+ const newItems = res.data.map((e) => entityToRelationItem(e));
916
+ setItems(newItems);
912
917
  setHasMore(res.meta.hasMore);
913
918
  setLoading(false);
914
919
  };
@@ -1201,14 +1206,38 @@ function useUnsavedChangesDialog(when, onOk) {
1201
1206
  function useAuthSubscription(authClient) {
1202
1207
  const currentSession = authClient?.getSession();
1203
1208
  const [user, setUser] = useState(currentSession?.user ?? null);
1204
- const [initialLoading, setInitialLoading] = useState(!currentSession);
1205
1209
  const [authLoading, setAuthLoading] = useState(false);
1206
1210
  const [authError, setAuthError] = useState();
1207
1211
  const [loginSkipped, setLoginSkipped] = useState(false);
1208
1212
  const [extra, setExtra] = useState();
1213
+ /**
1214
+ * Whether asking the server "who am I?" could possibly answer.
1215
+ *
1216
+ * A client configured with `persistSession: false` and JSON auth — the
1217
+ * shape used when one client borrows another's credential, as the hosted
1218
+ * Studio does — has no stored session and no auth cookie, so the probe
1219
+ * below is guaranteed to 401. Sending it anyway put three failing
1220
+ * `GET /auth/me` calls on the wire per mount, each one landing in the
1221
+ * *customer's* request log as an authentication failure against their own
1222
+ * backend.
1223
+ *
1224
+ * A client that does not implement the capability is treated as "might
1225
+ * work" — the historical behaviour.
1226
+ */
1227
+ const mayHaveRestorableSession = authClient?.canRestoreSession?.() ?? true;
1228
+ /**
1229
+ * `true` only while a probe is genuinely outstanding.
1230
+ *
1231
+ * It used to be seeded from `!currentSession` alone, which left a client
1232
+ * that cannot restore a session — and therefore never runs the probe that
1233
+ * clears the flag — reporting "still loading" forever. Anything gated on
1234
+ * `initialLoading` (a spinner, a redirect to the login view) would never
1235
+ * resolve.
1236
+ */
1237
+ const [initialLoading, setInitialLoading] = useState(!currentSession && mayHaveRestorableSession);
1209
1238
  useEffect(() => {
1210
1239
  if (!authClient) return;
1211
- if (!currentSession) {
1240
+ if (!currentSession && mayHaveRestorableSession) {
1212
1241
  setInitialLoading(true);
1213
1242
  authClient.getUser().then((user) => {
1214
1243
  if (user) setUser(user);
@@ -1216,7 +1245,11 @@ function useAuthSubscription(authClient) {
1216
1245
  setInitialLoading(false);
1217
1246
  });
1218
1247
  }
1219
- }, [authClient, currentSession]);
1248
+ }, [
1249
+ authClient,
1250
+ currentSession,
1251
+ mayHaveRestorableSession
1252
+ ]);
1220
1253
  useEffect(() => {
1221
1254
  if (!authClient) return;
1222
1255
  return authClient.onAuthStateChange((event, session) => {
@@ -1751,6 +1784,60 @@ function useRebaseRegistryDispatch() {
1751
1784
  return dispatch;
1752
1785
  }
1753
1786
  //#endregion
1787
+ //#region src/hooks/ApiConfigContext.tsx
1788
+ /** What the server uses when `basePath` is not configured. */
1789
+ var DEFAULT_API_PATH = "/api";
1790
+ var ApiConfigContext = React.createContext(void 0);
1791
+ /**
1792
+ * Read the API config from context. Returns `undefined` if no provider is present,
1793
+ * allowing hooks to fall back to their own props.
1794
+ */
1795
+ function useApiConfig() {
1796
+ return useContext(ApiConfigContext);
1797
+ }
1798
+ /**
1799
+ * `apiUrl` and `apiPath` joined, with no trailing slash — the prefix to build a
1800
+ * request URL from.
1801
+ *
1802
+ * Returns `undefined` when there is no provider, so a caller can keep its own
1803
+ * fallback rather than silently requesting a relative path.
1804
+ */
1805
+ function useApiBase() {
1806
+ const config = useApiConfig();
1807
+ if (!config?.apiUrl) return void 0;
1808
+ return config.apiUrl.replace(/\/+$/, "") + (config.apiPath || "/api");
1809
+ }
1810
+ /**
1811
+ * The same prefix, derived from a client rather than from context.
1812
+ *
1813
+ * For the code that holds a `RebaseClient` but sits outside (or above) an
1814
+ * `ApiConfigProvider`. Returns `undefined` when the client has no base URL,
1815
+ * which is the signal every caller already treats as "cannot build a request".
1816
+ */
1817
+ function apiBaseOf(client) {
1818
+ if (!client?.baseUrl) return void 0;
1819
+ return client.baseUrl.replace(/\/+$/, "") + (client.apiPath || "/api");
1820
+ }
1821
+ /**
1822
+ * Provide API configuration (apiUrl, apiPath, getAuthToken) to the entire subtree.
1823
+ * Typically rendered inside `<Rebase>` or at the app root.
1824
+ */
1825
+ function ApiConfigProvider({ apiUrl, apiPath = DEFAULT_API_PATH, getAuthToken, children }) {
1826
+ const value = React.useMemo(() => ({
1827
+ apiUrl,
1828
+ apiPath,
1829
+ getAuthToken
1830
+ }), [
1831
+ apiUrl,
1832
+ apiPath,
1833
+ getAuthToken
1834
+ ]);
1835
+ return /* @__PURE__ */ jsx(ApiConfigContext.Provider, {
1836
+ value,
1837
+ children
1838
+ });
1839
+ }
1840
+ //#endregion
1754
1841
  //#region src/hooks/useBackendStorageSource.ts
1755
1842
  /**
1756
1843
  * React hook for using backend storage API as a StorageSource
@@ -1770,8 +1857,8 @@ function useRebaseRegistryDispatch() {
1770
1857
  * <Rebase storageSource={storageSource} ... />
1771
1858
  * ```
1772
1859
  */
1773
- function useBackendStorageSource({ apiUrl, getAuthToken }) {
1774
- const storageBasePath = `${apiUrl}/api/storage`;
1860
+ function useBackendStorageSource({ apiUrl, apiPath = DEFAULT_API_PATH, getAuthToken }) {
1861
+ const storageBasePath = `${apiUrl.replace(/\/+$/, "")}${apiPath}/storage`;
1775
1862
  const urlsCache = useMemo(() => /* @__PURE__ */ new Map(), []);
1776
1863
  /**
1777
1864
  * Make an authenticated request to the storage API
@@ -1933,30 +2020,6 @@ function usePermissions() {
1933
2020
  ]);
1934
2021
  }
1935
2022
  //#endregion
1936
- //#region src/hooks/ApiConfigContext.tsx
1937
- var ApiConfigContext = React.createContext(void 0);
1938
- /**
1939
- * Read the API config from context. Returns `undefined` if no provider is present,
1940
- * allowing hooks to fall back to their own props.
1941
- */
1942
- function useApiConfig() {
1943
- return useContext(ApiConfigContext);
1944
- }
1945
- /**
1946
- * Provide API configuration (apiUrl, getAuthToken) to the entire subtree.
1947
- * Typically rendered inside `<Rebase>` or at the app root.
1948
- */
1949
- function ApiConfigProvider({ apiUrl, getAuthToken, children }) {
1950
- const value = React.useMemo(() => ({
1951
- apiUrl,
1952
- getAuthToken
1953
- }), [apiUrl, getAuthToken]);
1954
- return /* @__PURE__ */ jsx(ApiConfigContext.Provider, {
1955
- value,
1956
- children
1957
- });
1958
- }
1959
- //#endregion
1960
2023
  //#region src/hooks/useTranslation.ts
1961
2024
  var REBASE_NS$1 = "rebase_core";
1962
2025
  /**
@@ -6318,7 +6381,7 @@ function KanbanBoardDemo() {
6318
6381
  const [columns, setColumns] = useState(STATUS_COLUMNS);
6319
6382
  const assignColumn = useCallback((item) => item.data.status, []);
6320
6383
  const handleItemsReorder = useCallback((items, moveInfo) => {
6321
- setBoardData(items.map((item) => {
6384
+ const updatedItems = items.map((item) => {
6322
6385
  if (moveInfo && item.id === moveInfo.itemId && moveInfo.sourceColumn !== moveInfo.targetColumn) return {
6323
6386
  ...item,
6324
6387
  data: {
@@ -6328,7 +6391,8 @@ function KanbanBoardDemo() {
6328
6391
  }
6329
6392
  };
6330
6393
  return item;
6331
- }));
6394
+ });
6395
+ setBoardData(updatedItems);
6332
6396
  }, []);
6333
6397
  const handleColumnReorder = useCallback((reordered) => setColumns(reordered), []);
6334
6398
  const columnLoadingState = useMemo(() => {
@@ -7563,6 +7627,22 @@ function UIReferenceView() {
7563
7627
  disabled: true,
7564
7628
  value: "Read only",
7565
7629
  onChange: () => {}
7630
+ }),
7631
+ /* @__PURE__ */ jsx(TextField, {
7632
+ size: "medium",
7633
+ label: "Medium",
7634
+ placeholder: "Placeholder under the label"
7635
+ }),
7636
+ /* @__PURE__ */ jsx(TextField, {
7637
+ size: "small",
7638
+ label: "Small",
7639
+ placeholder: "Placeholder under the label"
7640
+ }),
7641
+ /* @__PURE__ */ jsx(TextField, {
7642
+ size: "smallest",
7643
+ label: "Smallest",
7644
+ value: "Filled value",
7645
+ onChange: () => {}
7566
7646
  })
7567
7647
  ]
7568
7648
  })]
@@ -8336,8 +8416,10 @@ function UserSettingsView() {
8336
8416
  setLoadingSessions(true);
8337
8417
  setSessionsError(null);
8338
8418
  try {
8339
- if (authController.fetchSessions) setSessions((await authController.fetchSessions() || []).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()));
8340
- else throw new Error("fetchSessions not implemented in this auth controller.");
8419
+ if (authController.fetchSessions) {
8420
+ const sortedSessions = (await authController.fetchSessions() || []).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
8421
+ setSessions(sortedSessions);
8422
+ } else throw new Error("fetchSessions not implemented in this auth controller.");
8341
8423
  } catch (e) {
8342
8424
  setSessionsError(e instanceof Error ? e.message : String(e));
8343
8425
  } finally {
@@ -15852,6 +15934,7 @@ function Rebase(props) {
15852
15934
  const resolvedApiUrl = apiUrl || client?.baseUrl || (typeof window !== "undefined" ? window.location.origin : "");
15853
15935
  if (resolvedApiUrl) return /* @__PURE__ */ jsx(ApiConfigProvider, {
15854
15936
  apiUrl: resolvedApiUrl,
15937
+ apiPath: client?.apiPath,
15855
15938
  getAuthToken: authController.getAuthToken,
15856
15939
  children: content
15857
15940
  });
@@ -15998,7 +16081,8 @@ function useRebaseAuthController(props = {}) {
15998
16081
  try {
15999
16082
  await auth.signOut();
16000
16083
  } catch (error) {
16001
- setAuthProviderError(error instanceof Error ? error : new Error(String(error)));
16084
+ const err = error instanceof Error ? error : new Error(String(error));
16085
+ setAuthProviderError(err);
16002
16086
  } finally {
16003
16087
  setAuthLoading(false);
16004
16088
  }
@@ -16010,7 +16094,8 @@ function useRebaseAuthController(props = {}) {
16010
16094
  try {
16011
16095
  await auth.signInWithEmail(email, password);
16012
16096
  } catch (error) {
16013
- setAuthProviderError(error instanceof Error ? error : new Error(String(error)));
16097
+ const err = error instanceof Error ? error : new Error(String(error));
16098
+ setAuthProviderError(err);
16014
16099
  throw error;
16015
16100
  } finally {
16016
16101
  setAuthLoading(false);
@@ -16023,7 +16108,8 @@ function useRebaseAuthController(props = {}) {
16023
16108
  try {
16024
16109
  await auth.signUp(email, password, displayName);
16025
16110
  } catch (error) {
16026
- setAuthProviderError(error instanceof Error ? error : new Error(String(error)));
16111
+ const err = error instanceof Error ? error : new Error(String(error));
16112
+ setAuthProviderError(err);
16027
16113
  throw error;
16028
16114
  } finally {
16029
16115
  setAuthLoading(false);
@@ -16036,7 +16122,8 @@ function useRebaseAuthController(props = {}) {
16036
16122
  try {
16037
16123
  await auth.signInWithGoogle(payload);
16038
16124
  } catch (error) {
16039
- setAuthProviderError(error instanceof Error ? error : new Error(String(error)));
16125
+ const err = error instanceof Error ? error : new Error(String(error));
16126
+ setAuthProviderError(err);
16040
16127
  throw error;
16041
16128
  } finally {
16042
16129
  setAuthLoading(false);
@@ -16049,7 +16136,8 @@ function useRebaseAuthController(props = {}) {
16049
16136
  try {
16050
16137
  await auth.signInWithOAuth(providerId, payload);
16051
16138
  } catch (error) {
16052
- setAuthProviderError(error instanceof Error ? error : new Error(String(error)));
16139
+ const err = error instanceof Error ? error : new Error(String(error));
16140
+ setAuthProviderError(err);
16053
16141
  throw error;
16054
16142
  } finally {
16055
16143
  setAuthLoading(false);
@@ -16066,7 +16154,8 @@ function useRebaseAuthController(props = {}) {
16066
16154
  try {
16067
16155
  await auth.resetPasswordForEmail(email);
16068
16156
  } catch (error) {
16069
- setAuthProviderError(error instanceof Error ? error : new Error(String(error)));
16157
+ const err = error instanceof Error ? error : new Error(String(error));
16158
+ setAuthProviderError(err);
16070
16159
  throw error;
16071
16160
  } finally {
16072
16161
  setAuthLoading(false);
@@ -16079,7 +16168,8 @@ function useRebaseAuthController(props = {}) {
16079
16168
  try {
16080
16169
  await auth.resetPassword(token, password);
16081
16170
  } catch (error) {
16082
- setAuthProviderError(error instanceof Error ? error : new Error(String(error)));
16171
+ const err = error instanceof Error ? error : new Error(String(error));
16172
+ setAuthProviderError(err);
16083
16173
  throw error;
16084
16174
  } finally {
16085
16175
  setAuthLoading(false);
@@ -16093,7 +16183,8 @@ function useRebaseAuthController(props = {}) {
16093
16183
  await auth.changePassword(oldPassword, newPassword);
16094
16184
  await auth.signOut();
16095
16185
  } catch (error) {
16096
- setAuthProviderError(error instanceof Error ? error : new Error(String(error)));
16186
+ const err = error instanceof Error ? error : new Error(String(error));
16187
+ setAuthProviderError(err);
16097
16188
  throw error;
16098
16189
  } finally {
16099
16190
  setAuthLoading(false);
@@ -16109,7 +16200,8 @@ function useRebaseAuthController(props = {}) {
16109
16200
  photoURL
16110
16201
  });
16111
16202
  } catch (error) {
16112
- setAuthProviderError(error instanceof Error ? error : new Error(String(error)));
16203
+ const err = error instanceof Error ? error : new Error(String(error));
16204
+ setAuthProviderError(err);
16113
16205
  throw error;
16114
16206
  } finally {
16115
16207
  setAuthLoading(false);
@@ -16238,11 +16330,11 @@ function createAuthConfigCache() {
16238
16330
  * Concurrent calls are deduplicated: only one network request is made
16239
16331
  * and all callers share the same promise.
16240
16332
  */
16241
- async function fetchAuthConfig(apiUrl, cache) {
16333
+ async function fetchAuthConfig(apiUrl, cache, apiPath = DEFAULT_API_PATH) {
16242
16334
  if (cache.cached) return cache.cached;
16243
16335
  if (cache.inflight) return cache.inflight;
16244
16336
  cache.inflight = (async () => {
16245
- return handleResponse(await fetchWithHandling(`${apiUrl}/api/auth/config`, {
16337
+ return handleResponse(await fetchWithHandling(`${apiUrl.replace(/\/+$/, "")}${apiPath}/auth/config`, {
16246
16338
  method: "GET",
16247
16339
  headers: { "Content-Type": "application/json" }
16248
16340
  }));
@@ -16660,7 +16752,8 @@ function useStorageUploadController({ entityId, entityValues, path, value, prope
16660
16752
  ]),
16661
16753
  onFileUploadError: useCallback((entry) => {
16662
16754
  console.debug("onFileUploadError", entry);
16663
- setInternalValue(internalValue.filter((item) => item.id !== entry.id));
16755
+ const newValue = internalValue.filter((item) => item.id !== entry.id);
16756
+ setInternalValue(newValue);
16664
16757
  }, [internalValue]),
16665
16758
  onFilesAdded: useCallback(async (acceptedFiles) => {
16666
16759
  if (!acceptedFiles.length || disabled) return;
@@ -16935,7 +17028,8 @@ var NOOP_BRIDGE = {
16935
17028
  breadcrumbs: [],
16936
17029
  set: () => {},
16937
17030
  updateCount: () => {}
16938
- }
17031
+ },
17032
+ capabilities: { codebase: true }
16939
17033
  };
16940
17034
  var StudioBridgeContext = createContext(NOOP_BRIDGE);
16941
17035
  /**
@@ -16959,7 +17053,14 @@ function StudioBridgeProvider({ value, children }) {
16959
17053
  const merged = React.useMemo(() => ({
16960
17054
  ...NOOP_BRIDGE,
16961
17055
  ...value
16962
- }), [value]);
17056
+ }), [
17057
+ value.collectionRegistry,
17058
+ value.sidePanelController,
17059
+ value.urlController,
17060
+ value.navigationState,
17061
+ value.breadcrumbs,
17062
+ value.capabilities
17063
+ ]);
16963
17064
  return /* @__PURE__ */ jsx(StudioBridgeContext.Provider, {
16964
17065
  value: merged,
16965
17066
  children
@@ -16985,6 +17086,10 @@ function useStudioNavigationState() {
16985
17086
  function useStudioBreadcrumbs() {
16986
17087
  return useContext(StudioBridgeContext).breadcrumbs;
16987
17088
  }
17089
+ /** What the host can do — see {@link StudioCapabilities}. */
17090
+ function useStudioCapabilities() {
17091
+ return useContext(StudioBridgeContext).capabilities;
17092
+ }
16988
17093
  var StudioBridgeRegistryContext = createContext(null);
16989
17094
  /**
16990
17095
  * Provider that creates a self-assembling bridge.
@@ -17600,7 +17705,7 @@ var SCORE = {
17600
17705
  * This is a *bonus* on top of the structural rules — never a requirement, and
17601
17706
  * never the mechanism that keeps identifiers out of the title slot.
17602
17707
  */
17603
- var TITLE_LIKE_KEYS = new Set([
17708
+ var TITLE_LIKE_KEYS = /* @__PURE__ */ new Set([
17604
17709
  "name",
17605
17710
  "fullname",
17606
17711
  "displayname",
@@ -17779,6 +17884,6 @@ function getTitlePropertyKeyForValues(collection, values, entityId) {
17779
17884
  return candidates[0];
17780
17885
  }
17781
17886
  //#endregion
17782
- export { ADDITIONAL_TAB_WIDTH, AIIcon, AIModifiedIndicator, AdminModeControllerContext, AdminModeControllerProvider, AnalyticsContext, ApiConfigProvider, AuthApiError, AuthControllerContext, CONTAINER_FULL_WIDTH, CollectionComponentOverrideProvider, CollectionResolverRegistrationContext, CollectionScopeContext, CollectionScopeProvider, ComponentOverrideContext, ConfirmationDialog, CrmDashboardDemo, CustomizationControllerContext, DEFAULT_PAGE_SIZE, DataDriverContext, DataSourcesContext, DialogsControllerContext, DialogsProvider, EffectiveRoleControllerContext, EffectiveRoleControllerProvider, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, GlobalComponentOverrideProvider, IconForView, LanguageToggle, LoginView, ModeControllerContext, ModeControllerProvider, NavigationBlockerProvider, NotFoundPage, PluginProviderStack, REBASE_LOCALE_STORAGE_KEY, Rebase, RebaseAuth, RebaseClientInstanceContext, RebaseDataContext, RebaseI18nProvider, RebaseLogo, RebaseRegistryProvider, RebaseRouter, RebaseRoutes, STUDIO_NAVIGATION_GROUPS, SchemaDriftBanner, SchemaDriftProvider, SnackbarProvider, StorageSourceContext, StorageSourcesContext, StudioBridgeContext, StudioBridgeProvider, StudioBridgeRegistryContext, StudioBridgeRegistryProvider, UIReferenceView, UIStyleGuide, UnsavedChangesDialog, UserConfigurationPersistenceContext, UserDisplay, UserSelectPopover, UserSettingsView, addInitialSlash, applyPropertyConditions, buildCollapsedDefaults, buildEnumLabel, clearAuthConfigCache, clearFetchCache, createAuthConfigCache, createFormexStub, deleteEntityWithCallbacks, en, es, fetchAuthConfig, flattenKeys, getAdminEntityChildViews, getAdminSubcollections, getCollectionBySlugWithin, getCollectionPathsCombinations, getColorScheme, getColumnKeysForProperty, getEntityFromCache, getEntityFromMemoryCache, getEntityImagePreviewPropertyKey, getEntityPreviewKeys, getEntityTitlePropertyKey, getFormFieldKeys, getIcon, getLastSegment, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getRelationIncludeParams, getRowHeight, getSubcollectionColumnId, getTitlePropertyCandidates, getTitlePropertyKey, getTitlePropertyKeyForValues, iconsSearch, isEnumValueDisabled, isFilterableRelation, isHidden, isReadOnly, isSchemaDriftError, looksLikeIdentifierValue, populateFetchCache, removeEntityFromCache, removeEntityFromMemoryCache, removeInitialAndTrailingSlashes, removeInitialSlash, removeTrailingSlash, resolveCollectionPathIds, resolveComponentRef, resolveDefaultSelectedView, resolveFilterOperators, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAdminModeController, useAnalyticsController, useApiConfig, useAuthController, useAuthSubscription, useBackendStorageSource, useBridgeRegistration, useBrowserTitleAndIcon, useBuildAdminModeController, useBuildEffectiveRoleController, useBuildLocalConfigurationPersistence, useBuildModeController, useClipboard, useCollapsedGroups, useCollection, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataSources, useDataTableController, useDebouncedData, useDialogsController, useEffectiveRoleController, useFetch, useLargeLayout, useModeController, useNavigationBlocker, usePermissions, useRebaseAuthController, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRelationSelector, useResolvedComponent, useRestoreScroll, useSchemaDriftContext, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useStudioBreadcrumbs, useStudioCollectionRegistry, useStudioNavigationState, useStudioSidePanelController, useStudioUrlController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence };
17887
+ export { ADDITIONAL_TAB_WIDTH, AIIcon, AIModifiedIndicator, AdminModeControllerContext, AdminModeControllerProvider, AnalyticsContext, ApiConfigProvider, AuthApiError, AuthControllerContext, CONTAINER_FULL_WIDTH, CollectionComponentOverrideProvider, CollectionResolverRegistrationContext, CollectionScopeContext, CollectionScopeProvider, ComponentOverrideContext, ConfirmationDialog, CrmDashboardDemo, CustomizationControllerContext, DEFAULT_API_PATH, DEFAULT_PAGE_SIZE, DataDriverContext, DataSourcesContext, DialogsControllerContext, DialogsProvider, EffectiveRoleControllerContext, EffectiveRoleControllerProvider, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, GlobalComponentOverrideProvider, IconForView, LanguageToggle, LoginView, ModeControllerContext, ModeControllerProvider, NavigationBlockerProvider, NotFoundPage, PluginProviderStack, REBASE_LOCALE_STORAGE_KEY, Rebase, RebaseAuth, RebaseClientInstanceContext, RebaseDataContext, RebaseI18nProvider, RebaseLogo, RebaseRegistryProvider, RebaseRouter, RebaseRoutes, STUDIO_NAVIGATION_GROUPS, SchemaDriftBanner, SchemaDriftProvider, SnackbarProvider, StorageSourceContext, StorageSourcesContext, StudioBridgeContext, StudioBridgeProvider, StudioBridgeRegistryContext, StudioBridgeRegistryProvider, UIReferenceView, UIStyleGuide, UnsavedChangesDialog, UserConfigurationPersistenceContext, UserDisplay, UserSelectPopover, UserSettingsView, addInitialSlash, apiBaseOf, applyPropertyConditions, buildCollapsedDefaults, buildEnumLabel, clearAuthConfigCache, clearFetchCache, createAuthConfigCache, createFormexStub, deleteEntityWithCallbacks, en, es, fetchAuthConfig, flattenKeys, getAdminEntityChildViews, getAdminSubcollections, getCollectionBySlugWithin, getCollectionPathsCombinations, getColorScheme, getColumnKeysForProperty, getEntityFromCache, getEntityFromMemoryCache, getEntityImagePreviewPropertyKey, getEntityPreviewKeys, getEntityTitlePropertyKey, getFormFieldKeys, getIcon, getLastSegment, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getRelationIncludeParams, getRowHeight, getSubcollectionColumnId, getTitlePropertyCandidates, getTitlePropertyKey, getTitlePropertyKeyForValues, iconsSearch, isEnumValueDisabled, isFilterableRelation, isHidden, isReadOnly, isSchemaDriftError, looksLikeIdentifierValue, populateFetchCache, removeEntityFromCache, removeEntityFromMemoryCache, removeInitialAndTrailingSlashes, removeInitialSlash, removeTrailingSlash, resolveCollectionPathIds, resolveComponentRef, resolveDefaultSelectedView, resolveFilterOperators, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAdminModeController, useAnalyticsController, useApiBase, useApiConfig, useAuthController, useAuthSubscription, useBackendStorageSource, useBridgeRegistration, useBrowserTitleAndIcon, useBuildAdminModeController, useBuildEffectiveRoleController, useBuildLocalConfigurationPersistence, useBuildModeController, useClipboard, useCollapsedGroups, useCollection, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataSources, useDataTableController, useDebouncedData, useDialogsController, useEffectiveRoleController, useFetch, useLargeLayout, useModeController, useNavigationBlocker, usePermissions, useRebaseAuthController, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRelationSelector, useResolvedComponent, useRestoreScroll, useSchemaDriftContext, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useStudioBreadcrumbs, useStudioCapabilities, useStudioCollectionRegistry, useStudioNavigationState, useStudioSidePanelController, useStudioUrlController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence };
17783
17888
 
17784
17889
  //# sourceMappingURL=index.es.js.map