@rebasepro/app 0.22.0 → 0.24.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.
@@ -8,7 +8,7 @@
8
8
  * here is the unauthenticated `/api/auth/config` probe used to detect
9
9
  * bootstrap mode and enabled providers before a session exists.
10
10
  */
11
- import type { AuthAdapterCapabilities } from "@rebasepro/types";
11
+ import type { AuthConfigDocument } from "@rebasepro/types";
12
12
  /**
13
13
  * What `GET /api/auth/config` answers.
14
14
  *
@@ -17,7 +17,7 @@ import type { AuthAdapterCapabilities } from "@rebasepro/types";
17
17
  * this one listed an `emailServiceEnabled` flag no backend sends, and marked as
18
18
  * optional fields every backend always returns.
19
19
  */
20
- export type AuthConfigResponse = AuthAdapterCapabilities;
20
+ export type AuthConfigResponse = AuthConfigDocument;
21
21
  /**
22
22
  * Cache container for `fetchAuthConfig` — holds both the inflight promise
23
23
  * (to deduplicate concurrent calls) and the cached result.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Empty every module-level cache that holds data read as the signed-in user:
3
+ * fetched records, each table's scroll position and the rows kept with it,
4
+ * edits parked for a layout handoff, and resolved display values (the titles
5
+ * on relation chips and breadcrumbs).
6
+ *
7
+ * All of them are keyed by path and id only, with no user in the key, and the
8
+ * views seed their first render from them — so without this the next user to
9
+ * sign in to the tab is shown what the last one could read. Called by both auth
10
+ * controllers on `SIGNED_OUT`.
11
+ *
12
+ * The local-changes backup is not cleared here: a sign-out is also how a
13
+ * rejected refresh token ends a session, and the backup exists for that user
14
+ * coming back. {@link bindSessionCachesToUser} drops it when somebody else
15
+ * signs in instead.
16
+ */
17
+ export declare function clearSessionCaches(): void;
18
+ /**
19
+ * Record that the tab now acts as `userId`. When that is not who the caches
20
+ * were filled for, everything goes — the session caches, and the drafts the
21
+ * other user left in the local-changes backup.
22
+ *
23
+ * Call it before the new user is rendered: the views read these caches in
24
+ * their first render, before any effect of the auth controller would run.
25
+ */
26
+ export declare function bindSessionCachesToUser(userId: string): void;
@@ -21,7 +21,14 @@ export type RebaseAuthController = AuthController & {
21
21
  /** Login with email and password */
22
22
  emailPasswordLogin: (email: string, password: string) => Promise<void>;
23
23
  /** Register a new user */
24
- register: (email: string, password: string, displayName?: string) => Promise<void>;
24
+ /**
25
+ * Create an account. `confirmationRequired` when the backend registers
26
+ * confirm-first (`requireEmailVerification`): nobody is signed in until
27
+ * the address is confirmed.
28
+ */
29
+ register: (email: string, password: string, displayName?: string) => Promise<{
30
+ confirmationRequired: boolean;
31
+ }>;
25
32
  /** Skip login (for anonymous access if enabled) */
26
33
  skipLogin: () => void;
27
34
  /** Whether login was skipped */
@@ -36,8 +43,33 @@ export type RebaseAuthController = AuthController & {
36
43
  forgotPassword: (email: string) => Promise<void>;
37
44
  /** Reset password using token from email */
38
45
  resetPassword: (token: string, password: string) => Promise<void>;
46
+ /**
47
+ * Confirm an email address using the token from a verification email.
48
+ * Refused with `PROOF_REQUIRED` when the account holds a password this
49
+ * call does not prove: pass `password` to keep it (and sign in), or
50
+ * `removeUnproven` to verify without it.
51
+ */
52
+ verifyEmail: (token: string, options?: {
53
+ password?: string;
54
+ removeUnproven?: boolean;
55
+ }) => Promise<{
56
+ passwordRemoved: boolean;
57
+ signedIn: boolean;
58
+ }>;
59
+ /** Sign in with a magic link's token. Absent when the client cannot. */
60
+ magicLinkLogin?: (token: string) => Promise<void>;
39
61
  /** Change password for authenticated user */
40
62
  changePassword: (oldPassword: string, newPassword: string) => Promise<void>;
63
+ /** Ask to move the signed-in account to another address. Absent when the client cannot. */
64
+ changeEmail?: (newEmail: string) => Promise<{
65
+ pendingEmail: string;
66
+ expiresAt: string;
67
+ }>;
68
+ /** Follow an address-change link. Absent when the client cannot. */
69
+ confirmEmailChange?: (token: string) => Promise<{
70
+ email: string;
71
+ removedProviders: string[];
72
+ }>;
41
73
  /** Update user profile */
42
74
  updateProfile: (displayName?: string, photoURL?: string) => Promise<User>;
43
75
  /** Fetch active sessions */
@@ -52,6 +84,18 @@ export type RebaseAuthController = AuthController & {
52
84
  clearError: () => void;
53
85
  /** Set or clear the auth provider error */
54
86
  setAuthProviderError: (error: Error | null) => void;
87
+ /**
88
+ * Open a challenge to finish a sign-in refused with `MFA_REQUIRED`. See
89
+ * `AuthControllerExtended`. Absent when the client has no MFA support.
90
+ */
91
+ startMfaChallenge?: (mfaToken: string, factorId: string) => Promise<string>;
92
+ /**
93
+ * Answer that challenge; the user is signed in on success. Absent when
94
+ * the client has no MFA support.
95
+ */
96
+ verifyMfaChallenge?: (mfaToken: string, challengeId: string, code: string) => Promise<void>;
97
+ /** The scopes the signed-in user holds; `undefined` until known. */
98
+ heldScopes?: string[];
55
99
  };
56
100
  /**
57
101
  * Structural type for the subset of `client.auth` (from `@rebasepro/client`)
@@ -78,7 +122,24 @@ export interface ClientAuth {
78
122
  signInWithOAuth(providerId: string, payload: Record<string, unknown>): Promise<unknown>;
79
123
  resetPasswordForEmail(email: string): Promise<unknown>;
80
124
  resetPassword(token: string, password: string): Promise<unknown>;
125
+ /** Optional so a hand-built auth client need not implement it. */
126
+ verifyEmail?(token: string, options?: {
127
+ password?: string;
128
+ removeUnproven?: boolean;
129
+ }): Promise<unknown>;
81
130
  changePassword(oldPassword: string, newPassword: string): Promise<unknown>;
131
+ /** Optional so a hand-built auth client need not implement it. */
132
+ verifyMagicLink?(token: string): Promise<unknown>;
133
+ /** Optional so a hand-built auth client need not implement it. */
134
+ changeEmail?(newEmail: string): Promise<{
135
+ pendingEmail: string;
136
+ expiresAt: string;
137
+ }>;
138
+ /** Optional so a hand-built auth client need not implement it. */
139
+ confirmEmailChange?(token: string): Promise<{
140
+ email: string;
141
+ removedProviders: string[];
142
+ }>;
82
143
  updateUser(updates: {
83
144
  displayName?: string;
84
145
  photoURL?: string;
@@ -86,6 +147,22 @@ export interface ClientAuth {
86
147
  getSessions(): Promise<DeviceSession[]>;
87
148
  revokeSession(sessionId: string): Promise<unknown>;
88
149
  revokeAllSessions(): Promise<unknown>;
150
+ /**
151
+ * The challenge that finishes a sign-in refused with `MFA_REQUIRED`: both
152
+ * requests carry that refusal's `mfaToken`, and a verified one signs the
153
+ * user in. Optional so a hand-built auth client need not implement it;
154
+ * without it an account with a second factor cannot sign in here.
155
+ */
156
+ mfa?: {
157
+ challenge(factorId: string, options: {
158
+ mfaToken: string;
159
+ }): Promise<{
160
+ challengeId: string;
161
+ }>;
162
+ verifyChallenge(challengeId: string, code: string, options: {
163
+ mfaToken: string;
164
+ }): Promise<unknown>;
165
+ };
89
166
  }
90
167
  /**
91
168
  * Props for useRebaseAuthController hook
@@ -101,6 +178,12 @@ export interface RebaseAuthControllerProps {
101
178
  setAuthTokenGetter: (getter: () => Promise<string | null>) => void;
102
179
  };
103
180
  auth?: ClientAuth;
181
+ /** Read for the scopes the signed-in user holds (`GET /auth/scopes`). */
182
+ personalKeys?: {
183
+ listScopes(): Promise<{
184
+ held: string[];
185
+ }>;
186
+ };
104
187
  };
105
188
  /** Google OAuth client ID (optional, enables Google login) */
106
189
  googleClientId?: string;
@@ -16,6 +16,7 @@ import type { EntityDisplayRole } from "@rebasepro/cms-types";
16
16
  export type EntityDisplayKey = string;
17
17
  /** The identity of one role of one record, as a cache key. */
18
18
  export declare function entityDisplayKey(path: string, entityId: string | number | undefined, role: EntityDisplayRole): EntityDisplayKey;
19
+ export declare function getSharedEntityDisplayCache(): EntityDisplayCache;
19
20
  export declare class EntityDisplayCache {
20
21
  private readonly entries;
21
22
  private readonly listeners;
@@ -55,3 +55,21 @@ export interface ResolveFilterOperatorsParams {
55
55
  * @group Models
56
56
  */
57
57
  export declare function resolveFilterOperators({ property, isArray, engine }: ResolveFilterOperatorsParams): WhereFilterOp[];
58
+ /**
59
+ * Whether a property can be filtered at all — the one answer the table header
60
+ * and the filters dialog both give.
61
+ *
62
+ * They used to answer it with a predicate each, and disagreed: the header
63
+ * allowed an array only of strings, numbers, dates, references and relations;
64
+ * the dialog allowed an array of anything, and listed an array of maps with an
65
+ * empty control. The header, for its part, ignored both the developer's
66
+ * `filterOperators: []` and a custom `admin.Filter`.
67
+ *
68
+ * A property with a custom filter field is filterable: the developer owns that
69
+ * field. Otherwise it is filterable when at least one operator survives
70
+ * {@link resolveFilterOperators} — for an array, on its item type. A tuple
71
+ * array (`of: [a, b]`) has no single item type to filter by.
72
+ *
73
+ * @group Models
74
+ */
75
+ export declare function isPropertyFilterable(property: Property, engine?: string): boolean;
@@ -13,8 +13,14 @@ export declare function resolveCollectionPathIds(path: string, allCollections: A
13
13
  */
14
14
  export declare function getCollectionBySlugWithin(slugOrPath: string, collections: AdminCollection[]): AdminCollection | undefined;
15
15
  /**
16
- * Get the subcollection combinations from a path:
17
- * "sites/es/locales" => ["sites/es/locales", "sites"]
16
+ * Every leading run of whole segments of a path, longest first: the
17
+ * candidates for the collection the path starts with.
18
+ * "sites/es/locales" => ["sites/es/locales", "sites/es", "sites"]
19
+ *
20
+ * Every length, not only the odd ones. A slug may contain slashes, so
21
+ * `content/podcasts/abc123/episodes` starts with the two-segment collection
22
+ * `content/podcasts`, and a path's own segment count says nothing about where
23
+ * its first collection ends.
18
24
  * @param subpaths
19
25
  */
20
26
  export declare function getCollectionPathsCombinations(subpaths: string[]): string[];
@@ -3,23 +3,20 @@
3
3
  *
4
4
  * These read a property's admin block — `readOnly`, `disabled.hidden`, the
5
5
  * declarative `conditions` — and lived in `@rebasepro/common`, which is on the
6
- * backend's dependency path. Nothing in core ever called them: `isReadOnly` and
7
- * `isHidden` are used only by the panel, and `applyPropertyConditions` has no
8
- * production caller at all.
6
+ * backend's dependency path. Nothing in core ever called them: `isReadOnly`,
7
+ * `isHidden` and `applyPropertyConditions` are the panel's alone.
9
8
  *
10
- * That last one is worth knowing about rather than assuming: the collection editor
11
- * has a whole Conditions UI, `serializable_utils` persists what it writes, and
12
- * `BaseProperty.conditions` documents itself as "evaluated at runtime like property
13
- * builders" — but the evaluator below is reached only from its own tests. It lives
14
- * here because here is where it would be called from once it is wired up.
9
+ * `applyPropertyConditions` is called by the record form, through
10
+ * `resolveFormProperty`, both where a field is laid out and where the form is
11
+ * judged on save — so a rule the collection editor writes hides, disables and
12
+ * requires what it says it does, and a field it hides is not held against the
13
+ * save. Until that was wired, the evaluator was reached only from its own tests.
15
14
  *
16
- * The one part of `conditions` that *is* applied is the literal case:
17
- * `hidden`/`readOnly`/`disabled` stated as a plain boolean rather than as a rule.
18
- * A literal needs no context, so `isHidden`/`isReadOnly`/`isDisabled` can answer
19
- * it directly, and those three gates are consulted everywhere a field is laid
20
- * out. A *rule* still is not evaluated anywhere in production — the split is
21
- * deliberate, not an oversight: it is the difference between a condition that
22
- * needs an entity to be evaluated against and one that does not.
15
+ * A condition may also be a literal: `hidden`/`readOnly`/`disabled` stated as a
16
+ * plain boolean rather than as a rule. A literal needs no context, so
17
+ * `isHidden`/`isReadOnly`/`isDisabled` answer it directly, and those three
18
+ * gates are consulted everywhere a field is laid out — including places with
19
+ * no entity to evaluate a rule against.
23
20
  */
24
21
  import type { ConditionContext, Property } from "@rebasepro/types";
25
22
  export declare function isReadOnly(property: Property): boolean;
@@ -0,0 +1,31 @@
1
+ import React from "react";
2
+ import type { RealtimeConnectionState } from "@rebasepro/types";
3
+ /**
4
+ * The realtime connection's state, re-rendering on every change.
5
+ *
6
+ * `undefined` when there is no client, or it was built without realtime —
7
+ * there is no connection to describe.
8
+ *
9
+ * @group Hooks and utilities
10
+ */
11
+ export declare function useRealtimeConnectionState(): RealtimeConnectionState | undefined;
12
+ export interface ConnectionLostBannerProps {
13
+ className?: string;
14
+ }
15
+ /**
16
+ * Says that live updates have stopped, while they have.
17
+ *
18
+ * Shown once the realtime client reports the connection `disconnected`: the
19
+ * socket has been down for longer than a blip, so tables, boards and open
20
+ * records are showing what they last received and nothing else will arrive
21
+ * until it is back. They keep their data rather than turning into errors — this
22
+ * banner is how the user learns it may be out of date. It disappears by itself
23
+ * when the connection returns, and every live view then refreshes.
24
+ *
25
+ * A brief drop (`reconnecting`) shows nothing: a deploy blips every socket for
26
+ * a second or two, and a banner that flashes on each one teaches people to
27
+ * ignore it.
28
+ *
29
+ * @group Components
30
+ */
31
+ export declare function ConnectionLostBanner({ className }: ConnectionLostBannerProps): React.JSX.Element | null;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The links the backend puts in its auth emails, as the login view reads them.
3
+ *
4
+ * The server builds them from the configured frontend URL:
5
+ * `<frontend>/reset-password?token=…` for a forgotten password, an admin's
6
+ * "send reset email" and every invitation, `<frontend>/verify-email?token=…`
7
+ * to confirm an address, `<frontend>/confirm-email-change?token=…` to move an
8
+ * account to a new one, and `<frontend>/auth/magic-link?token=…` to sign in.
9
+ * The frontend URL may carry a base path (`/admin`), so the action is read off
10
+ * the *end* of the path, not the whole path.
11
+ */
12
+ export type EmailLinkAction = {
13
+ kind: "reset-password";
14
+ token: string;
15
+ } | {
16
+ kind: "verify-email";
17
+ token: string;
18
+ } | {
19
+ kind: "confirm-email-change";
20
+ token: string;
21
+ } | {
22
+ kind: "magic-link";
23
+ token: string;
24
+ };
25
+ export declare function readEmailLinkAction(location: {
26
+ pathname: string;
27
+ search: string;
28
+ }): EmailLinkAction | null;
29
+ /**
30
+ * Where the app lives: the link's own address without the action's path and
31
+ * its token. `/admin/reset-password?token=…` becomes `/admin/`, and
32
+ * `/admin/auth/magic-link?token=…` too.
33
+ */
34
+ export declare function appAddressOfEmailLink(location: {
35
+ origin: string;
36
+ pathname: string;
37
+ }): string;
@@ -1,2 +1,4 @@
1
1
  export { LoginView } from "./LoginView.js";
2
2
  export type { LoginViewProps } from "./LoginView.js";
3
+ export { readEmailLinkAction } from "./email-link.js";
4
+ export type { EmailLinkAction } from "./email-link.js";
@@ -0,0 +1,33 @@
1
+ import type { AuthControllerExtended, MfaFactorSummary } from "@rebasepro/cms-types";
2
+ /**
3
+ * A sign-in that is waiting for its second factor.
4
+ *
5
+ * The server refuses the first factor of an account with MFA enrolled with
6
+ * `401 MFA_REQUIRED`, and puts in `details` what the second step needs: a
7
+ * pending token, good for five minutes and for the two challenge routes only,
8
+ * and the factors the account can answer with.
9
+ */
10
+ export interface PendingMfaSignIn {
11
+ mfaToken: string;
12
+ factors: MfaFactorSummary[];
13
+ }
14
+ /**
15
+ * The pending sign-in an `MFA_REQUIRED` refusal describes, or `null` for any
16
+ * other error — including an `MFA_REQUIRED` without the token or a factor to
17
+ * answer with, which no code step could finish.
18
+ */
19
+ export declare function readMfaRequired(error: unknown): PendingMfaSignIn | null;
20
+ /** Whether this controller can take a sign-in through its second step. */
21
+ export declare function canAnswerMfa(authController: AuthControllerExtended): boolean;
22
+ /**
23
+ * What a refused challenge means for the code step.
24
+ *
25
+ * - `invalid-code`: the code was wrong; the same challenge takes another.
26
+ * - `exhausted`: the challenge has had its five attempts; the next code needs
27
+ * a new one.
28
+ * - `expired`: the sign-in itself is over. The pending token lasts five
29
+ * minutes from the password, and once it has lapsed both routes refuse the
30
+ * caller as unauthenticated; a challenge that lapsed, or a sign-in revoked
31
+ * meanwhile, ends the same way. Only a new sign-in gets a new token.
32
+ */
33
+ export declare function classifyMfaRefusal(error: unknown): "invalid-code" | "exhausted" | "expired" | "other";
@@ -28,6 +28,19 @@ export interface UserSelectPopoverProps {
28
28
  * Whether users are currently loading.
29
29
  */
30
30
  loading?: boolean;
31
+ /**
32
+ * Called with the search text each time it changes (debounced), for a
33
+ * parent that searches its users on a server rather than passing them all
34
+ * in `users`. The list is still filtered here by the same text.
35
+ */
36
+ onSearchTextChange?: (searchText: string) => void;
37
+ /**
38
+ * For a parent that lists only the first page of a server search: how many
39
+ * users that search matched in all. Leave it out when `users` holds every
40
+ * match. When set, the header counts the listed users against it and a
41
+ * notice says the others are not listed.
42
+ */
43
+ totalCount?: number;
31
44
  /**
32
45
  * The current user (displayed as the "self" option at the top).
33
46
  */
@@ -60,4 +73,4 @@ export interface UserSelectPopoverProps {
60
73
  *
61
74
  * @group Components
62
75
  */
63
- export declare function UserSelectPopover({ selectedUser, onUserSelected, users, loading, currentUser, defaultLabel, renderLimit, className, size }: UserSelectPopoverProps): React.JSX.Element;
76
+ export declare function UserSelectPopover({ selectedUser, onUserSelected, users, loading, onSearchTextChange, totalCount, currentUser, defaultLabel, renderLimit, className, size }: UserSelectPopoverProps): React.JSX.Element;
@@ -42,4 +42,4 @@ export type DataTableControllerProps<M extends Record<string, any> = any> = {
42
42
  * @param fixedFilterFromProps
43
43
  * @param updateUrl
44
44
  */
45
- export declare function useDataTableController<M extends Record<string, any> = any, USER extends User = User>({ path, collection, scrollRestoration, entitiesDisplayedFirst, lastDeleteTimestamp: _lastDeleteTimestamp, fixedFilter: fixedFilterFromProps, updateUrl }: DataTableControllerProps<M>): EntityTableController<M>;
45
+ export declare function useDataTableController<M extends Record<string, any> = any, USER extends User = User>({ path, collection, scrollRestoration, entitiesDisplayedFirst, lastDeleteTimestamp, fixedFilter: fixedFilterFromProps, updateUrl }: DataTableControllerProps<M>): EntityTableController<M>;
@@ -1,4 +1,11 @@
1
1
  import { Entity, FilterValues } from "@rebasepro/types";
2
+ /**
3
+ * Forget every collection's scroll position and the rows kept with it.
4
+ *
5
+ * The rows are what the signed-in user was allowed to read, and a table seeds
6
+ * its first render from them, so they must not outlive that user's session.
7
+ */
8
+ export declare function clearCollectionScrollCache(): void;
2
9
  export type ScrollRestorationController = {
3
10
  getCollectionScroll: (path: string, filters?: FilterValues<any>) => {
4
11
  scrollOffset: number;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * What the settings screen and the link's screen say about a refused address
3
+ * change: the codes a person can act on get the panel's own, translated words,
4
+ * and anything else keeps the server's message.
5
+ */
6
+ export declare function emailChangeErrorMessage(error: unknown, t: (key: string) => string): string;
@@ -13,3 +13,4 @@ export * from "./UserDisplay.js";
13
13
  export * from "./LoginView/index.js";
14
14
  export * from "./RebaseAuth.js";
15
15
  export * from "./SchemaDriftBanner.js";
16
+ export * from "./ConnectionLostBanner.js";
@@ -5,6 +5,12 @@ import React from "react";
5
5
  export type CollectionResolver = (slug: string) => {
6
6
  properties?: Record<string, unknown>;
7
7
  } | undefined;
8
+ /**
9
+ * Translate the path a collection is addressed by into the path its driver
10
+ * stores it under — `fs_diagnosis` into `diagnosis`, for a Firestore collection
11
+ * that declares `path: "diagnosis"`. See `EntityDataOptions.resolveDataPath`.
12
+ */
13
+ export type DataPathResolver = (path: string) => string | undefined;
8
14
  /**
9
15
  * Lets the layer that owns the collections hand a resolver *up* to the data
10
16
  * layer, which needs a collection's primary keys to give its rows an address.
@@ -15,9 +21,13 @@ export type CollectionResolver = (slug: string) => {
15
21
  * prop without giving every headless BaaS app a collections argument it has no
16
22
  * use for.
17
23
  *
24
+ * The same layer knows where each collection is stored, so it hands up the
25
+ * data-path translation alongside: a direct driver has to be given a
26
+ * collection's declared `path`, and only the registry knows it.
27
+ *
18
28
  * Registration is a ref write, so it is safe to call during render — and it has
19
29
  * to be. The views that fetch rows are *below* the registrar, and child effects
20
30
  * run before parent effects, so registering in an effect would bind after the
21
31
  * first page of rows had already been converted.
22
32
  */
23
- export declare const CollectionResolverRegistrationContext: React.Context<(resolver: CollectionResolver | undefined) => void>;
33
+ export declare const CollectionResolverRegistrationContext: React.Context<(resolver: CollectionResolver | undefined, resolveDataPath?: DataPathResolver) => void>;
@@ -2,16 +2,24 @@ import type { RebasePlugin, RebaseContext } from "@rebasepro/cms-types";
2
2
  /**
3
3
  * Render-less component that manages plugin lifecycle hooks.
4
4
  *
5
- * - Calls `lifecycle.onMount(context)` when plugins mount.
6
- * - Calls `lifecycle.onUnmount()` when plugins unmount.
7
- * - Subscribes to auth state changes and calls `lifecycle.onAuthStateChange`.
5
+ * - Calls `lifecycle.onMount(context)` once, the first time auth is ready.
6
+ * - Calls `lifecycle.onAuthStateChange(user)` on every change of user after
7
+ * that — `null` on sign-out, the new user on the next sign-in.
8
+ * - Calls `lifecycle.onUnmount()` when the Rebase tree unmounts.
9
+ *
10
+ * Mounted for as long as there are plugins, signed in or not. It used to be
11
+ * mounted only while a user was signed in, so a sign-out unmounted it and the
12
+ * next sign-in mounted a fresh one — `onAuthStateChange` could never see the
13
+ * user change, and never fired.
8
14
  *
9
15
  * Mount this component inside the Rebase tree, below PluginProviderStack,
10
16
  * so that the RebaseContext is fully available.
11
17
  *
12
18
  * @internal
13
19
  */
14
- export declare function PluginLifecycleManager({ plugins, context }: {
20
+ export declare function PluginLifecycleManager({ plugins, context, authReady }: {
15
21
  plugins: RebasePlugin[];
16
22
  context: RebaseContext;
23
+ /** Signed in, or sign-in skipped: what `onMount` waits for. */
24
+ authReady: boolean;
17
25
  }): null;