@lowdefy/shell 3.23.2 → 7.2.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.
- package/LICENSE +183 -183
- package/README.md +364 -2
- package/app/(t0,t1,t2,t3,t4)/[...path].js +38 -0
- package/app/(t0,t1,t2,t3,t4)/_layout.js +48 -0
- package/app/(t0,t1,t2,t3,t4)/index.js +38 -0
- package/app/+native-intent.js +39 -0
- package/app/_layout.js +36 -0
- package/app.config.ts +183 -0
- package/appLinks.js +65 -0
- package/applyShellPlugins.js +43 -0
- package/authCallbackIntentFilters.js +46 -0
- package/babel.config.js +25 -0
- package/checkShellConfig.js +172 -0
- package/checkShellPlugins.js +74 -0
- package/getPrivacyManifest.js +39 -0
- package/metro.config.js +73 -0
- package/native/LowdefyAppShortcuts.swift.template +84 -0
- package/native/withAppShortcuts.js +60 -0
- package/native-fingerprint.json +16 -0
- package/package.json +120 -40
- package/profiles.js +607 -0
- package/readShellConfig.js +36 -0
- package/resolveShellPlugins.js +109 -0
- package/runtimeVersion.js +112 -0
- package/scripts/checkReleaseBundle.mjs +358 -0
- package/scripts/createDayjsLocales.mjs +67 -0
- package/scripts/createShellAssets.mjs +54 -0
- package/scripts/createShellVersion.mjs +83 -0
- package/scripts/createTypeManifest.mjs +205 -0
- package/shell.dev.json +11 -0
- package/shellVariant.js +126 -0
- package/src/ShellContext.js +32 -0
- package/src/actions/CopyToClipboard.js +29 -0
- package/src/actions/CopyToClipboard.test.js +33 -0
- package/src/actions/GeolocationCurrentPosition.js +61 -0
- package/src/actions/GeolocationCurrentPosition.test.js +79 -0
- package/src/actions/GetLocalStorage.js +36 -0
- package/src/actions/RemoveLocalStorage.js +31 -0
- package/src/actions/ScrollTo.js +66 -0
- package/src/actions/ScrollTo.test.js +83 -0
- package/src/actions/SetFocus.js +33 -0
- package/src/actions/SetFocus.test.js +49 -0
- package/src/actions/SetLocalStorage.js +34 -0
- package/src/actions/localStorage.test.js +68 -0
- package/src/actions/measureInScrollView.js +36 -0
- package/src/actions/nativeActions.js +41 -0
- package/src/auth/AuthButton.js +44 -0
- package/src/auth/AuthGate.js +57 -0
- package/src/auth/AuthLink.js +43 -0
- package/src/auth/LockScreen.js +67 -0
- package/src/auth/LockScreen.test.js +37 -0
- package/src/auth/SignInScreen.js +268 -0
- package/src/auth/SignInScreen.test.js +187 -0
- package/src/auth/createAppAuth.js +325 -0
- package/src/auth/createAppAuth.test.js +424 -0
- package/src/auth/createAuthApi.js +115 -0
- package/src/auth/createAuthApi.test.js +138 -0
- package/src/auth/createCoreAuth.js +96 -0
- package/src/auth/createCoreAuth.test.js +188 -0
- package/src/auth/createDeviceKey.js +62 -0
- package/src/auth/createDeviceKey.test.js +54 -0
- package/src/auth/createSessionStores.js +45 -0
- package/src/auth/createTokenStore.js +103 -0
- package/src/auth/createTokenStore.test.js +116 -0
- package/src/auth/getAuthSettings.js +24 -0
- package/src/auth/getAuthView.js +39 -0
- package/src/auth/getAuthView.test.js +52 -0
- package/src/auth/getSheetReturnUrl.js +35 -0
- package/src/auth/getTokenStoreOptions.js +46 -0
- package/src/auth/isBackgroundShell.js +26 -0
- package/src/auth/isSheetReturnLink.js +35 -0
- package/src/auth/parseQuery.js +39 -0
- package/src/auth/purgeAppData.js +80 -0
- package/src/auth/purgeAppData.test.js +104 -0
- package/src/auth/shouldLock.js +26 -0
- package/src/auth/signInWithSheet.js +97 -0
- package/src/auth/signInWithSheet.test.js +205 -0
- package/src/auth/styles.js +78 -0
- package/src/auth/unregisterPushToken.js +62 -0
- package/src/auth/unregisterPushToken.test.js +67 -0
- package/src/auth/usesBiometricUnlock.js +23 -0
- package/src/auth/watchSession.js +47 -0
- package/src/auth/withoutCookies.js +26 -0
- package/src/cache/createSqliteCacheStore.js +113 -0
- package/src/cache/createSqliteCacheStore.test.js +93 -0
- package/src/cache/getCacheKey.js +25 -0
- package/src/cache/getLastCacheKeyName.js +22 -0
- package/src/components/AppProvider.js +118 -0
- package/src/components/Frame.js +116 -0
- package/src/components/Frame.test.js +101 -0
- package/src/components/LoadingScreen.js +35 -0
- package/src/components/MenuButton.js +121 -0
- package/src/components/MenuButton.test.js +74 -0
- package/src/components/MessageScreen.js +51 -0
- package/src/components/NavigationTheme.js +33 -0
- package/src/components/NoConnectionBanner.js +51 -0
- package/src/components/NoConnectionBanner.test.js +73 -0
- package/src/components/OtherPageScreen.js +95 -0
- package/src/components/OtherPageScreen.test.js +74 -0
- package/src/components/PageScreen.js +254 -0
- package/src/components/PageScreen.test.js +492 -0
- package/src/components/RouteErrorScreen.js +47 -0
- package/src/components/ShellRoot.js +34 -0
- package/src/components/ShellRoot.test.js +62 -0
- package/src/components/ShellThemeProvider.js +45 -0
- package/src/components/SwitchToTab.js +36 -0
- package/src/components/SwitchToTab.test.js +63 -0
- package/src/components/TabIconImages.js +72 -0
- package/src/components/UpdateScreen.js +40 -0
- package/src/components/getNavigationTheme.js +44 -0
- package/src/components/getNavigationTheme.test.js +41 -0
- package/src/components/styles.js +50 -0
- package/src/components/useSurfaceTokens.js +47 -0
- package/src/config/getAppApiBase.js +34 -0
- package/src/config/getShellConfig.js +37 -0
- package/src/config/getShellConfig.test.js +80 -0
- package/src/createShellApp.js +126 -0
- package/src/dev/DevShell.js +239 -0
- package/src/dev/DevShell.test.js +233 -0
- package/src/dev/connect/ConfirmSheet.js +71 -0
- package/src/dev/connect/ConnectScreen.js +117 -0
- package/src/dev/connect/QrScanner.js +89 -0
- package/src/dev/connection/createDevShellConfig.js +47 -0
- package/src/dev/connection/createDevTransport.js +51 -0
- package/src/dev/connection/createDevTransport.test.js +87 -0
- package/src/dev/connection/createServerList.js +48 -0
- package/src/dev/connection/isLoopbackHost.js +25 -0
- package/src/dev/connection/pairedShellApp.test.js +104 -0
- package/src/dev/connection/parseDevLink.js +66 -0
- package/src/dev/connection/parseDevLink.test.js +97 -0
- package/src/dev/connection/parseQuery.js +33 -0
- package/src/dev/connection/parseServerInput.js +40 -0
- package/src/dev/connection/parseServerUrl.js +45 -0
- package/src/dev/devStyles.js +97 -0
- package/src/dev/index.js +21 -0
- package/src/dev/inject/createDeviceInjections.js +188 -0
- package/src/dev/inject/createDeviceInjections.test.js +224 -0
- package/src/dev/inject/createOfflineFetch.js +35 -0
- package/src/dev/inject/createOfflineFetch.test.js +68 -0
- package/src/dev/inject/injectPush.js +60 -0
- package/src/dev/inject/injectShare.js +55 -0
- package/src/dev/inject/withInjectedDevice.js +98 -0
- package/src/dev/menu/BlockGallery.js +33 -0
- package/src/dev/menu/DevMenu.js +121 -0
- package/src/dev/menu/createLogBuffer.js +83 -0
- package/src/dev/menu/watchShake.js +52 -0
- package/src/dev/menu/watchShake.test.js +61 -0
- package/src/dev/recorder/createNativeRecorder.js +204 -0
- package/src/dev/recorder/createNativeRecorder.test.js +307 -0
- package/src/dev/redirectDevLink.js +30 -0
- package/src/dev/redirectDevLink.test.js +49 -0
- package/src/dev/steps/createStepRunner.js +184 -0
- package/src/dev/steps/createStepRunner.test.js +262 -0
- package/src/dev/steps/findBlock.js +78 -0
- package/src/dev/steps/isContextSettled.js +37 -0
- package/src/dev/steps/waitForSettle.js +39 -0
- package/src/dev/steps/waitUntil.js +40 -0
- package/src/dev/tab/DevTabClient.js +97 -0
- package/src/dev/tab/answerEval.js +39 -0
- package/src/dev/tab/answerInspect.js +45 -0
- package/src/dev/tab/createDevTabClient.js +272 -0
- package/src/dev/tab/createDevTabClient.test.js +317 -0
- package/src/dev/tab/describeTabError.js +35 -0
- package/src/dev/tab/devClientMarker.js +22 -0
- package/src/dev/tab/devClientMarker.test.js +23 -0
- package/src/dev/tab/findPageContext.js +43 -0
- package/src/dev/tab/getShownPage.js +42 -0
- package/src/dev/tab/readEventStream.js +68 -0
- package/src/dev/tab/reloadApp.js +42 -0
- package/src/dev/tab/reloadApp.test.js +115 -0
- package/src/device/DeviceHosts.js +32 -0
- package/src/device/basic/createFilesFeature.js +41 -0
- package/src/device/basic/createHapticsFeature.js +46 -0
- package/src/device/basic/createKeepAwakeFeature.js +37 -0
- package/src/device/basic/createPhotosFeature.js +40 -0
- package/src/device/basic/createRequestQueueFeature.js +37 -0
- package/src/device/basic/createShareFeature.js +63 -0
- package/src/device/basic/createShareFeature.test.js +47 -0
- package/src/device/basic/createSpeechFeature.js +43 -0
- package/src/device/basic/createUploadFeature.js +41 -0
- package/src/device/basic/toImagePickerOptions.js +41 -0
- package/src/device/basic/toPickedFile.js +54 -0
- package/src/device/calendar/index.js +68 -0
- package/src/device/contacts/index.js +67 -0
- package/src/device/createDevice.js +65 -0
- package/src/device/device.js +27 -0
- package/src/device/field/MapView.js +93 -0
- package/src/device/field/ScanCodeSheet.js +86 -0
- package/src/device/field/ScannerView.js +52 -0
- package/src/device/field/createAudioFeature.js +70 -0
- package/src/device/field/createBrightnessFeature.js +40 -0
- package/src/device/field/createCameraFeature.js +56 -0
- package/src/device/field/createDocumentScannerFeature.js +51 -0
- package/src/device/field/createLocationFeature.js +39 -0
- package/src/device/field/createScanRequests.js +55 -0
- package/src/device/field/index.js +39 -0
- package/src/device/field/scanRequests.js +22 -0
- package/src/device/injected.js +60 -0
- package/src/device/injected.test.js +39 -0
- package/src/device/nfc/index.js +89 -0
- package/src/device/nfc/toNfcRecord.js +44 -0
- package/src/device/nfc/toNfcRecord.test.js +48 -0
- package/src/device/tracking/createBacklog.js +63 -0
- package/src/device/tracking/createBacklog.test.js +47 -0
- package/src/device/tracking/index.js +211 -0
- package/src/device/tracking/index.test.js +110 -0
- package/src/host/createDeviceStore.js +32 -0
- package/src/host/createMemoryStore.js +32 -0
- package/src/host/createNetworkStatus.js +94 -0
- package/src/host/createNetworkStatus.test.js +94 -0
- package/src/host/createRouterAdapter.js +33 -0
- package/src/host/createShellFetch.js +62 -0
- package/src/host/createShellFetch.test.js +165 -0
- package/src/host/createShellGlobals.js +62 -0
- package/src/host/createShellHost.js +83 -0
- package/src/host/createShellHost.test.js +36 -0
- package/src/host/getOrigin.js +31 -0
- package/src/host/isNetworkError.js +24 -0
- package/src/host/isOnline.js +22 -0
- package/src/host/networkStatus.js +24 -0
- package/src/host/readServerError.js +45 -0
- package/src/host/readServerError.test.js +85 -0
- package/src/host/reportShellError.js +47 -0
- package/src/host/reportShellError.test.js +79 -0
- package/src/host/withNetworkErrors.js +38 -0
- package/src/host/withNetworkErrors.test.js +61 -0
- package/src/host/withShellHeader.js +25 -0
- package/src/host/withShellHeader.test.js +28 -0
- package/src/js/evaluateJsEntries.js +43 -0
- package/src/js/evaluateJsEntries.test.js +48 -0
- package/src/locale/createShellLocale.js +112 -0
- package/src/locale/createShellLocale.test.js +74 -0
- package/src/locale/dayjsLocales.js +165 -0
- package/src/locale/getDeviceLocales.js +23 -0
- package/src/locale/loadDayjsLocale.js +32 -0
- package/src/locale/resolveActiveLocale.js +46 -0
- package/src/locale/resolveActiveLocale.test.js +40 -0
- package/src/native/backgroundFetch.js +97 -0
- package/src/native/callEndpoint.js +63 -0
- package/src/native/createAppEventTrigger.js +70 -0
- package/src/native/currentApp.js +49 -0
- package/src/native/getPagePath.js +32 -0
- package/src/native/openPath.js +55 -0
- package/src/native/openPath.test.js +39 -0
- package/src/native/parseShellLink.js +38 -0
- package/src/native/parseShellLink.test.js +29 -0
- package/src/native/startAppServices.js +94 -0
- package/src/native/startAppServices.test.js +97 -0
- package/src/native/startShareIn.js +59 -0
- package/src/native/startShortcuts.js +88 -0
- package/src/native/startShortcuts.test.js +59 -0
- package/src/native/toSharePayload.js +47 -0
- package/src/native/toSharePayload.test.js +47 -0
- package/src/native/whenAppReady.js +48 -0
- package/src/navigation/SlotContext.js +24 -0
- package/src/navigation/frameSlots.js +27 -0
- package/src/navigation/getFrame.js +30 -0
- package/src/navigation/getInitialScreenOptions.js +63 -0
- package/src/navigation/getInitialScreenOptions.test.js +66 -0
- package/src/navigation/getPresentation.js +28 -0
- package/src/navigation/getSlotRootPath.js +27 -0
- package/src/navigation/getStackOptions.js +185 -0
- package/src/navigation/getTabHref.js +45 -0
- package/src/navigation/getTabIcons.js +43 -0
- package/src/navigation/isPageUnderHeader.js +46 -0
- package/src/navigation/navigation.test.js +342 -0
- package/src/navigation/toAppLinkPath.js +43 -0
- package/src/navigation/toAppLinkPath.test.js +53 -0
- package/src/navigation/toRouterHref.js +29 -0
- package/src/notifications/createNotificationsFeature.js +61 -0
- package/src/notifications/getCategoryAction.js +31 -0
- package/src/notifications/handleNotificationResponse.js +82 -0
- package/src/notifications/registerPushToken.js +71 -0
- package/src/notifications/startNotifications.js +114 -0
- package/src/notifications/toCategoryActions.js +42 -0
- package/src/notifications/toNotificationEvent.js +36 -0
- package/src/notifications/toScheduleRequest.js +62 -0
- package/src/plugins/appTypes.js +28 -0
- package/src/plugins/prefixTypes.js +28 -0
- package/src/plugins/shellPlugins.json +15 -0
- package/src/plugins/typeManifest.js +789 -0
- package/src/plugins/types.js +64 -0
- package/src/plugins/types.test.js +173 -0
- package/src/polyfills/installGetRandomValues.js +28 -0
- package/src/queue/createRequestQueue.js +198 -0
- package/src/queue/createRequestQueue.test.js +215 -0
- package/src/queue/createSqliteQueueStore.js +121 -0
- package/src/queue/summarizeQueue.js +39 -0
- package/src/session/createAppSession.js +323 -0
- package/src/session/createAppSession.test.js +391 -0
- package/src/session/getPageView.js +59 -0
- package/src/session/getPageView.test.js +137 -0
- package/src/sync/encodePagePath.js +27 -0
- package/src/sync/fetchPage.js +72 -0
- package/src/sync/fetchPage.test.js +84 -0
- package/src/sync/fetchRoot.js +43 -0
- package/src/sync/fetchUser.js +33 -0
- package/src/sync/runLimited.js +36 -0
- package/src/sync/toRootConfig.js +35 -0
- package/src/test/createFakeNetwork.js +40 -0
- package/src/test/createFakeSecureStore.js +40 -0
- package/src/test/createFakeServer.js +98 -0
- package/src/test/createMemoryCacheStore.js +59 -0
- package/src/test/createNetworkError.js +24 -0
- package/src/test/expoImage.js +42 -0
- package/src/test/expoNotifications.js +40 -0
- package/src/test/expoQuickActions.js +22 -0
- package/src/test/expoSpeechRecognition.js +31 -0
- package/src/test/expoTaskManager.js +25 -0
- package/src/test/expoUi.js +55 -0
- package/src/test/expoUiBottomSheet.js +64 -0
- package/src/test/expoUiDateTimePicker.js +26 -0
- package/src/test/expoUiMenu.js +46 -0
- package/src/test/expoUiSegmentedControl.js +47 -0
- package/src/test/expoUiSwiftUi.js +62 -0
- package/src/test/expoUiSwiftUiModifiers.js +23 -0
- package/src/test/expoWebBrowser.js +23 -0
- package/src/test/fixtures.js +98 -0
- package/src/test/nativeModule.js +33 -0
- package/src/test/renderPageScreen.js +109 -0
- package/src/types/checkTypeSet.js +39 -0
- package/src/types/collectPageTypes.js +49 -0
- package/src/types/collectPageTypes.test.js +90 -0
- package/src/types/findMissingPageTypes.js +40 -0
- package/src/version/compareVersions.js +38 -0
- package/src/version/isShellTooOld.js +30 -0
- package/src/version/shellVersion.js +21 -0
- package/src/version/shellVersion.test.js +32 -0
- package/src/widgets/fetchWidgetData.js +59 -0
- package/src/widgets/index.js +156 -0
- package/src/widgets/refreshWidgets.js +31 -0
- package/withExcludedNativeModules.js +93 -0
- package/dist/location.js +0 -33
- package/dist/public/apple-touch-icon.png +0 -0
- package/dist/public/icon-32.png +0 -0
- package/dist/public/icon-512.png +0 -0
- package/dist/public/icon.svg +0 -17
- package/dist/public/logo-dark-theme.png +0 -0
- package/dist/public/logo-light-theme.png +0 -0
- package/dist/public/logo-square-dark-theme.png +0 -0
- package/dist/public/logo-square-light-theme.png +0 -0
- package/dist/public/manifest.webmanifest +0 -16
- package/dist/shell/246.93432ff68186aae6c87b.js +0 -2
- package/dist/shell/246.93432ff68186aae6c87b.js.LICENSE.txt +0 -8
- package/dist/shell/454.b597ca21366afda000af.js +0 -2
- package/dist/shell/454.b597ca21366afda000af.js.LICENSE.txt +0 -14
- package/dist/shell/484.d645e6c09079f45ce044.js +0 -1
- package/dist/shell/706.a66931e02a549fd269bd.js +0 -2
- package/dist/shell/706.a66931e02a549fd269bd.js.LICENSE.txt +0 -8
- package/dist/shell/9.78be8c1b6c3986f404fc.js +0 -2
- package/dist/shell/9.78be8c1b6c3986f404fc.js.LICENSE.txt +0 -45
- package/dist/shell/922.43b7a3ebe22ef05b3049.js +0 -2
- package/dist/shell/922.43b7a3ebe22ef05b3049.js.LICENSE.txt +0 -14
- package/dist/shell/index.html +0 -57
- package/dist/shell/main.bde87d1433f0625faed6.js +0 -1
- package/dist/shell/runtime.1ead93159ba56c05b969.js +0 -1
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { AppState } from 'react-native';
|
|
18
|
+
import * as LocalAuthentication from 'expo-local-authentication';
|
|
19
|
+
|
|
20
|
+
import createAuthApi from './createAuthApi.js';
|
|
21
|
+
import createDeviceKey from './createDeviceKey.js';
|
|
22
|
+
import createCoreAuth from './createCoreAuth.js';
|
|
23
|
+
import createSessionStores from './createSessionStores.js';
|
|
24
|
+
import createTokenStore from './createTokenStore.js';
|
|
25
|
+
import getAuthSettings from './getAuthSettings.js';
|
|
26
|
+
import isBackgroundShell from './isBackgroundShell.js';
|
|
27
|
+
import purgeAppData from './purgeAppData.js';
|
|
28
|
+
import shouldLock from './shouldLock.js';
|
|
29
|
+
import signInWithSheet from './signInWithSheet.js';
|
|
30
|
+
import unregisterPushToken from './unregisterPushToken.js';
|
|
31
|
+
import usesBiometricUnlock from './usesBiometricUnlock.js';
|
|
32
|
+
import watchSession from './watchSession.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sign-in, unlock and sign-out for one app in the shell, and its session token.
|
|
36
|
+
*
|
|
37
|
+
* State, read with getState and followed with subscribe:
|
|
38
|
+
* - `status`: 'starting' while the token is read, 'locked' behind the lock screen, else 'open'.
|
|
39
|
+
* - `hasToken`: someone is signed in.
|
|
40
|
+
* - `settings`: the app's mobile.auth (getAuthSettings), null for an app without auth; the last
|
|
41
|
+
* ones the server sent, kept on the device so a lock or a shared-device sign-out applies before
|
|
42
|
+
* the app's server answers.
|
|
43
|
+
* - `slug`: the app's slug, which names its storage keys.
|
|
44
|
+
*
|
|
45
|
+
* The session ends when the token is cleared anywhere (a 401, a Logout action, the shell's own
|
|
46
|
+
* sign-out): everything the app kept for the person is removed (purgeAppData) and onSignedOut
|
|
47
|
+
* opens the app afresh, with nothing of theirs left in memory either.
|
|
48
|
+
*
|
|
49
|
+
* @param {Object} params
|
|
50
|
+
* @param {string} params.apiBase The app server's URL.
|
|
51
|
+
* @param {{ id, name }} params.app The app, from shell.json.
|
|
52
|
+
* @param {Object} params.cacheStore The page cache.
|
|
53
|
+
* @param {Function} params.fetch The shell's fetch (shell header, no cookies).
|
|
54
|
+
* @param {{ get, set, remove, keys }} params.kv The device store.
|
|
55
|
+
* @param {Function} params.onSignedOut Called once the person's data is gone.
|
|
56
|
+
* @param {Object} params.queueStore The offline request queue, cleared with the rest.
|
|
57
|
+
* @param {{ id, scheme, profile }} params.shell The shell config.
|
|
58
|
+
* @param {Function} params.streamFetch The shell's streaming fetch (expo/fetch, sent as fetch is),
|
|
59
|
+
* watched the same way: a chat stream answered 401 ends the session too.
|
|
60
|
+
*/
|
|
61
|
+
function createAppAuth({
|
|
62
|
+
apiBase,
|
|
63
|
+
app,
|
|
64
|
+
appState = AppState,
|
|
65
|
+
cacheStore,
|
|
66
|
+
fetch,
|
|
67
|
+
kv,
|
|
68
|
+
localAuthentication = LocalAuthentication,
|
|
69
|
+
now = Date.now,
|
|
70
|
+
onSignedOut,
|
|
71
|
+
queueStore,
|
|
72
|
+
shell,
|
|
73
|
+
streamFetch,
|
|
74
|
+
}) {
|
|
75
|
+
const storedKey = `lowdefy-shell:auth:${app.id}`;
|
|
76
|
+
const stored = JSON.parse(kv.get(storedKey) ?? 'null');
|
|
77
|
+
let state = {
|
|
78
|
+
status: 'starting',
|
|
79
|
+
hasToken: false,
|
|
80
|
+
settings: stored?.settings ?? null,
|
|
81
|
+
slug: stored?.slug ?? null,
|
|
82
|
+
};
|
|
83
|
+
const listeners = new Set();
|
|
84
|
+
const cleanups = [];
|
|
85
|
+
let session = null;
|
|
86
|
+
let sessionStarted = false;
|
|
87
|
+
let lastRoot = null;
|
|
88
|
+
let backgroundedAt = null;
|
|
89
|
+
let ending = false;
|
|
90
|
+
|
|
91
|
+
const tokenStore = createTokenStore({
|
|
92
|
+
appId: app.id,
|
|
93
|
+
background: isBackgroundShell(shell),
|
|
94
|
+
getBiometric: () => usesBiometricUnlock(state.settings),
|
|
95
|
+
kv,
|
|
96
|
+
});
|
|
97
|
+
const authApi = createAuthApi({
|
|
98
|
+
apiBase,
|
|
99
|
+
fetch,
|
|
100
|
+
getDeviceKey: createDeviceKey({ appId: app.id }),
|
|
101
|
+
tokenStore,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
function getState() {
|
|
105
|
+
return state;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function setState(update) {
|
|
109
|
+
state = { ...state, ...update };
|
|
110
|
+
listeners.forEach((listener) => listener());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function subscribe(listener) {
|
|
114
|
+
listeners.add(listener);
|
|
115
|
+
return () => listeners.delete(listener);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function dispose() {
|
|
119
|
+
cleanups.splice(0).forEach((cleanup) => cleanup());
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getAppName() {
|
|
123
|
+
return session?.getState().root?.lowdefyApp?.name ?? app.name;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function rememberRoot() {
|
|
127
|
+
const { root } = session.getState();
|
|
128
|
+
if (root === null || root === lastRoot) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
lastRoot = root;
|
|
132
|
+
const update = { settings: getAuthSettings(root), slug: root.lowdefyApp?.slug ?? null };
|
|
133
|
+
kv.set(storedKey, JSON.stringify(update));
|
|
134
|
+
setState(update);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function open() {
|
|
138
|
+
setState({ status: 'open', hasToken: tokenStore.get() !== null });
|
|
139
|
+
if (!sessionStarted) {
|
|
140
|
+
sessionStarted = true;
|
|
141
|
+
session.start();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function endSession({ callServer }) {
|
|
146
|
+
if (ending) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
ending = true;
|
|
150
|
+
dispose();
|
|
151
|
+
const token = tokenStore.get();
|
|
152
|
+
if (callServer && token !== null) {
|
|
153
|
+
// Never waited on: offline, the session still ends on the phone. The push token goes first,
|
|
154
|
+
// while the session can still name whose it is.
|
|
155
|
+
unregisterPushToken({ app: { apiBase, fetch, session, shell, tokenStore } })
|
|
156
|
+
.catch(() => {})
|
|
157
|
+
.finally(() => authApi.signOut({ token }).catch(() => {}));
|
|
158
|
+
}
|
|
159
|
+
await purgeAppData({
|
|
160
|
+
appId: app.id,
|
|
161
|
+
cacheStore,
|
|
162
|
+
kv,
|
|
163
|
+
queueStore,
|
|
164
|
+
slug: state.slug,
|
|
165
|
+
tokenStore,
|
|
166
|
+
});
|
|
167
|
+
onSignedOut();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function signOut() {
|
|
171
|
+
return endSession({ callServer: true });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function lock() {
|
|
175
|
+
// A token behind biometrics is read again, with its prompt, to unlock.
|
|
176
|
+
if (tokenStore.isProtected()) {
|
|
177
|
+
tokenStore.unload();
|
|
178
|
+
}
|
|
179
|
+
setState({ status: 'locked' });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// True once unlocked. A cancelled or failed prompt leaves the app locked.
|
|
183
|
+
async function unlock() {
|
|
184
|
+
if (tokenStore.isProtected()) {
|
|
185
|
+
try {
|
|
186
|
+
await tokenStore.load();
|
|
187
|
+
} catch (error) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (tokenStore.get() === null) {
|
|
191
|
+
await endSession({ callServer: false });
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
open();
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
const result = await localAuthentication.authenticateAsync({
|
|
198
|
+
promptMessage: `Unlock ${getAppName()}`,
|
|
199
|
+
});
|
|
200
|
+
if (!result.success) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
open();
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function handleAppStateChange(next) {
|
|
208
|
+
if (next === 'background') {
|
|
209
|
+
backgroundedAt = now();
|
|
210
|
+
// A shared device signs out on every close.
|
|
211
|
+
if (state.settings?.sharedDevice === true && tokenStore.get() !== null) {
|
|
212
|
+
signOut();
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (next !== 'active') {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const locks =
|
|
220
|
+
state.status === 'open' &&
|
|
221
|
+
state.hasToken &&
|
|
222
|
+
usesBiometricUnlock(state.settings) &&
|
|
223
|
+
shouldLock({ backgroundedAt, now: now(), unlockAfter: state.settings.unlockAfter });
|
|
224
|
+
backgroundedAt = null;
|
|
225
|
+
if (locks) {
|
|
226
|
+
lock();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// The server ended the session (a 401 cleared the token): with no session to call with, the push
|
|
231
|
+
// token is not unregistered (unregisterPushToken).
|
|
232
|
+
function handleTokenChange(token) {
|
|
233
|
+
if (token === null) {
|
|
234
|
+
endSession({ callServer: false });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// The session moves onto the new caller before the screens hear of the token, so no page of
|
|
239
|
+
// the signed-out caller shows in between: handleCallerChanged shows loading at once.
|
|
240
|
+
function completeSignIn(result) {
|
|
241
|
+
if (result.status === 'signedIn') {
|
|
242
|
+
session.handleCallerChanged();
|
|
243
|
+
setState({ hasToken: true });
|
|
244
|
+
}
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function start({ session: appSession }) {
|
|
249
|
+
session = appSession;
|
|
250
|
+
cleanups.push(tokenStore.subscribe(handleTokenChange));
|
|
251
|
+
const appStateSubscription = appState.addEventListener('change', handleAppStateChange);
|
|
252
|
+
cleanups.push(() => appStateSubscription.remove());
|
|
253
|
+
cleanups.push(session.subscribe(rememberRoot));
|
|
254
|
+
const sharedDevice = state.settings?.sharedDevice === true;
|
|
255
|
+
if (tokenStore.isProtected()) {
|
|
256
|
+
if (sharedDevice) {
|
|
257
|
+
// A token stored behind biometrics before the app became a shared device: it is never
|
|
258
|
+
// unlocked into, and its sign-out needs no prompt.
|
|
259
|
+
await signOut();
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
setState({ status: 'locked' });
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
await tokenStore.load();
|
|
266
|
+
const signedIn = tokenStore.get() !== null;
|
|
267
|
+
if (signedIn && sharedDevice) {
|
|
268
|
+
// A shared phone whose app was killed without going to the background first.
|
|
269
|
+
await signOut();
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (signedIn && usesBiometricUnlock(state.settings)) {
|
|
273
|
+
setState({ status: 'locked' });
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
open();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const sessionStores = createSessionStores({ cacheStore, isEnded: () => ending, kv });
|
|
280
|
+
|
|
281
|
+
function watch(baseFetch) {
|
|
282
|
+
return watchSession({
|
|
283
|
+
fetch: baseFetch,
|
|
284
|
+
onRefused: () => {
|
|
285
|
+
const { cacheKey } = session.getState();
|
|
286
|
+
if (cacheKey !== null) {
|
|
287
|
+
cacheStore.removeEntry({ cacheKey });
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
onSessionEnded: () => tokenStore.clear(),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
const watchedFetch = watch(fetch);
|
|
294
|
+
|
|
295
|
+
const coreAuth = createCoreAuth({
|
|
296
|
+
apiBase,
|
|
297
|
+
fetch: watchedFetch,
|
|
298
|
+
getSession: () => session,
|
|
299
|
+
tokenStore,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
// The auth client the core's auth actions call (createCoreAuth).
|
|
304
|
+
coreAuth,
|
|
305
|
+
dispose,
|
|
306
|
+
fetch: watchedFetch,
|
|
307
|
+
getAppName,
|
|
308
|
+
// What the app's session reads and writes through (createSessionStores).
|
|
309
|
+
sessionStores,
|
|
310
|
+
getState,
|
|
311
|
+
sendCode: ({ email }) => authApi.sendCode({ email }),
|
|
312
|
+
signInWithCode: async ({ email, otp }) =>
|
|
313
|
+
completeSignIn(await authApi.signInWithCode({ email, otp })),
|
|
314
|
+
signInWithSheet: async () => completeSignIn(await signInWithSheet({ apiBase, authApi, shell })),
|
|
315
|
+
signOut,
|
|
316
|
+
start,
|
|
317
|
+
// The streaming fetch blocks call through (the host's streamFetch).
|
|
318
|
+
streamFetch: watch(streamFetch),
|
|
319
|
+
subscribe,
|
|
320
|
+
tokenStore,
|
|
321
|
+
unlock,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export default createAppAuth;
|