@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.
Files changed (356) hide show
  1. package/LICENSE +183 -183
  2. package/README.md +364 -2
  3. package/app/(t0,t1,t2,t3,t4)/[...path].js +38 -0
  4. package/app/(t0,t1,t2,t3,t4)/_layout.js +48 -0
  5. package/app/(t0,t1,t2,t3,t4)/index.js +38 -0
  6. package/app/+native-intent.js +39 -0
  7. package/app/_layout.js +36 -0
  8. package/app.config.ts +183 -0
  9. package/appLinks.js +65 -0
  10. package/applyShellPlugins.js +43 -0
  11. package/authCallbackIntentFilters.js +46 -0
  12. package/babel.config.js +25 -0
  13. package/checkShellConfig.js +172 -0
  14. package/checkShellPlugins.js +74 -0
  15. package/getPrivacyManifest.js +39 -0
  16. package/metro.config.js +73 -0
  17. package/native/LowdefyAppShortcuts.swift.template +84 -0
  18. package/native/withAppShortcuts.js +60 -0
  19. package/native-fingerprint.json +16 -0
  20. package/package.json +120 -40
  21. package/profiles.js +607 -0
  22. package/readShellConfig.js +36 -0
  23. package/resolveShellPlugins.js +109 -0
  24. package/runtimeVersion.js +112 -0
  25. package/scripts/checkReleaseBundle.mjs +358 -0
  26. package/scripts/createDayjsLocales.mjs +67 -0
  27. package/scripts/createShellAssets.mjs +54 -0
  28. package/scripts/createShellVersion.mjs +83 -0
  29. package/scripts/createTypeManifest.mjs +205 -0
  30. package/shell.dev.json +11 -0
  31. package/shellVariant.js +126 -0
  32. package/src/ShellContext.js +32 -0
  33. package/src/actions/CopyToClipboard.js +29 -0
  34. package/src/actions/CopyToClipboard.test.js +33 -0
  35. package/src/actions/GeolocationCurrentPosition.js +61 -0
  36. package/src/actions/GeolocationCurrentPosition.test.js +79 -0
  37. package/src/actions/GetLocalStorage.js +36 -0
  38. package/src/actions/RemoveLocalStorage.js +31 -0
  39. package/src/actions/ScrollTo.js +66 -0
  40. package/src/actions/ScrollTo.test.js +83 -0
  41. package/src/actions/SetFocus.js +33 -0
  42. package/src/actions/SetFocus.test.js +49 -0
  43. package/src/actions/SetLocalStorage.js +34 -0
  44. package/src/actions/localStorage.test.js +68 -0
  45. package/src/actions/measureInScrollView.js +36 -0
  46. package/src/actions/nativeActions.js +41 -0
  47. package/src/auth/AuthButton.js +44 -0
  48. package/src/auth/AuthGate.js +57 -0
  49. package/src/auth/AuthLink.js +43 -0
  50. package/src/auth/LockScreen.js +67 -0
  51. package/src/auth/LockScreen.test.js +37 -0
  52. package/src/auth/SignInScreen.js +268 -0
  53. package/src/auth/SignInScreen.test.js +187 -0
  54. package/src/auth/createAppAuth.js +325 -0
  55. package/src/auth/createAppAuth.test.js +424 -0
  56. package/src/auth/createAuthApi.js +115 -0
  57. package/src/auth/createAuthApi.test.js +138 -0
  58. package/src/auth/createCoreAuth.js +96 -0
  59. package/src/auth/createCoreAuth.test.js +188 -0
  60. package/src/auth/createDeviceKey.js +62 -0
  61. package/src/auth/createDeviceKey.test.js +54 -0
  62. package/src/auth/createSessionStores.js +45 -0
  63. package/src/auth/createTokenStore.js +103 -0
  64. package/src/auth/createTokenStore.test.js +116 -0
  65. package/src/auth/getAuthSettings.js +24 -0
  66. package/src/auth/getAuthView.js +39 -0
  67. package/src/auth/getAuthView.test.js +52 -0
  68. package/src/auth/getSheetReturnUrl.js +35 -0
  69. package/src/auth/getTokenStoreOptions.js +46 -0
  70. package/src/auth/isBackgroundShell.js +26 -0
  71. package/src/auth/isSheetReturnLink.js +35 -0
  72. package/src/auth/parseQuery.js +39 -0
  73. package/src/auth/purgeAppData.js +80 -0
  74. package/src/auth/purgeAppData.test.js +104 -0
  75. package/src/auth/shouldLock.js +26 -0
  76. package/src/auth/signInWithSheet.js +97 -0
  77. package/src/auth/signInWithSheet.test.js +205 -0
  78. package/src/auth/styles.js +78 -0
  79. package/src/auth/unregisterPushToken.js +62 -0
  80. package/src/auth/unregisterPushToken.test.js +67 -0
  81. package/src/auth/usesBiometricUnlock.js +23 -0
  82. package/src/auth/watchSession.js +47 -0
  83. package/src/auth/withoutCookies.js +26 -0
  84. package/src/cache/createSqliteCacheStore.js +113 -0
  85. package/src/cache/createSqliteCacheStore.test.js +93 -0
  86. package/src/cache/getCacheKey.js +25 -0
  87. package/src/cache/getLastCacheKeyName.js +22 -0
  88. package/src/components/AppProvider.js +118 -0
  89. package/src/components/Frame.js +116 -0
  90. package/src/components/Frame.test.js +101 -0
  91. package/src/components/LoadingScreen.js +35 -0
  92. package/src/components/MenuButton.js +121 -0
  93. package/src/components/MenuButton.test.js +74 -0
  94. package/src/components/MessageScreen.js +51 -0
  95. package/src/components/NavigationTheme.js +33 -0
  96. package/src/components/NoConnectionBanner.js +51 -0
  97. package/src/components/NoConnectionBanner.test.js +73 -0
  98. package/src/components/OtherPageScreen.js +95 -0
  99. package/src/components/OtherPageScreen.test.js +74 -0
  100. package/src/components/PageScreen.js +254 -0
  101. package/src/components/PageScreen.test.js +492 -0
  102. package/src/components/RouteErrorScreen.js +47 -0
  103. package/src/components/ShellRoot.js +34 -0
  104. package/src/components/ShellRoot.test.js +62 -0
  105. package/src/components/ShellThemeProvider.js +45 -0
  106. package/src/components/SwitchToTab.js +36 -0
  107. package/src/components/SwitchToTab.test.js +63 -0
  108. package/src/components/TabIconImages.js +72 -0
  109. package/src/components/UpdateScreen.js +40 -0
  110. package/src/components/getNavigationTheme.js +44 -0
  111. package/src/components/getNavigationTheme.test.js +41 -0
  112. package/src/components/styles.js +50 -0
  113. package/src/components/useSurfaceTokens.js +47 -0
  114. package/src/config/getAppApiBase.js +34 -0
  115. package/src/config/getShellConfig.js +37 -0
  116. package/src/config/getShellConfig.test.js +80 -0
  117. package/src/createShellApp.js +126 -0
  118. package/src/dev/DevShell.js +239 -0
  119. package/src/dev/DevShell.test.js +233 -0
  120. package/src/dev/connect/ConfirmSheet.js +71 -0
  121. package/src/dev/connect/ConnectScreen.js +117 -0
  122. package/src/dev/connect/QrScanner.js +89 -0
  123. package/src/dev/connection/createDevShellConfig.js +47 -0
  124. package/src/dev/connection/createDevTransport.js +51 -0
  125. package/src/dev/connection/createDevTransport.test.js +87 -0
  126. package/src/dev/connection/createServerList.js +48 -0
  127. package/src/dev/connection/isLoopbackHost.js +25 -0
  128. package/src/dev/connection/pairedShellApp.test.js +104 -0
  129. package/src/dev/connection/parseDevLink.js +66 -0
  130. package/src/dev/connection/parseDevLink.test.js +97 -0
  131. package/src/dev/connection/parseQuery.js +33 -0
  132. package/src/dev/connection/parseServerInput.js +40 -0
  133. package/src/dev/connection/parseServerUrl.js +45 -0
  134. package/src/dev/devStyles.js +97 -0
  135. package/src/dev/index.js +21 -0
  136. package/src/dev/inject/createDeviceInjections.js +188 -0
  137. package/src/dev/inject/createDeviceInjections.test.js +224 -0
  138. package/src/dev/inject/createOfflineFetch.js +35 -0
  139. package/src/dev/inject/createOfflineFetch.test.js +68 -0
  140. package/src/dev/inject/injectPush.js +60 -0
  141. package/src/dev/inject/injectShare.js +55 -0
  142. package/src/dev/inject/withInjectedDevice.js +98 -0
  143. package/src/dev/menu/BlockGallery.js +33 -0
  144. package/src/dev/menu/DevMenu.js +121 -0
  145. package/src/dev/menu/createLogBuffer.js +83 -0
  146. package/src/dev/menu/watchShake.js +52 -0
  147. package/src/dev/menu/watchShake.test.js +61 -0
  148. package/src/dev/recorder/createNativeRecorder.js +204 -0
  149. package/src/dev/recorder/createNativeRecorder.test.js +307 -0
  150. package/src/dev/redirectDevLink.js +30 -0
  151. package/src/dev/redirectDevLink.test.js +49 -0
  152. package/src/dev/steps/createStepRunner.js +184 -0
  153. package/src/dev/steps/createStepRunner.test.js +262 -0
  154. package/src/dev/steps/findBlock.js +78 -0
  155. package/src/dev/steps/isContextSettled.js +37 -0
  156. package/src/dev/steps/waitForSettle.js +39 -0
  157. package/src/dev/steps/waitUntil.js +40 -0
  158. package/src/dev/tab/DevTabClient.js +97 -0
  159. package/src/dev/tab/answerEval.js +39 -0
  160. package/src/dev/tab/answerInspect.js +45 -0
  161. package/src/dev/tab/createDevTabClient.js +272 -0
  162. package/src/dev/tab/createDevTabClient.test.js +317 -0
  163. package/src/dev/tab/describeTabError.js +35 -0
  164. package/src/dev/tab/devClientMarker.js +22 -0
  165. package/src/dev/tab/devClientMarker.test.js +23 -0
  166. package/src/dev/tab/findPageContext.js +43 -0
  167. package/src/dev/tab/getShownPage.js +42 -0
  168. package/src/dev/tab/readEventStream.js +68 -0
  169. package/src/dev/tab/reloadApp.js +42 -0
  170. package/src/dev/tab/reloadApp.test.js +115 -0
  171. package/src/device/DeviceHosts.js +32 -0
  172. package/src/device/basic/createFilesFeature.js +41 -0
  173. package/src/device/basic/createHapticsFeature.js +46 -0
  174. package/src/device/basic/createKeepAwakeFeature.js +37 -0
  175. package/src/device/basic/createPhotosFeature.js +40 -0
  176. package/src/device/basic/createRequestQueueFeature.js +37 -0
  177. package/src/device/basic/createShareFeature.js +63 -0
  178. package/src/device/basic/createShareFeature.test.js +47 -0
  179. package/src/device/basic/createSpeechFeature.js +43 -0
  180. package/src/device/basic/createUploadFeature.js +41 -0
  181. package/src/device/basic/toImagePickerOptions.js +41 -0
  182. package/src/device/basic/toPickedFile.js +54 -0
  183. package/src/device/calendar/index.js +68 -0
  184. package/src/device/contacts/index.js +67 -0
  185. package/src/device/createDevice.js +65 -0
  186. package/src/device/device.js +27 -0
  187. package/src/device/field/MapView.js +93 -0
  188. package/src/device/field/ScanCodeSheet.js +86 -0
  189. package/src/device/field/ScannerView.js +52 -0
  190. package/src/device/field/createAudioFeature.js +70 -0
  191. package/src/device/field/createBrightnessFeature.js +40 -0
  192. package/src/device/field/createCameraFeature.js +56 -0
  193. package/src/device/field/createDocumentScannerFeature.js +51 -0
  194. package/src/device/field/createLocationFeature.js +39 -0
  195. package/src/device/field/createScanRequests.js +55 -0
  196. package/src/device/field/index.js +39 -0
  197. package/src/device/field/scanRequests.js +22 -0
  198. package/src/device/injected.js +60 -0
  199. package/src/device/injected.test.js +39 -0
  200. package/src/device/nfc/index.js +89 -0
  201. package/src/device/nfc/toNfcRecord.js +44 -0
  202. package/src/device/nfc/toNfcRecord.test.js +48 -0
  203. package/src/device/tracking/createBacklog.js +63 -0
  204. package/src/device/tracking/createBacklog.test.js +47 -0
  205. package/src/device/tracking/index.js +211 -0
  206. package/src/device/tracking/index.test.js +110 -0
  207. package/src/host/createDeviceStore.js +32 -0
  208. package/src/host/createMemoryStore.js +32 -0
  209. package/src/host/createNetworkStatus.js +94 -0
  210. package/src/host/createNetworkStatus.test.js +94 -0
  211. package/src/host/createRouterAdapter.js +33 -0
  212. package/src/host/createShellFetch.js +62 -0
  213. package/src/host/createShellFetch.test.js +165 -0
  214. package/src/host/createShellGlobals.js +62 -0
  215. package/src/host/createShellHost.js +83 -0
  216. package/src/host/createShellHost.test.js +36 -0
  217. package/src/host/getOrigin.js +31 -0
  218. package/src/host/isNetworkError.js +24 -0
  219. package/src/host/isOnline.js +22 -0
  220. package/src/host/networkStatus.js +24 -0
  221. package/src/host/readServerError.js +45 -0
  222. package/src/host/readServerError.test.js +85 -0
  223. package/src/host/reportShellError.js +47 -0
  224. package/src/host/reportShellError.test.js +79 -0
  225. package/src/host/withNetworkErrors.js +38 -0
  226. package/src/host/withNetworkErrors.test.js +61 -0
  227. package/src/host/withShellHeader.js +25 -0
  228. package/src/host/withShellHeader.test.js +28 -0
  229. package/src/js/evaluateJsEntries.js +43 -0
  230. package/src/js/evaluateJsEntries.test.js +48 -0
  231. package/src/locale/createShellLocale.js +112 -0
  232. package/src/locale/createShellLocale.test.js +74 -0
  233. package/src/locale/dayjsLocales.js +165 -0
  234. package/src/locale/getDeviceLocales.js +23 -0
  235. package/src/locale/loadDayjsLocale.js +32 -0
  236. package/src/locale/resolveActiveLocale.js +46 -0
  237. package/src/locale/resolveActiveLocale.test.js +40 -0
  238. package/src/native/backgroundFetch.js +97 -0
  239. package/src/native/callEndpoint.js +63 -0
  240. package/src/native/createAppEventTrigger.js +70 -0
  241. package/src/native/currentApp.js +49 -0
  242. package/src/native/getPagePath.js +32 -0
  243. package/src/native/openPath.js +55 -0
  244. package/src/native/openPath.test.js +39 -0
  245. package/src/native/parseShellLink.js +38 -0
  246. package/src/native/parseShellLink.test.js +29 -0
  247. package/src/native/startAppServices.js +94 -0
  248. package/src/native/startAppServices.test.js +97 -0
  249. package/src/native/startShareIn.js +59 -0
  250. package/src/native/startShortcuts.js +88 -0
  251. package/src/native/startShortcuts.test.js +59 -0
  252. package/src/native/toSharePayload.js +47 -0
  253. package/src/native/toSharePayload.test.js +47 -0
  254. package/src/native/whenAppReady.js +48 -0
  255. package/src/navigation/SlotContext.js +24 -0
  256. package/src/navigation/frameSlots.js +27 -0
  257. package/src/navigation/getFrame.js +30 -0
  258. package/src/navigation/getInitialScreenOptions.js +63 -0
  259. package/src/navigation/getInitialScreenOptions.test.js +66 -0
  260. package/src/navigation/getPresentation.js +28 -0
  261. package/src/navigation/getSlotRootPath.js +27 -0
  262. package/src/navigation/getStackOptions.js +185 -0
  263. package/src/navigation/getTabHref.js +45 -0
  264. package/src/navigation/getTabIcons.js +43 -0
  265. package/src/navigation/isPageUnderHeader.js +46 -0
  266. package/src/navigation/navigation.test.js +342 -0
  267. package/src/navigation/toAppLinkPath.js +43 -0
  268. package/src/navigation/toAppLinkPath.test.js +53 -0
  269. package/src/navigation/toRouterHref.js +29 -0
  270. package/src/notifications/createNotificationsFeature.js +61 -0
  271. package/src/notifications/getCategoryAction.js +31 -0
  272. package/src/notifications/handleNotificationResponse.js +82 -0
  273. package/src/notifications/registerPushToken.js +71 -0
  274. package/src/notifications/startNotifications.js +114 -0
  275. package/src/notifications/toCategoryActions.js +42 -0
  276. package/src/notifications/toNotificationEvent.js +36 -0
  277. package/src/notifications/toScheduleRequest.js +62 -0
  278. package/src/plugins/appTypes.js +28 -0
  279. package/src/plugins/prefixTypes.js +28 -0
  280. package/src/plugins/shellPlugins.json +15 -0
  281. package/src/plugins/typeManifest.js +789 -0
  282. package/src/plugins/types.js +64 -0
  283. package/src/plugins/types.test.js +173 -0
  284. package/src/polyfills/installGetRandomValues.js +28 -0
  285. package/src/queue/createRequestQueue.js +198 -0
  286. package/src/queue/createRequestQueue.test.js +215 -0
  287. package/src/queue/createSqliteQueueStore.js +121 -0
  288. package/src/queue/summarizeQueue.js +39 -0
  289. package/src/session/createAppSession.js +323 -0
  290. package/src/session/createAppSession.test.js +391 -0
  291. package/src/session/getPageView.js +59 -0
  292. package/src/session/getPageView.test.js +137 -0
  293. package/src/sync/encodePagePath.js +27 -0
  294. package/src/sync/fetchPage.js +72 -0
  295. package/src/sync/fetchPage.test.js +84 -0
  296. package/src/sync/fetchRoot.js +43 -0
  297. package/src/sync/fetchUser.js +33 -0
  298. package/src/sync/runLimited.js +36 -0
  299. package/src/sync/toRootConfig.js +35 -0
  300. package/src/test/createFakeNetwork.js +40 -0
  301. package/src/test/createFakeSecureStore.js +40 -0
  302. package/src/test/createFakeServer.js +98 -0
  303. package/src/test/createMemoryCacheStore.js +59 -0
  304. package/src/test/createNetworkError.js +24 -0
  305. package/src/test/expoImage.js +42 -0
  306. package/src/test/expoNotifications.js +40 -0
  307. package/src/test/expoQuickActions.js +22 -0
  308. package/src/test/expoSpeechRecognition.js +31 -0
  309. package/src/test/expoTaskManager.js +25 -0
  310. package/src/test/expoUi.js +55 -0
  311. package/src/test/expoUiBottomSheet.js +64 -0
  312. package/src/test/expoUiDateTimePicker.js +26 -0
  313. package/src/test/expoUiMenu.js +46 -0
  314. package/src/test/expoUiSegmentedControl.js +47 -0
  315. package/src/test/expoUiSwiftUi.js +62 -0
  316. package/src/test/expoUiSwiftUiModifiers.js +23 -0
  317. package/src/test/expoWebBrowser.js +23 -0
  318. package/src/test/fixtures.js +98 -0
  319. package/src/test/nativeModule.js +33 -0
  320. package/src/test/renderPageScreen.js +109 -0
  321. package/src/types/checkTypeSet.js +39 -0
  322. package/src/types/collectPageTypes.js +49 -0
  323. package/src/types/collectPageTypes.test.js +90 -0
  324. package/src/types/findMissingPageTypes.js +40 -0
  325. package/src/version/compareVersions.js +38 -0
  326. package/src/version/isShellTooOld.js +30 -0
  327. package/src/version/shellVersion.js +21 -0
  328. package/src/version/shellVersion.test.js +32 -0
  329. package/src/widgets/fetchWidgetData.js +59 -0
  330. package/src/widgets/index.js +156 -0
  331. package/src/widgets/refreshWidgets.js +31 -0
  332. package/withExcludedNativeModules.js +93 -0
  333. package/dist/location.js +0 -33
  334. package/dist/public/apple-touch-icon.png +0 -0
  335. package/dist/public/icon-32.png +0 -0
  336. package/dist/public/icon-512.png +0 -0
  337. package/dist/public/icon.svg +0 -17
  338. package/dist/public/logo-dark-theme.png +0 -0
  339. package/dist/public/logo-light-theme.png +0 -0
  340. package/dist/public/logo-square-dark-theme.png +0 -0
  341. package/dist/public/logo-square-light-theme.png +0 -0
  342. package/dist/public/manifest.webmanifest +0 -16
  343. package/dist/shell/246.93432ff68186aae6c87b.js +0 -2
  344. package/dist/shell/246.93432ff68186aae6c87b.js.LICENSE.txt +0 -8
  345. package/dist/shell/454.b597ca21366afda000af.js +0 -2
  346. package/dist/shell/454.b597ca21366afda000af.js.LICENSE.txt +0 -14
  347. package/dist/shell/484.d645e6c09079f45ce044.js +0 -1
  348. package/dist/shell/706.a66931e02a549fd269bd.js +0 -2
  349. package/dist/shell/706.a66931e02a549fd269bd.js.LICENSE.txt +0 -8
  350. package/dist/shell/9.78be8c1b6c3986f404fc.js +0 -2
  351. package/dist/shell/9.78be8c1b6c3986f404fc.js.LICENSE.txt +0 -45
  352. package/dist/shell/922.43b7a3ebe22ef05b3049.js +0 -2
  353. package/dist/shell/922.43b7a3ebe22ef05b3049.js.LICENSE.txt +0 -14
  354. package/dist/shell/index.html +0 -57
  355. package/dist/shell/main.bde87d1433f0625faed6.js +0 -1
  356. package/dist/shell/runtime.1ead93159ba56c05b969.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
+ }
@@ -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;