@terreno/mcp 57.21.0 → 58.0.0-beta.1
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/CHANGELOG.md +43 -0
- package/README.md +57 -3
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +17 -16
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
- package/dist/docs/guidelines/ui/guidelines/core.md +1 -1
- package/dist/docs/prompts/debug.md +6 -4
- package/dist/docs/prompts/screen.md +11 -17
- package/dist/docs/resources/overview.md +5 -3
- package/dist/docs/resources/patterns.md +157 -238
- package/dist/docs/resources/syncdb.md +44 -16
- package/dist/docs/templates/bootstrap/CLAUDE.template.md +2 -2
- package/dist/docs/templates/bootstrap/cursorrules.template.md +7 -5
- package/dist/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
- package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
- package/dist/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
- package/dist/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
- package/dist/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
- package/dist/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
- package/dist/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
- package/dist/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
- package/dist/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
- package/dist/docs/upgrades/58.0.0-beta.1.md +504 -0
- package/dist/docs/upgrades/58.0.0.md +495 -4
- package/dist/docs/versioned/next/README.md +2 -2
- package/dist/docs/versioned/next/approval-rubric.md +2 -2
- package/dist/docs/versioned/next/explanation/admin-interface.md +6 -5
- package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
- package/dist/docs/versioned/next/explanation/ai-workflows.md +5 -3
- package/dist/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
- package/dist/docs/versioned/next/explanation/authentication.md +62 -43
- package/dist/docs/versioned/next/explanation/dependency-management.md +1 -1
- package/dist/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/dist/docs/versioned/next/explanation/example-coverage.md +2 -2
- package/dist/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
- package/dist/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
- package/dist/docs/versioned/next/explanation/local-first-data.md +11 -9
- package/dist/docs/versioned/next/explanation/model-router-actions.md +1 -1
- package/dist/docs/versioned/next/explanation/modular-api-design.md +2 -2
- package/dist/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
- package/dist/docs/versioned/next/explanation/source-rules.md +2 -2
- package/dist/docs/versioned/next/explanation/typescript-configuration.md +2 -4
- package/dist/docs/versioned/next/explanation/versioning-policy.md +3 -3
- package/dist/docs/versioned/next/how-to/README.md +3 -2
- package/dist/docs/versioned/next/how-to/add-feature-flags.md +50 -25
- package/dist/docs/versioned/next/how-to/add-organizations.md +2 -1
- package/dist/docs/versioned/next/how-to/admin-custom-home.md +8 -2
- package/dist/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
- package/dist/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
- package/dist/docs/versioned/next/how-to/build-admin-screens.md +10 -8
- package/dist/docs/versioned/next/how-to/build-for-web.md +1 -1
- package/dist/docs/versioned/next/how-to/call-external-apis.md +2 -2
- package/dist/docs/versioned/next/how-to/circleci.md +12 -6
- package/dist/docs/versioned/next/how-to/configure-better-auth.md +114 -60
- package/dist/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
- package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
- package/dist/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
- package/dist/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
- package/dist/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
- package/dist/docs/versioned/next/how-to/github-actions-ci.md +2 -2
- package/dist/docs/versioned/next/how-to/in-app-notifications.md +1 -1
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
- package/dist/docs/versioned/next/how-to/password-reset.md +1 -1
- package/dist/docs/versioned/next/how-to/product-announcements.md +6 -3
- package/dist/docs/versioned/next/how-to/rate-limiting.md +38 -16
- package/dist/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
- package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -3
- package/dist/docs/versioned/next/how-to/set-up-social-login.md +1 -1
- package/dist/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
- package/dist/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
- package/dist/docs/versioned/next/how-to/upgrade-banner.md +5 -3
- package/dist/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
- package/dist/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
- package/dist/docs/versioned/next/how-to/websocket-integration.md +113 -402
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
- package/dist/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
- package/dist/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
- package/dist/docs/versioned/next/reference/README.md +1 -1
- package/dist/docs/versioned/next/reference/admin-frontend.md +85 -70
- package/dist/docs/versioned/next/reference/admin-spa.md +10 -4
- package/dist/docs/versioned/next/reference/ai.md +36 -4
- package/dist/docs/versioned/next/reference/announcements.md +3 -3
- package/dist/docs/versioned/next/reference/api.md +39 -24
- package/dist/docs/versioned/next/reference/cli.md +3 -4
- package/dist/docs/versioned/next/reference/environment-variables.md +16 -16
- package/dist/docs/versioned/next/reference/feature-flags.md +48 -7
- package/dist/docs/versioned/next/reference/jobs.md +2 -2
- package/dist/docs/versioned/next/reference/legacy/rtk.md +25 -600
- package/dist/docs/versioned/next/reference/mcp-server.md +441 -1158
- package/dist/docs/versioned/next/reference/syncdb.md +426 -16
- package/dist/docs/versioned/next/reference/test.md +28 -3
- package/dist/docs/versioned/next/reference/ui.md +132 -19
- package/dist/docs/versioned/next/tasks/native-module-baseline.md +8 -4
- package/dist/docs/versioned/next/tasks/rate-limiting.md +9 -2
- package/dist/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
- package/dist/docs/versioned/next/tasks/remove-rtk.md +155 -0
- package/dist/local/localTools.d.ts.map +1 -1
- package/dist/local/localTools.js +3 -54
- package/dist/local/localTools.js.map +1 -1
- package/dist/local/tools/runtime.d.ts +0 -21
- package/dist/local/tools/runtime.d.ts.map +1 -1
- package/dist/local/tools/runtime.js +0 -176
- package/dist/local/tools/runtime.js.map +1 -1
- package/dist/local/tools/syncDb.d.ts +0 -11
- package/dist/local/tools/syncDb.d.ts.map +1 -1
- package/dist/local/tools/syncDb.js +1 -5
- package/dist/local/tools/syncDb.js.map +1 -1
- package/dist/packageGuidelines.d.ts +3 -2
- package/dist/packageGuidelines.d.ts.map +1 -1
- package/dist/packageGuidelines.js +4 -11
- package/dist/packageGuidelines.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +17 -18
- package/dist/prompts.js.map +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +1 -8
- package/dist/resources.js.map +1 -1
- package/dist/search/chunker.d.ts +1 -1
- package/dist/search/chunker.d.ts.map +1 -1
- package/dist/search/inferPackages.d.ts +1 -1
- package/dist/search/inferPackages.js +5 -5
- package/dist/search/inferPackages.js.map +1 -1
- package/dist/tools.d.ts +6 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +126 -119
- package/dist/tools.js.map +1 -1
- package/package.json +6 -6
- package/src/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
- package/src/docs/guidelines/ui/guidelines/core.md +1 -1
- package/src/docs/prompts/debug.md +6 -4
- package/src/docs/prompts/screen.md +11 -17
- package/src/docs/resources/overview.md +5 -3
- package/src/docs/resources/patterns.md +157 -238
- package/src/docs/resources/syncdb.md +44 -16
- package/src/docs/templates/bootstrap/CLAUDE.template.md +2 -2
- package/src/docs/templates/bootstrap/cursorrules.template.md +7 -5
- package/src/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
- package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
- package/src/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
- package/src/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
- package/src/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
- package/src/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
- package/src/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
- package/src/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
- package/src/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
- package/src/docs/upgrades/58.0.0-beta.1.md +504 -0
- package/src/docs/upgrades/58.0.0.md +495 -4
- package/src/docs/versioned/next/README.md +2 -2
- package/src/docs/versioned/next/approval-rubric.md +2 -2
- package/src/docs/versioned/next/explanation/admin-interface.md +6 -5
- package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
- package/src/docs/versioned/next/explanation/ai-workflows.md +5 -3
- package/src/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
- package/src/docs/versioned/next/explanation/authentication.md +62 -43
- package/src/docs/versioned/next/explanation/dependency-management.md +1 -1
- package/src/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/src/docs/versioned/next/explanation/example-coverage.md +2 -2
- package/src/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
- package/src/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
- package/src/docs/versioned/next/explanation/local-first-data.md +11 -9
- package/src/docs/versioned/next/explanation/model-router-actions.md +1 -1
- package/src/docs/versioned/next/explanation/modular-api-design.md +2 -2
- package/src/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
- package/src/docs/versioned/next/explanation/source-rules.md +2 -2
- package/src/docs/versioned/next/explanation/typescript-configuration.md +2 -4
- package/src/docs/versioned/next/explanation/versioning-policy.md +3 -3
- package/src/docs/versioned/next/how-to/README.md +3 -2
- package/src/docs/versioned/next/how-to/add-feature-flags.md +50 -25
- package/src/docs/versioned/next/how-to/add-organizations.md +2 -1
- package/src/docs/versioned/next/how-to/admin-custom-home.md +8 -2
- package/src/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
- package/src/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
- package/src/docs/versioned/next/how-to/build-admin-screens.md +10 -8
- package/src/docs/versioned/next/how-to/build-for-web.md +1 -1
- package/src/docs/versioned/next/how-to/call-external-apis.md +2 -2
- package/src/docs/versioned/next/how-to/circleci.md +12 -6
- package/src/docs/versioned/next/how-to/configure-better-auth.md +114 -60
- package/src/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
- package/src/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
- package/src/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
- package/src/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
- package/src/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
- package/src/docs/versioned/next/how-to/github-actions-ci.md +2 -2
- package/src/docs/versioned/next/how-to/in-app-notifications.md +1 -1
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
- package/src/docs/versioned/next/how-to/password-reset.md +1 -1
- package/src/docs/versioned/next/how-to/product-announcements.md +6 -3
- package/src/docs/versioned/next/how-to/rate-limiting.md +38 -16
- package/src/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
- package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -3
- package/src/docs/versioned/next/how-to/set-up-social-login.md +1 -1
- package/src/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
- package/src/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
- package/src/docs/versioned/next/how-to/upgrade-banner.md +5 -3
- package/src/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
- package/src/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
- package/src/docs/versioned/next/how-to/websocket-integration.md +113 -402
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
- package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
- package/src/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
- package/src/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
- package/src/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
- package/src/docs/versioned/next/reference/README.md +1 -1
- package/src/docs/versioned/next/reference/admin-frontend.md +85 -70
- package/src/docs/versioned/next/reference/admin-spa.md +10 -4
- package/src/docs/versioned/next/reference/ai.md +36 -4
- package/src/docs/versioned/next/reference/announcements.md +3 -3
- package/src/docs/versioned/next/reference/api.md +39 -24
- package/src/docs/versioned/next/reference/cli.md +3 -4
- package/src/docs/versioned/next/reference/environment-variables.md +16 -16
- package/src/docs/versioned/next/reference/feature-flags.md +48 -7
- package/src/docs/versioned/next/reference/jobs.md +2 -2
- package/src/docs/versioned/next/reference/legacy/rtk.md +25 -600
- package/src/docs/versioned/next/reference/mcp-server.md +441 -1158
- package/src/docs/versioned/next/reference/syncdb.md +426 -16
- package/src/docs/versioned/next/reference/test.md +28 -3
- package/src/docs/versioned/next/reference/ui.md +132 -19
- package/src/docs/versioned/next/tasks/native-module-baseline.md +8 -4
- package/src/docs/versioned/next/tasks/rate-limiting.md +9 -2
- package/src/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
- package/src/docs/versioned/next/tasks/remove-rtk.md +155 -0
- package/dist/docs/guidelines/rtk/guidelines/core.md +0 -26
- package/dist/docs/resources/rtk.md +0 -181
- package/dist/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
- package/dist/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
- package/dist/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
- package/dist/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
- package/dist/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
- package/dist/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
- package/dist/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
- package/src/docs/guidelines/rtk/guidelines/core.md +0 -26
- package/src/docs/resources/rtk.md +0 -181
- package/src/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
- package/src/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
- package/src/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
- package/src/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
- package/src/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
- package/src/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
- package/src/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
|
@@ -1,21 +1,18 @@
|
|
|
1
|
+
import {useSession} from "@terreno/syncdb/auth";
|
|
1
2
|
import {Box, Button, Card, Heading, Page, Spinner, TapToEdit, Text, TextField} from "@terreno/ui";
|
|
2
3
|
import type React from "react";
|
|
3
4
|
import {useCallback, useEffect, useMemo, useState} from "react";
|
|
4
|
-
import {
|
|
5
|
-
import {logout, syncBetterAuthSession, useAppDispatch} from "@/store/index";
|
|
6
|
-
import {useGetMeQuery, usePatchMeMutation} from "@/store/sdk";
|
|
5
|
+
import {useGetAuthMeQuery, usePatchAuthMeMutation} from "@/store/apiSdk";
|
|
7
6
|
|
|
8
7
|
const ProfileScreen: React.FC = () => {
|
|
9
|
-
const
|
|
10
|
-
const {data:
|
|
11
|
-
const [updateProfile] =
|
|
8
|
+
const {logout} = useSession();
|
|
9
|
+
const {data: user, isLoading} = useGetAuthMeQuery();
|
|
10
|
+
const [updateProfile] = usePatchAuthMeMutation();
|
|
12
11
|
const [name, setName] = useState<string>("");
|
|
13
12
|
const [email, setEmail] = useState<string>("");
|
|
14
13
|
const [currentPassword, setCurrentPassword] = useState<string>("");
|
|
15
14
|
const [saveError, setSaveError] = useState<string | null>(null);
|
|
16
15
|
|
|
17
|
-
const user = profile?.data;
|
|
18
|
-
|
|
19
16
|
// Copy the server name into local state without resetting an in-progress email edit.
|
|
20
17
|
useEffect(() => {
|
|
21
18
|
if (!user) {
|
|
@@ -32,11 +29,10 @@ const ProfileScreen: React.FC = () => {
|
|
|
32
29
|
setEmail(user.email || "");
|
|
33
30
|
}, [user?.email]);
|
|
34
31
|
|
|
32
|
+
// logout signs syncDb out, then Better Auth, and clears cached API responses.
|
|
35
33
|
const handleLogout = useCallback(async (): Promise<void> => {
|
|
36
|
-
await
|
|
37
|
-
|
|
38
|
-
await syncBetterAuthSession(dispatch);
|
|
39
|
-
}, [dispatch]);
|
|
34
|
+
await logout();
|
|
35
|
+
}, [logout]);
|
|
40
36
|
|
|
41
37
|
const handleSaveName = useCallback(
|
|
42
38
|
async (value: string): Promise<void> => {
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import {SessionProvider, useSession} from "@terreno/syncdb/auth";
|
|
2
|
+
import {baseUrl} from "@terreno/syncdb/config";
|
|
3
|
+
import {ApiClientProvider, SyncDbProvider} from "@terreno/syncdb/react";
|
|
4
|
+
import {Spinner, TerrenoProvider} from "@terreno/ui";
|
|
4
5
|
import {Stack} from "expo-router";
|
|
5
|
-
import
|
|
6
|
+
import {DefaultTheme, ThemeProvider} from "expo-router/react-navigation";
|
|
7
|
+
import type React from "react";
|
|
6
8
|
import {useEffect} from "react";
|
|
7
9
|
import "react-native-reanimated";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {PersistGate} from "redux-persist/integration/react";
|
|
13
|
-
import {useColorScheme} from "@/components/useColorScheme";
|
|
14
|
-
import store, {persistor} from "@/store/index";
|
|
10
|
+
import {installDevConsoleLogger} from "@terreno/syncdb/app";
|
|
11
|
+
import {betterAuthClient} from "@/lib/betterAuth";
|
|
12
|
+
import {apiClient} from "@/store/apiClient";
|
|
13
|
+
import {setSyncDbReady, syncDb} from "@/store/syncdb";
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
installDevConsoleLogger({baseUrl});
|
|
17
16
|
|
|
18
17
|
export {ErrorBoundary} from "expo-router";
|
|
19
18
|
|
|
@@ -21,54 +20,78 @@ export const unstable_settings = {
|
|
|
21
20
|
initialRouteName: "(tabs)",
|
|
22
21
|
};
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const [loaded, error] = useFonts({
|
|
28
|
-
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
|
|
29
|
-
...FontAwesome.font,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
if (error) {
|
|
34
|
-
throw error;
|
|
35
|
-
}
|
|
36
|
-
}, [error]);
|
|
37
|
-
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
if (loaded) {
|
|
40
|
-
SplashScreen.hideAsync();
|
|
41
|
-
}
|
|
42
|
-
}, [loaded]);
|
|
43
|
-
|
|
44
|
-
if (!loaded) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
23
|
+
// Logging out signs syncDb out (stop + wipe) and drops cached API responses so the next
|
|
24
|
+
// user never sees the previous user's data.
|
|
25
|
+
const SYNC_DBS = [syncDb];
|
|
47
26
|
|
|
27
|
+
// TerrenoProvider loads the Nunito/Titillium Web families @terreno/ui renders with, so this
|
|
28
|
+
// app ships no font assets of its own. Add `useFonts` here only for extra custom faces.
|
|
29
|
+
// SessionProvider owns the Better Auth session (`useSession()`); ApiClientProvider gives the
|
|
30
|
+
// generated hooks in store/apiSdk.ts their request client. No Redux store.
|
|
31
|
+
export default function RootLayout(): React.ReactElement {
|
|
48
32
|
return (
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
<
|
|
33
|
+
<TerrenoProvider openAPISpecUrl={`${baseUrl}/openapi.json`}>
|
|
34
|
+
<ApiClientProvider client={apiClient}>
|
|
35
|
+
<SessionProvider
|
|
36
|
+
authClient={betterAuthClient}
|
|
37
|
+
onLogout={apiClient.reset}
|
|
38
|
+
syncDbs={SYNC_DBS}
|
|
39
|
+
>
|
|
52
40
|
<RootLayoutNav />
|
|
53
|
-
</
|
|
54
|
-
</
|
|
55
|
-
</
|
|
41
|
+
</SessionProvider>
|
|
42
|
+
</ApiClientProvider>
|
|
43
|
+
</TerrenoProvider>
|
|
56
44
|
);
|
|
57
45
|
}
|
|
58
46
|
|
|
59
47
|
function RootLayoutNav(): React.ReactElement {
|
|
60
|
-
const
|
|
61
|
-
|
|
48
|
+
const {userId, isLoading: isAuthLoading} = useSession();
|
|
49
|
+
|
|
50
|
+
// Start syncdb after login; stop on logout or user switch.
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!userId) {
|
|
53
|
+
setSyncDbReady(false);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
let stopped = false;
|
|
57
|
+
void syncDb
|
|
58
|
+
.start()
|
|
59
|
+
.then(() => {
|
|
60
|
+
if (!stopped) {
|
|
61
|
+
setSyncDbReady(true);
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
.catch((startError: unknown) => {
|
|
65
|
+
console.error("[syncdb] start failed", startError);
|
|
66
|
+
setSyncDbReady(false);
|
|
67
|
+
});
|
|
68
|
+
return () => {
|
|
69
|
+
stopped = true;
|
|
70
|
+
setSyncDbReady(false);
|
|
71
|
+
void syncDb.stop();
|
|
72
|
+
};
|
|
73
|
+
}, [userId]);
|
|
74
|
+
|
|
75
|
+
if (isAuthLoading) {
|
|
76
|
+
return <Spinner />;
|
|
77
|
+
}
|
|
62
78
|
|
|
63
79
|
return (
|
|
64
|
-
<
|
|
65
|
-
<
|
|
66
|
-
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
<SyncDbProvider client={syncDb}>
|
|
81
|
+
<ThemeProvider value={DefaultTheme}>
|
|
82
|
+
{/* Stack.Protected is the supported way to gate routes on auth state. Rendering
|
|
83
|
+
Stack.Screen children behind a raw conditional (or a fragment) crashes the
|
|
84
|
+
navigator, because Stack reads its screen config off the child elements. */}
|
|
85
|
+
<Stack>
|
|
86
|
+
<Stack.Protected guard={!userId}>
|
|
87
|
+
<Stack.Screen name="login" options={{headerShown: false}} />
|
|
88
|
+
<Stack.Screen name="signup" options={{headerShown: false}} />
|
|
89
|
+
</Stack.Protected>
|
|
90
|
+
<Stack.Protected guard={Boolean(userId)}>
|
|
91
|
+
<Stack.Screen name="(tabs)" options={{headerShown: false}} />
|
|
92
|
+
</Stack.Protected>
|
|
93
|
+
</Stack>
|
|
94
|
+
</ThemeProvider>
|
|
95
|
+
</SyncDbProvider>
|
|
73
96
|
);
|
|
74
97
|
}
|
|
@@ -1,116 +1,54 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {useSession} from "@terreno/syncdb/auth";
|
|
2
|
+
import {baseUrl} from "@terreno/syncdb/config";
|
|
3
|
+
import {LoginScreen} from "@terreno/ui";
|
|
4
|
+
import {useRouter} from "expo-router";
|
|
2
5
|
import type React from "react";
|
|
3
6
|
import {useCallback, useState} from "react";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const [
|
|
10
|
-
const [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
await emailLogin({email, password}).unwrap();
|
|
7
|
+
import {betterAuthClient} from "@/lib/betterAuth";
|
|
8
|
+
|
|
9
|
+
const Login: React.FC = () => {
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
const {refresh} = useSession();
|
|
12
|
+
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
|
|
13
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
14
|
+
|
|
15
|
+
const handleSubmit = useCallback(
|
|
16
|
+
async (values: Record<string, string>): Promise<void> => {
|
|
17
|
+
const {email, password} = values;
|
|
18
|
+
setErrorMessage(undefined);
|
|
19
|
+
setIsSubmitting(true);
|
|
20
|
+
try {
|
|
21
|
+
const result = await betterAuthClient.signIn.email({email, password});
|
|
22
|
+
if (result.error) {
|
|
23
|
+
setErrorMessage(result.error.message ?? "Sign in failed.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Resolve the new session now instead of waiting for the next session push.
|
|
27
|
+
await refresh();
|
|
28
|
+
router.replace("/(tabs)");
|
|
29
|
+
} catch (error: unknown) {
|
|
30
|
+
console.error("[login] Sign in threw", {baseUrl, error});
|
|
31
|
+
setErrorMessage("Sign in failed. Please try again.");
|
|
32
|
+
} finally {
|
|
33
|
+
setIsSubmitting(false);
|
|
32
34
|
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}, [email, password, name, isSignUp, emailLogin, emailSignUp, toast]);
|
|
37
|
-
|
|
38
|
-
const toggleMode = useCallback((): void => {
|
|
39
|
-
setIsSignUp(!isSignUp);
|
|
40
|
-
}, [isSignUp]);
|
|
41
|
-
|
|
42
|
-
const isLoading = isLoginLoading || isSignUpLoading;
|
|
43
|
-
const error = loginError || signUpError;
|
|
44
|
-
const isSubmitDisabled = !email || !password || (isSignUp && !name) || isLoading;
|
|
35
|
+
},
|
|
36
|
+
[refresh, router]
|
|
37
|
+
);
|
|
45
38
|
|
|
46
39
|
return (
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<Heading>{isSignUp ? "Create Account" : "Welcome Back"}</Heading>
|
|
59
|
-
</Box>
|
|
60
|
-
<Box gap={4} width="100%">
|
|
61
|
-
{isSignUp && (
|
|
62
|
-
<TextField
|
|
63
|
-
disabled={isLoading}
|
|
64
|
-
onChange={setName}
|
|
65
|
-
placeholder="Name"
|
|
66
|
-
title="Name"
|
|
67
|
-
value={name}
|
|
68
|
-
/>
|
|
69
|
-
)}
|
|
70
|
-
<TextField
|
|
71
|
-
autoComplete="username"
|
|
72
|
-
disabled={isLoading}
|
|
73
|
-
onChange={setEmail}
|
|
74
|
-
placeholder="Email"
|
|
75
|
-
title="Email"
|
|
76
|
-
type="email"
|
|
77
|
-
value={email}
|
|
78
|
-
/>
|
|
79
|
-
<TextField
|
|
80
|
-
disabled={isLoading}
|
|
81
|
-
onChange={setPassword}
|
|
82
|
-
placeholder="Password"
|
|
83
|
-
title="Password"
|
|
84
|
-
type="password"
|
|
85
|
-
value={password}
|
|
86
|
-
/>
|
|
87
|
-
{Boolean(error) && (
|
|
88
|
-
<Text color="error">
|
|
89
|
-
{(error as {data?: {message?: string}})?.data?.message || "An error occurred"}
|
|
90
|
-
</Text>
|
|
91
|
-
)}
|
|
92
|
-
<Box marginTop={4}>
|
|
93
|
-
<Button
|
|
94
|
-
disabled={isSubmitDisabled}
|
|
95
|
-
fullWidth
|
|
96
|
-
loading={isLoading}
|
|
97
|
-
onClick={handleSubmit}
|
|
98
|
-
text={isSignUp ? "Sign Up" : "Login"}
|
|
99
|
-
/>
|
|
100
|
-
</Box>
|
|
101
|
-
<Box marginTop={2}>
|
|
102
|
-
<Button
|
|
103
|
-
disabled={isLoading}
|
|
104
|
-
fullWidth
|
|
105
|
-
onClick={toggleMode}
|
|
106
|
-
text={isSignUp ? "Already have an account? Login" : "Need an account? Sign Up"}
|
|
107
|
-
variant="outline"
|
|
108
|
-
/>
|
|
109
|
-
</Box>
|
|
110
|
-
</Box>
|
|
111
|
-
</Box>
|
|
112
|
-
</Page>
|
|
40
|
+
<LoginScreen
|
|
41
|
+
error={errorMessage}
|
|
42
|
+
fields={[
|
|
43
|
+
{autoComplete: "username", label: "Email", name: "email", required: true, type: "email"},
|
|
44
|
+
{label: "Password", name: "password", required: true, type: "password"},
|
|
45
|
+
]}
|
|
46
|
+
loading={isSubmitting}
|
|
47
|
+
onSignUpPress={() => router.push("/signup")}
|
|
48
|
+
onSubmit={handleSubmit}
|
|
49
|
+
title="Welcome"
|
|
50
|
+
/>
|
|
113
51
|
);
|
|
114
52
|
};
|
|
115
53
|
|
|
116
|
-
export default
|
|
54
|
+
export default Login;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {useSession} from "@terreno/syncdb/auth";
|
|
2
|
+
import {SignUpScreen, simplePasswordRequirements} from "@terreno/ui";
|
|
3
|
+
import {useRouter} from "expo-router";
|
|
4
|
+
import type React from "react";
|
|
5
|
+
import {useCallback, useState} from "react";
|
|
6
|
+
import {betterAuthClient} from "@/lib/betterAuth";
|
|
7
|
+
|
|
8
|
+
const SignUp: React.FC = () => {
|
|
9
|
+
const router = useRouter();
|
|
10
|
+
const {refresh} = useSession();
|
|
11
|
+
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
|
|
12
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
13
|
+
|
|
14
|
+
const handleSubmit = useCallback(
|
|
15
|
+
async (values: Record<string, string>): Promise<void> => {
|
|
16
|
+
const {email, password, name} = values;
|
|
17
|
+
setErrorMessage(undefined);
|
|
18
|
+
setIsSubmitting(true);
|
|
19
|
+
try {
|
|
20
|
+
const result = await betterAuthClient.signUp.email({email, name, password});
|
|
21
|
+
if (result.error) {
|
|
22
|
+
setErrorMessage(result.error.message ?? "Sign up failed.");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// Resolve the new session now instead of waiting for the next session push.
|
|
26
|
+
await refresh();
|
|
27
|
+
router.replace("/(tabs)");
|
|
28
|
+
} catch {
|
|
29
|
+
setErrorMessage("Sign up failed. Please try again.");
|
|
30
|
+
} finally {
|
|
31
|
+
setIsSubmitting(false);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
[refresh, router]
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<SignUpScreen
|
|
39
|
+
error={errorMessage}
|
|
40
|
+
fields={[
|
|
41
|
+
{label: "Name", name: "name", required: true, type: "text"},
|
|
42
|
+
{autoComplete: "off", label: "Email", name: "email", required: true, type: "email"},
|
|
43
|
+
{label: "Password", name: "password", required: true, type: "password"},
|
|
44
|
+
]}
|
|
45
|
+
loading={isSubmitting}
|
|
46
|
+
onLoginPress={() => router.back()}
|
|
47
|
+
onSubmit={handleSubmit}
|
|
48
|
+
passwordRequirements={simplePasswordRequirements}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default SignUp;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {createBetterAuthClient} from "@terreno/syncdb/auth";
|
|
2
|
+
import {baseUrl} from "@terreno/syncdb/config";
|
|
3
|
+
import Constants from "expo-constants";
|
|
4
|
+
|
|
5
|
+
const getAppScheme = (): string => {
|
|
6
|
+
const expoExtra = Constants.expoConfig?.extra;
|
|
7
|
+
return expoExtra?.scheme ?? "{{appName}}";
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Expo-ready Better Auth client (SecureStore on native, AsyncStorage on web). Feeds
|
|
12
|
+
* `SessionProvider` (app/_layout.tsx) and the shared auth provider in store/syncdb.ts.
|
|
13
|
+
*/
|
|
14
|
+
export const betterAuthClient = createBetterAuthClient({
|
|
15
|
+
baseURL: baseUrl,
|
|
16
|
+
scheme: getAppScheme(),
|
|
17
|
+
storagePrefix: "{{appName}}",
|
|
18
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request client for every non-synced backend route (`/auth/me`, modelRouter actions, admin).
|
|
3
|
+
* Synced collections go through `syncDb` instead. Generated hooks in `store/apiSdk.ts` use the
|
|
4
|
+
* client that `ApiClientProvider` (in `app/_layout.tsx`) puts in context.
|
|
5
|
+
*/
|
|
6
|
+
import {type ApiClient, createApiClient} from "@terreno/syncdb/api";
|
|
7
|
+
import {appHeaders, baseUrl} from "@terreno/syncdb/config";
|
|
8
|
+
import {authProvider} from "@/store/syncdb";
|
|
9
|
+
|
|
10
|
+
/** Failures callers already handle (signed out, missing rows) are not worth a warning. */
|
|
11
|
+
const QUIET_STATUSES = new Set([401, 404]);
|
|
12
|
+
|
|
13
|
+
export const apiClient: ApiClient = createApiClient({
|
|
14
|
+
authProvider,
|
|
15
|
+
baseUrl,
|
|
16
|
+
credentials: "include",
|
|
17
|
+
headers: appHeaders,
|
|
18
|
+
onError: (error, args): void => {
|
|
19
|
+
if (QUIET_STATUSES.has(error.status)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
console.warn(`${args.method ?? "GET"} ${args.url} failed: ${error.message}`);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/** Bearer header for `@terreno/admin-frontend`'s `AdminProvider` fetch client. */
|
|
27
|
+
export const getAdminAuthHeaders = async (): Promise<Record<string, string>> => {
|
|
28
|
+
const token = await authProvider.getToken();
|
|
29
|
+
if (!token) {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
return {Authorization: `Bearer ${token}`};
|
|
33
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// GENERATED by @terreno/syncdb (terreno-syncdb-codegen --api-out). Do not edit.
|
|
2
|
+
// Typed request hooks for every non-synced @terreno/api route. Synced collections use the
|
|
3
|
+
// collection hooks from the sync SDK instead. Run `bun run sdk` with the backend running to
|
|
4
|
+
// regenerate this file from its OpenAPI spec.
|
|
5
|
+
// biome-ignore-all lint/suspicious/noConfusingVoidType: no-argument operations type their argument as void so callers can omit it
|
|
6
|
+
|
|
7
|
+
import type {ApiRequestArgs} from "@terreno/syncdb/api";
|
|
8
|
+
import {
|
|
9
|
+
type UseApiMutationOptions,
|
|
10
|
+
type UseApiMutationResult,
|
|
11
|
+
type UseApiQueryOptions,
|
|
12
|
+
type UseApiQueryResult,
|
|
13
|
+
type UseLazyApiQueryOptions,
|
|
14
|
+
type UseLazyApiQueryResult,
|
|
15
|
+
useApiMutation,
|
|
16
|
+
useApiQuery,
|
|
17
|
+
useLazyApiQuery,
|
|
18
|
+
} from "@terreno/syncdb/react";
|
|
19
|
+
|
|
20
|
+
type ApiSdkQueryOptions<T> = Omit<UseApiQueryOptions<T, T>, "providesTags" | "transformResponse">;
|
|
21
|
+
type ApiSdkLazyOptions<T> = Omit<UseLazyApiQueryOptions<T, never>, "providesTags" | "toRequest">;
|
|
22
|
+
|
|
23
|
+
export type GetAuthMeArgs = void;
|
|
24
|
+
|
|
25
|
+
export type GetAuthMeRes = {
|
|
26
|
+
admin?: boolean;
|
|
27
|
+
email: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
_id: string;
|
|
30
|
+
id: string;
|
|
31
|
+
created?: string;
|
|
32
|
+
updated?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const getAuthMeRequest = (): ApiRequestArgs => ({
|
|
36
|
+
method: "GET",
|
|
37
|
+
url: `/auth/me`,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/** Get the current user */
|
|
41
|
+
export const useGetAuthMeQuery = (
|
|
42
|
+
_arg?: GetAuthMeArgs,
|
|
43
|
+
options: ApiSdkQueryOptions<GetAuthMeRes> = {}
|
|
44
|
+
): UseApiQueryResult<GetAuthMeRes> =>
|
|
45
|
+
useApiQuery<GetAuthMeRes>(getAuthMeRequest(), {...options, providesTags: ["auth"]});
|
|
46
|
+
|
|
47
|
+
export const useLazyGetAuthMeQuery = (
|
|
48
|
+
options: ApiSdkLazyOptions<GetAuthMeRes> = {}
|
|
49
|
+
): UseLazyApiQueryResult<GetAuthMeRes, GetAuthMeArgs> =>
|
|
50
|
+
useLazyApiQuery<GetAuthMeRes, GetAuthMeArgs>({
|
|
51
|
+
...options,
|
|
52
|
+
providesTags: ["auth"],
|
|
53
|
+
toRequest: (): ApiRequestArgs => getAuthMeRequest(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export type PatchAuthMeArgs = {
|
|
57
|
+
email?: string;
|
|
58
|
+
name?: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type PatchAuthMeRes = GetAuthMeRes;
|
|
62
|
+
|
|
63
|
+
export const patchAuthMeRequest = (arg: PatchAuthMeArgs): ApiRequestArgs => ({
|
|
64
|
+
body: arg,
|
|
65
|
+
method: "PATCH",
|
|
66
|
+
url: `/auth/me`,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/** Update the current user */
|
|
70
|
+
export const usePatchAuthMeMutation = (
|
|
71
|
+
options: UseApiMutationOptions<PatchAuthMeArgs, PatchAuthMeRes> = {}
|
|
72
|
+
): UseApiMutationResult<PatchAuthMeArgs, PatchAuthMeRes> =>
|
|
73
|
+
useApiMutation<PatchAuthMeArgs, PatchAuthMeRes>(patchAuthMeRequest, {
|
|
74
|
+
invalidatesTags: ["auth"],
|
|
75
|
+
...options,
|
|
76
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
betterAuthAdapter,
|
|
3
|
+
bridgeBetterAuthReactClient,
|
|
4
|
+
createSyncDb,
|
|
5
|
+
type SyncDb,
|
|
6
|
+
} from "@terreno/syncdb";
|
|
7
|
+
import {baseUrl} from "@terreno/syncdb/config";
|
|
8
|
+
import {betterAuthClient} from "@/lib/betterAuth";
|
|
9
|
+
|
|
10
|
+
export const SYNC_DB_NAME = "{{appName}}";
|
|
11
|
+
|
|
12
|
+
/** Add synced collection names here as you register modelRouter sync scopes on the backend. */
|
|
13
|
+
export const SYNC_COLLECTIONS: string[] = [];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Shared by the sync client and the request client (store/apiClient.ts) so both send the same
|
|
17
|
+
* Better Auth session token and refresh the same way. bridgeBetterAuthReactClient turns the
|
|
18
|
+
* Better Auth session atom into push updates; pollIntervalMs is only a fallback if that atom
|
|
19
|
+
* is unavailable, so keep it slow.
|
|
20
|
+
*/
|
|
21
|
+
export const authProvider = betterAuthAdapter(bridgeBetterAuthReactClient(betterAuthClient), {
|
|
22
|
+
pollIntervalMs: 60_000,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const syncDb: SyncDb = createSyncDb({
|
|
26
|
+
authProvider,
|
|
27
|
+
baseUrl,
|
|
28
|
+
collections: SYNC_COLLECTIONS,
|
|
29
|
+
name: SYNC_DB_NAME,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Tracks whether `syncDb.start()` has resolved for the current login. Screens gate
|
|
34
|
+
* `mutate()` on it: `start()` must resolve an authenticated user before writes are safe.
|
|
35
|
+
*/
|
|
36
|
+
let syncDbReady = false;
|
|
37
|
+
const syncDbReadyListeners = new Set<() => void>();
|
|
38
|
+
|
|
39
|
+
export const setSyncDbReady = (ready: boolean): void => {
|
|
40
|
+
if (syncDbReady === ready) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
syncDbReady = ready;
|
|
44
|
+
for (const listener of syncDbReadyListeners) {
|
|
45
|
+
listener();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const subscribeSyncDbReady = (listener: () => void): (() => void) => {
|
|
50
|
+
syncDbReadyListeners.add(listener);
|
|
51
|
+
return () => {
|
|
52
|
+
syncDbReadyListeners.delete(listener);
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const getSyncDbReadySnapshot = (): boolean => syncDbReady;
|