@lowdefy/shell 3.23.3 → 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.f01501a75909f38ccc32.js +0 -1
- package/dist/shell/runtime.e50f6c3a5f7c74a0a717.js +0 -1
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
/*
|
|
18
|
+
Writes a shell's Android adaptive-icon foreground from its 1024×1024 icon: the icon scaled into
|
|
19
|
+
the centre 66% of a transparent 1024×1024 canvas, the safe zone a launcher's mask never cuts.
|
|
20
|
+
app.config.ts runs it when the shell's icon has no foreground yet (the app config is evaluated
|
|
21
|
+
synchronously, and image code is asynchronous).
|
|
22
|
+
|
|
23
|
+
node scripts/createShellAssets.mjs <icon.png> <foreground.png>
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import fs from 'node:fs';
|
|
27
|
+
import { createRequire } from 'node:module';
|
|
28
|
+
import { fileURLToPath } from 'node:url';
|
|
29
|
+
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const Jimp = require('jimp-compact');
|
|
32
|
+
|
|
33
|
+
const SIZE = 1024;
|
|
34
|
+
const SAFE_ZONE = 0.66;
|
|
35
|
+
|
|
36
|
+
async function createAdaptiveIconForeground({ icon, output }) {
|
|
37
|
+
const image = await Jimp.read(icon);
|
|
38
|
+
const inner = Math.round(SIZE * SAFE_ZONE);
|
|
39
|
+
image.contain(inner, inner);
|
|
40
|
+
const canvas = new Jimp(SIZE, SIZE, 0x00000000);
|
|
41
|
+
canvas.composite(image, Math.round((SIZE - inner) / 2), Math.round((SIZE - inner) / 2));
|
|
42
|
+
await canvas.writeAsync(output);
|
|
43
|
+
return output;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (fs.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
47
|
+
const [icon, output] = process.argv.slice(2);
|
|
48
|
+
if (icon === undefined || output === undefined) {
|
|
49
|
+
throw new Error('Usage: node scripts/createShellAssets.mjs <icon.png> <foreground.png>');
|
|
50
|
+
}
|
|
51
|
+
await createAdaptiveIconForeground({ icon, output });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { createAdaptiveIconForeground };
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
/*
|
|
18
|
+
The shell's version: the @lowdefy/shell package version, which the shell sends as
|
|
19
|
+
`x-lowdefy-shell` and compares with an app's `mobile.minShellVersion`
|
|
20
|
+
(src/version/isShellTooOld.js).
|
|
21
|
+
|
|
22
|
+
Written to src/version/shellVersion.js, which the shell's JavaScript imports
|
|
23
|
+
(src/config/getShellConfig.js). The version travels in the JavaScript bundle, not the Expo config,
|
|
24
|
+
so it is the version of the code that runs: an embedded launch reads the Expo config the native
|
|
25
|
+
build embedded, which a shell running newer JavaScript from Metro has outgrown. Only the
|
|
26
|
+
version is written, not package.json, which would put every dependency name in the bundle. The
|
|
27
|
+
file is committed; src/version/shellVersion.test.js fails when it is stale, and
|
|
28
|
+
`pnpm release:version` rewrites it after the version bump.
|
|
29
|
+
|
|
30
|
+
node scripts/createShellVersion.mjs prints the version
|
|
31
|
+
node scripts/createShellVersion.mjs --write writes src/version/shellVersion.js
|
|
32
|
+
|
|
33
|
+
Usage: pnpm --filter=@lowdefy/shell shell-version
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import fs from 'node:fs';
|
|
37
|
+
import path from 'node:path';
|
|
38
|
+
import { fileURLToPath } from 'node:url';
|
|
39
|
+
|
|
40
|
+
const shellRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
41
|
+
|
|
42
|
+
const HEADER = `/*
|
|
43
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
44
|
+
|
|
45
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
46
|
+
you may not use this file except in compliance with the License.
|
|
47
|
+
You may obtain a copy of the License at
|
|
48
|
+
|
|
49
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
50
|
+
|
|
51
|
+
Unless required by applicable law or agreed to in writing, software
|
|
52
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
53
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
54
|
+
See the License for the specific language governing permissions and
|
|
55
|
+
limitations under the License.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
// Generated by scripts/createShellVersion.mjs from package.json. Do not edit.
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
function createShellVersion() {
|
|
62
|
+
const { version } = JSON.parse(fs.readFileSync(path.join(shellRoot, 'package.json'), 'utf8'));
|
|
63
|
+
return version;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function createShellVersionModule() {
|
|
67
|
+
return `${HEADER}\nconst shellVersion = ${JSON.stringify(
|
|
68
|
+
createShellVersion()
|
|
69
|
+
)};\n\nexport default shellVersion;\n`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
73
|
+
if (process.argv.includes('--write')) {
|
|
74
|
+
fs.writeFileSync(
|
|
75
|
+
path.join(shellRoot, 'src/version/shellVersion.js'),
|
|
76
|
+
createShellVersionModule()
|
|
77
|
+
);
|
|
78
|
+
} else {
|
|
79
|
+
process.stdout.write(createShellVersion());
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default createShellVersion;
|
|
@@ -0,0 +1,205 @@
|
|
|
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
|
+
/*
|
|
18
|
+
The type manifest: every block, action and operator type this shell carries, with the plugin
|
|
19
|
+
package that provides it and that package's major version. A page renders only when the shell
|
|
20
|
+
carries every type its type set names at the same major version (src/types/checkTypeSet.js).
|
|
21
|
+
|
|
22
|
+
Made from src/plugins/shellPlugins.json, the plugin packages the shell registers
|
|
23
|
+
(src/plugins/types.js), and the shell's plugins (shell.json `plugins`, read with
|
|
24
|
+
readShellConfig.js): each one's `./types` `native.blocks` and `native.actions`, named with its
|
|
25
|
+
typePrefix, as src/plugins/appTypes.js registers them. A plugin without a `./types` export adds
|
|
26
|
+
none. Written to src/plugins/typeManifest.js, which the shell's JavaScript
|
|
27
|
+
imports (src/config/getShellConfig.js). The manifest travels in the JavaScript bundle, not the
|
|
28
|
+
Expo config, so it always matches the code that runs: a type added to the plugin maps reaches a
|
|
29
|
+
shell with an over-the-air update or from Metro, without a native build. The file is committed;
|
|
30
|
+
src/plugins/types.test.js fails when it is stale, and `pnpm release:version` rewrites it after
|
|
31
|
+
the version bump. `lowdefy mobile config` rewrites it in a copied shell, with that shell's
|
|
32
|
+
plugins.
|
|
33
|
+
|
|
34
|
+
node scripts/createTypeManifest.mjs prints the manifest as JSON
|
|
35
|
+
node scripts/createTypeManifest.mjs --write writes src/plugins/typeManifest.js
|
|
36
|
+
|
|
37
|
+
Usage: pnpm --filter=@lowdefy/shell type-manifest
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import fs from 'node:fs';
|
|
41
|
+
import { createRequire } from 'node:module';
|
|
42
|
+
import path from 'node:path';
|
|
43
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
44
|
+
|
|
45
|
+
const shellRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
46
|
+
const require = createRequire(import.meta.url);
|
|
47
|
+
const readShellConfig = require('../readShellConfig.js');
|
|
48
|
+
|
|
49
|
+
const NATIVE_CATEGORIES = ['actions', 'blocks'];
|
|
50
|
+
|
|
51
|
+
const CATEGORIES = ['actions', 'blocks', 'operators'];
|
|
52
|
+
|
|
53
|
+
const HEADER = `/*
|
|
54
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
55
|
+
|
|
56
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
57
|
+
you may not use this file except in compliance with the License.
|
|
58
|
+
You may obtain a copy of the License at
|
|
59
|
+
|
|
60
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
61
|
+
|
|
62
|
+
Unless required by applicable law or agreed to in writing, software
|
|
63
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
64
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
65
|
+
See the License for the specific language governing permissions and
|
|
66
|
+
limitations under the License.
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
// Generated by scripts/createTypeManifest.mjs from src/plugins/shellPlugins.json and shell.json
|
|
70
|
+
// \`plugins\`. Do not edit.
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
// The plugin packages export no package.json, so it is found above the resolved types file.
|
|
74
|
+
function readMajorVersion(packageName) {
|
|
75
|
+
let directory = path.dirname(require.resolve(`${packageName}/types`));
|
|
76
|
+
while (!fs.existsSync(path.join(directory, 'package.json'))) {
|
|
77
|
+
directory = path.dirname(directory);
|
|
78
|
+
}
|
|
79
|
+
const { name, version } = JSON.parse(
|
|
80
|
+
fs.readFileSync(path.join(directory, 'package.json'), 'utf8')
|
|
81
|
+
);
|
|
82
|
+
if (name !== packageName) {
|
|
83
|
+
throw new Error(`Could not find the package.json of "${packageName}".`);
|
|
84
|
+
}
|
|
85
|
+
return Number(version.split('.')[0]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getPackageTypeNames({ category, types }) {
|
|
89
|
+
if (category === 'operators') {
|
|
90
|
+
return types.operators.client;
|
|
91
|
+
}
|
|
92
|
+
return types[category];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Two packages giving one type name would leave one of them unreachable on a page.
|
|
96
|
+
function addType({ category, manifest, name, packageName }) {
|
|
97
|
+
const existing = manifest[category][name];
|
|
98
|
+
if (existing !== undefined && existing.package !== packageName) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Shell plugin "${packageName}" gives the ${category.slice(0, -1)} type "${name}", which "${
|
|
101
|
+
existing.package
|
|
102
|
+
}" already gives. Set a typePrefix on the plugin in mobile.shells[].plugins.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
manifest[category][name] = { package: packageName, version: readMajorVersion(packageName) };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// A shell plugin need not export "./types": a native-only plugin, such as a wrapped SDK, gives no
|
|
109
|
+
// types. Resolving a missing export throws, so the plugin's own package.json says, found on the
|
|
110
|
+
// lookup paths of `require` (its exports need not list package.json).
|
|
111
|
+
function hasTypesExport(packageName) {
|
|
112
|
+
const packageJsonPath = require.resolve
|
|
113
|
+
.paths(packageName)
|
|
114
|
+
.map((directory) => path.join(directory, packageName, 'package.json'))
|
|
115
|
+
.find((filePath) => fs.existsSync(filePath));
|
|
116
|
+
if (packageJsonPath === undefined) {
|
|
117
|
+
throw new Error(`Shell plugin "${packageName}" is not installed.`);
|
|
118
|
+
}
|
|
119
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
120
|
+
return packageJson.exports?.['./types'] !== undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// A shell plugin's native type names, from its `./types` `native: { blocks, actions }`.
|
|
124
|
+
async function addShellPluginTypes({ manifest, plugin }) {
|
|
125
|
+
const { default: types } = await import(pathToFileURL(require.resolve(`${plugin.name}/types`)));
|
|
126
|
+
NATIVE_CATEGORIES.forEach((category) => {
|
|
127
|
+
const names = types.native?.[category] ?? [];
|
|
128
|
+
if (!Array.isArray(names)) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Shell plugin "${
|
|
131
|
+
plugin.name
|
|
132
|
+
}" "./types" native.${category} should be an array of type names. Received ${JSON.stringify(
|
|
133
|
+
names
|
|
134
|
+
)}.`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
names.forEach((name) => {
|
|
138
|
+
addType({
|
|
139
|
+
category,
|
|
140
|
+
manifest,
|
|
141
|
+
name: `${plugin.typePrefix ?? ''}${name}`,
|
|
142
|
+
packageName: plugin.name,
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function readShellPlugins() {
|
|
149
|
+
return JSON.parse(fs.readFileSync(path.join(shellRoot, 'src/plugins/shellPlugins.json'), 'utf8'));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function createTypeManifest({
|
|
153
|
+
plugins = readShellPlugins(),
|
|
154
|
+
shellPlugins = readShellConfig().shell.plugins ?? [],
|
|
155
|
+
} = {}) {
|
|
156
|
+
const manifest = { actions: {}, blocks: {}, operators: {} };
|
|
157
|
+
for (const plugin of plugins) {
|
|
158
|
+
if (!CATEGORIES.includes(plugin.category)) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Shell plugin "${plugin.package}" has an unknown category. Received ${JSON.stringify(
|
|
161
|
+
plugin.category
|
|
162
|
+
)}.`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
const { default: types } = await import(`${plugin.package}/types`);
|
|
166
|
+
const packageTypeNames = getPackageTypeNames({ category: plugin.category, types });
|
|
167
|
+
const names = plugin.types ?? packageTypeNames;
|
|
168
|
+
names.forEach((name) => {
|
|
169
|
+
if (!packageTypeNames.includes(name)) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Shell plugin "${plugin.package}" has no ${plugin.category} type "${name}".`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
addType({ category: plugin.category, manifest, name, packageName: plugin.package });
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
for (const plugin of shellPlugins) {
|
|
178
|
+
if (hasTypesExport(plugin.name)) {
|
|
179
|
+
await addShellPluginTypes({ manifest, plugin });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return manifest;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function createTypeManifestModule() {
|
|
186
|
+
const manifest = await createTypeManifest();
|
|
187
|
+
return `${HEADER}\nconst typeManifest = ${JSON.stringify(
|
|
188
|
+
manifest,
|
|
189
|
+
null,
|
|
190
|
+
2
|
|
191
|
+
)};\n\nexport default typeManifest;\n`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
195
|
+
if (process.argv.includes('--write')) {
|
|
196
|
+
fs.writeFileSync(
|
|
197
|
+
path.join(shellRoot, 'src/plugins/typeManifest.js'),
|
|
198
|
+
await createTypeManifestModule()
|
|
199
|
+
);
|
|
200
|
+
} else {
|
|
201
|
+
process.stdout.write(JSON.stringify(await createTypeManifest()));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export default createTypeManifest;
|
package/shell.dev.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "lowdefy-dev",
|
|
3
|
+
"name": "Lowdefy Dev",
|
|
4
|
+
"profile": "dev",
|
|
5
|
+
"scheme": "lowdefy-dev",
|
|
6
|
+
"environment": "dev",
|
|
7
|
+
"ios": { "bundleIdentifier": "com.lowdefy.dev" },
|
|
8
|
+
"android": { "package": "com.lowdefy.dev" },
|
|
9
|
+
"store": {},
|
|
10
|
+
"app": { "id": "dev", "name": "Lowdefy Dev", "urls": { "dev": "http://localhost:3000" } }
|
|
11
|
+
}
|
package/shellVariant.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
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
|
+
const {
|
|
18
|
+
applyExtensions,
|
|
19
|
+
DEV_ENTRY,
|
|
20
|
+
EXTENSIONS,
|
|
21
|
+
PROFILES,
|
|
22
|
+
mergeProfiles,
|
|
23
|
+
pluginName,
|
|
24
|
+
resolveProfile,
|
|
25
|
+
} = require('./profiles.js');
|
|
26
|
+
|
|
27
|
+
const VARIANTS = ['dev', 'release'];
|
|
28
|
+
|
|
29
|
+
// The string the dev-tab client sends to the dev server. scripts/checkReleaseBundle.mjs fails when a
|
|
30
|
+
// release bundle holds it. Keep it in step with src/dev/tab/devClientMarker.js.
|
|
31
|
+
const DEV_CLIENT_MARKER = 'lowdefy-dev-tab-client';
|
|
32
|
+
|
|
33
|
+
// Everything any profile or extension carries: every module, entry and permission a build can
|
|
34
|
+
// leave out.
|
|
35
|
+
const EVERYTHING = applyExtensions(
|
|
36
|
+
Object.keys(PROFILES).reduce(
|
|
37
|
+
(all, name) => mergeProfiles(all, resolveProfile(name)),
|
|
38
|
+
resolveProfile('basic')
|
|
39
|
+
),
|
|
40
|
+
Object.keys(EXTENSIONS)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// What the `dev` profile adds itself: the developer features, which a release build never carries.
|
|
44
|
+
const DEV_ONLY = PROFILES.dev;
|
|
45
|
+
|
|
46
|
+
// The shell's capability profile: shell.json `profile`, else `dev` for Lowdefy Dev and `basic` for
|
|
47
|
+
// any other shell.
|
|
48
|
+
function getProfileName({ shell, variant }) {
|
|
49
|
+
return shell.profile ?? (variant === 'dev' ? 'dev' : 'basic');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* What a shell build of this variant carries: its profile, resolved (profiles.js), with its
|
|
54
|
+
* extensions (shell.json `extensions`). The dev variant also carries Lowdefy Dev's developer
|
|
55
|
+
* features, whatever the profile; a release build carries none of them.
|
|
56
|
+
*/
|
|
57
|
+
function getShellProfile({ shell, variant }) {
|
|
58
|
+
const profile = applyExtensions(
|
|
59
|
+
resolveProfile(getProfileName({ shell, variant })),
|
|
60
|
+
shell.extensions
|
|
61
|
+
);
|
|
62
|
+
if (variant === 'dev') {
|
|
63
|
+
return mergeProfiles(profile, { ...resolveProfile('dev'), name: profile.name });
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
...profile,
|
|
67
|
+
modules: Object.fromEntries(
|
|
68
|
+
Object.entries(profile.modules).filter(([name]) => !(name in DEV_ONLY.modules))
|
|
69
|
+
),
|
|
70
|
+
plugins: profile.plugins.filter((plugin) => !(pluginName(plugin) in DEV_ONLY.modules)),
|
|
71
|
+
permissions: profile.permissions.filter(
|
|
72
|
+
(permission) => !DEV_ONLY.permissions.includes(permission)
|
|
73
|
+
),
|
|
74
|
+
jsEntries: profile.jsEntries.filter((entry) => !DEV_ONLY.jsEntries.includes(entry)),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* What a shell build of this variant leaves out: every module, JavaScript entry and permission some
|
|
80
|
+
* profile carries and its own does not. app.config.ts leaves the modules unlinked and their config
|
|
81
|
+
* plugins unapplied (withExcludedNativeModules.js) and blocks the permissions; metro.config.js
|
|
82
|
+
* resolves the entries to empty modules.
|
|
83
|
+
*
|
|
84
|
+
* @returns {{ modules: Object<string, string|null>, jsEntries: string[], permissions: string[] }}
|
|
85
|
+
* modules as { [package]: nativeModuleName }.
|
|
86
|
+
*/
|
|
87
|
+
function getExclusions({ shell, variant }) {
|
|
88
|
+
const profile = getShellProfile({ shell, variant });
|
|
89
|
+
return {
|
|
90
|
+
modules: Object.fromEntries(
|
|
91
|
+
Object.entries(EVERYTHING.modules).filter(([name]) => !(name in profile.modules))
|
|
92
|
+
),
|
|
93
|
+
jsEntries: EVERYTHING.jsEntries.filter((entry) => !profile.jsEntries.includes(entry)),
|
|
94
|
+
permissions: EVERYTHING.permissions.filter(
|
|
95
|
+
(permission) => !profile.permissions.includes(permission)
|
|
96
|
+
),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// The native modules a shell build leaves unlinked, as { [package]: nativeModuleName }.
|
|
101
|
+
function getExcludedModules({ shell, variant }) {
|
|
102
|
+
return getExclusions({ shell, variant }).modules;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// LOWDEFY_SHELL_VARIANT=dev | release. Without it, Lowdefy Dev (no LOWDEFY_SHELL_CONFIG) is dev and
|
|
106
|
+
// every other shell is release, so a customer shell leaves the developer features out unless its
|
|
107
|
+
// build asks for them.
|
|
108
|
+
function getShellVariant(env = process.env) {
|
|
109
|
+
const variant = env.LOWDEFY_SHELL_VARIANT || (env.LOWDEFY_SHELL_CONFIG ? 'release' : 'dev');
|
|
110
|
+
if (!VARIANTS.includes(variant)) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`LOWDEFY_SHELL_VARIANT must be "dev" or "release". Received ${JSON.stringify(variant)}.`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return variant;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = {
|
|
119
|
+
DEV_CLIENT_MARKER,
|
|
120
|
+
DEV_ENTRY,
|
|
121
|
+
getExcludedModules,
|
|
122
|
+
getExclusions,
|
|
123
|
+
getProfileName,
|
|
124
|
+
getShellProfile,
|
|
125
|
+
getShellVariant,
|
|
126
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
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 { createContext, useContext, useSyncExternalStore } from 'react';
|
|
18
|
+
|
|
19
|
+
// { auth, session, host, locale, shell } of the app the shell has open.
|
|
20
|
+
const ShellContext = createContext(null);
|
|
21
|
+
|
|
22
|
+
function useShell() {
|
|
23
|
+
return useContext(ShellContext);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function useSessionState() {
|
|
27
|
+
const { session } = useShell();
|
|
28
|
+
return useSyncExternalStore(session.subscribe, session.getState);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { useSessionState, useShell };
|
|
32
|
+
export default ShellContext;
|
|
@@ -0,0 +1,29 @@
|
|
|
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 { type } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
// The native body of actions-core's CopyToClipboard, with its params ({ copy }), through
|
|
20
|
+
// expo-clipboard (globals.clipboard).
|
|
21
|
+
async function CopyToClipboard({ globals, params }) {
|
|
22
|
+
const { copy } = params;
|
|
23
|
+
if (!type.isString(copy)) {
|
|
24
|
+
throw new Error(`CopyToClipboard requires "copy" text. Received ${JSON.stringify(copy)}.`);
|
|
25
|
+
}
|
|
26
|
+
await globals.clipboard.setString(copy);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default CopyToClipboard;
|
|
@@ -0,0 +1,33 @@
|
|
|
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 { jest } from '@jest/globals';
|
|
18
|
+
|
|
19
|
+
import CopyToClipboard from './CopyToClipboard.js';
|
|
20
|
+
|
|
21
|
+
test('CopyToClipboard copies the text through the shell clipboard', async () => {
|
|
22
|
+
const clipboard = { setString: jest.fn(async () => undefined) };
|
|
23
|
+
await CopyToClipboard({ globals: { clipboard }, params: { copy: 'JOB-42' } });
|
|
24
|
+
expect(clipboard.setString.mock.calls).toEqual([['JOB-42']]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('CopyToClipboard throws when there is no text to copy', async () => {
|
|
28
|
+
const clipboard = { setString: jest.fn(async () => undefined) };
|
|
29
|
+
await expect(CopyToClipboard({ globals: { clipboard }, params: {} })).rejects.toThrow(
|
|
30
|
+
'CopyToClipboard requires "copy" text. Received undefined.'
|
|
31
|
+
);
|
|
32
|
+
expect(clipboard.setString).not.toHaveBeenCalled();
|
|
33
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
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 { type } from '@lowdefy/helpers';
|
|
18
|
+
import { getDeviceFeature, hasDeviceFeature } from '@lowdefy/blocks-native/device';
|
|
19
|
+
|
|
20
|
+
import injected from '../device/injected.js';
|
|
21
|
+
|
|
22
|
+
function numberOrNull(value) {
|
|
23
|
+
return type.isNumber(value) ? value : null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The native body of actions-core's GeolocationCurrentPosition, answering as the web body does:
|
|
28
|
+
* `{ coords, timestamp }`, or `{ code: 1, error: 'PERMISSION_DENIED', message }` when location is
|
|
29
|
+
* refused. A location Lowdefy Dev injected (src/device/injected.js) answers first; otherwise the
|
|
30
|
+
* shell's location feature reads the phone (profile field and up), and a shell without it throws.
|
|
31
|
+
*/
|
|
32
|
+
async function GeolocationCurrentPosition({ params }) {
|
|
33
|
+
if (injected('permission:location') === 'denied') {
|
|
34
|
+
return { code: 1, error: 'PERMISSION_DENIED', message: 'Location permission was denied.' };
|
|
35
|
+
}
|
|
36
|
+
const location = injected('location');
|
|
37
|
+
if (type.isUndefined(location)) {
|
|
38
|
+
if (!hasDeviceFeature('location')) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
'GeolocationCurrentPosition needs a shell that carries location (profile field). In Lowdefy Dev, inject a location.'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return getDeviceFeature({ feature: 'location', action: 'GeolocationCurrentPosition' }).current(
|
|
44
|
+
params ?? {}
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
coords: {
|
|
49
|
+
accuracy: numberOrNull(location.accuracy),
|
|
50
|
+
altitude: numberOrNull(location.altitude),
|
|
51
|
+
altitudeAccuracy: numberOrNull(location.altitudeAccuracy),
|
|
52
|
+
heading: numberOrNull(location.heading),
|
|
53
|
+
latitude: location.latitude,
|
|
54
|
+
longitude: location.longitude,
|
|
55
|
+
speed: numberOrNull(location.speed),
|
|
56
|
+
},
|
|
57
|
+
timestamp: location.timestamp ?? Date.now(),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default GeolocationCurrentPosition;
|