@stacksjs/mobile 0.74.43 → 0.74.46

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/README.md CHANGED
@@ -14,6 +14,43 @@ if (isNativeMobile())
14
14
  const position = await location.getCurrentPosition()
15
15
  ```
16
16
 
17
+ ## Native bridge availability
18
+
19
+ `mobile` is a typed service collection (`MobileApi`), available in both browsers
20
+ and native apps. Its services keep Craft's existing web fallbacks. Use
21
+ `getNativeMobileBridge()` or `mobile.nativeBridge` for native-only feature checks:
22
+
23
+ ```ts
24
+ import { getNativeMobileBridge, mobile } from '@stacksjs/mobile'
25
+
26
+ const bridge = getNativeMobileBridge()
27
+ if (bridge?.capabilities.backgroundLocation === true)
28
+ await mobile.location.startRecording({ enableHighAccuracy: true })
29
+
30
+ const unsubscribe = mobile.onReady(() => {
31
+ const current = mobile.nativeBridge
32
+ const hasHealth = current?.platform === 'ios'
33
+ ? current.capabilities.healthKit === true
34
+ : current?.capabilities.health === true
35
+ // Update capability-specific UI with hasHealth.
36
+ })
37
+ ```
38
+
39
+ Both accessors return `CraftMobileBridge | null`. They read the current host on
40
+ each call, so late bridge injection is visible without reimporting the package.
41
+ Browsers, server rendering, and desktop Craft hosts return `null`. Mobile hosts
42
+ report their native `ios` or `android` platform, independent of the user agent.
43
+ Call `unsubscribe()` when disposing the readiness listener.
44
+
45
+ `CraftMobileBridge` exposes read-only metadata, not raw native methods. Its
46
+ `capabilities` uses Craft's enabled-feature names, including `geolocation`,
47
+ `biometric`, iOS `healthKit`, and Android `health` (Health Connect). A missing or
48
+ non-boolean flag is `undefined`, never implicitly enabled. Older hosts without a
49
+ capability map return an empty map. These flags are distinct from the hardware
50
+ report returned asynchronously by `mobile.device.getCapabilities()` and do not
51
+ replace permission requests or error handling. Invoke operations through the
52
+ typed services, not through the metadata object.
53
+
17
54
  The `health` facade reads authorized Apple Health or Android Health Connect
18
55
  metrics and writes completed workouts using one payload. `watchConnectivity`
19
56
  keeps companion controls and recording state synchronized without exposing
package/dist/index.d.ts CHANGED
@@ -24,12 +24,92 @@ export declare const pushNotifications: PushNotificationsApi;
24
24
  export declare const health: HealthApi;
25
25
  export declare const liveActivities: LiveActivitiesApi;
26
26
  export declare const watchConnectivity: WatchConnectivityApi;
27
+ /** Reads the current host, including a bridge injected after this module loaded. */
28
+ export declare function getNativeMobileBridge(): CraftMobileBridge | null;
27
29
  export declare function isNativeMobile(): boolean;
28
30
  export declare function onMobileReady(callback: (event: CraftReadyEvent) => void): () => void;
29
31
  export declare function withNativeFeedback<T>(action: () => T | Promise<T>): Promise<T>;
30
- export declare const mobile: unknown;
32
+ export declare const mobile: MobileApi;
31
33
 
32
34
  // From: src/types.ts
35
+ /** Enabled native features, not the hardware report from device.getCapabilities(). */
36
+ export declare interface CraftMobileCapabilities {
37
+ haptics?: boolean
38
+ speechRecognition?: boolean
39
+ share?: boolean
40
+ camera?: boolean
41
+ biometric?: boolean
42
+ pushNotifications?: boolean
43
+ secureStorage?: boolean
44
+ geolocation?: boolean
45
+ backgroundLocation?: boolean
46
+ healthKit?: boolean
47
+ health?: boolean
48
+ liveActivities?: boolean
49
+ watchConnectivity?: boolean
50
+ clipboard?: boolean
51
+ contacts?: boolean
52
+ calendar?: boolean
53
+ localNotifications?: boolean
54
+ inAppPurchase?: boolean
55
+ keepAwake?: boolean
56
+ orientationLock?: boolean
57
+ deepLinks?: boolean
58
+ flashlight?: boolean
59
+ network?: boolean
60
+ deviceInfo?: boolean
61
+ badge?: boolean
62
+ appReview?: boolean
63
+ appBadge?: boolean
64
+ networkStatus?: boolean
65
+ openURL?: boolean
66
+ vibrationPattern?: boolean
67
+ appState?: boolean
68
+ qrScanner?: boolean
69
+ filePicker?: boolean
70
+ fileDownload?: boolean
71
+ googleSignIn?: boolean
72
+ audioRecording?: boolean
73
+ videoRecording?: boolean
74
+ motionSensors?: boolean
75
+ localDatabase?: boolean
76
+ bluetooth?: boolean
77
+ nfc?: boolean
78
+ screenCapture?: boolean
79
+ }
80
+ /** Browser-safe metadata from Craft's iOS or Android bridge. */
81
+ export declare interface CraftMobileBridge {
82
+ readonly platform: 'ios' | 'android'
83
+ readonly capabilities: Readonly<CraftMobileCapabilities>
84
+ }
85
+ export declare interface CraftReadyEvent extends Event {
86
+ detail?: { platform?: string }
87
+ }
88
+ /** Services retain their web fallbacks; nativeBridge describes only a native host. */
89
+ export declare interface MobileApi {
90
+ readonly nativeBridge: CraftMobileBridge | null
91
+ biometrics: BiometricsApi
92
+ camera: CameraApi
93
+ device: DeviceApi
94
+ haptics: HapticsApi
95
+ lifecycle: LifecycleApi
96
+ location: LocationApi
97
+ notifications: NotificationsApi
98
+ permissions: PermissionsApi
99
+ secureStorage: SecureStorageApi
100
+ share: ShareApi
101
+ appReview: AppReviewApi
102
+ deepLinks: DeepLinksApi
103
+ keepAwake: KeepAwakeApi
104
+ network: NetworkApi
105
+ pushNotifications: PushNotificationsApi
106
+ health: HealthApi
107
+ liveActivities: LiveActivitiesApi
108
+ watchConnectivity: WatchConnectivityApi
109
+ isNativeMobile: () => boolean
110
+ onReady: (callback: (event: CraftReadyEvent) => void) => () => void
111
+ withFeedback: <T>(action: () => T | Promise<T>) => Promise<T>
112
+ }
33
113
  export declare interface DeviceInfo {
34
114
  platform: 'ios' | 'android' | 'macos' | 'windows' | 'linux'
35
115
  osVersion: string
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import*as p from"craft-native/mobile";var{biometrics:f,camera:A,device:u,haptics:l,lifecycle:v,location:h,notifications:w,permissions:m,secureStorage:d,share:y,appReview:k,deepLinks:n,keepAwake:x,network:L,pushNotifications:R,health:g,liveActivities:C,watchConnectivity:N}=p,b=f,S=A,c=u,o=l,E=v,H=h,P=w,D=m,T=d,M=y,B=k;function r(t){if(typeof t==="string")return t.trim()||null;if(!t||typeof t!=="object")return null;let e=t.url;return typeof e==="string"&&e.trim()?e:null}var K={async getInitialURL(){return r(await n.getInitialURL?.())},onLink(t){return n.onLink?.((e)=>{let i=r(e);if(i)t(i)})??(()=>{})}},U=x,W=L,F=R,I=g,j=C,q=N;function a(){if(typeof globalThis>"u")return;return globalThis}function z(){let t=a();return Boolean(t?.craft)&&c.isMobile()}function G(t){let e=a();if(!e)return()=>{};if(e.craft)return queueMicrotask(()=>t(new Event("craftReady"))),()=>{};let i=(s)=>t(s);return e.addEventListener("craftReady",i,{once:!0}),()=>e.removeEventListener("craftReady",i)}async function J(t){await o.impact("light");try{let e=await t();return await o.notification("success"),e}catch(e){throw await o.notification("error"),e}}var O={biometrics:b,camera:S,device:c,haptics:o,lifecycle:E,location:H,notifications:P,permissions:D,secureStorage:T,share:M,appReview:B,deepLinks:K,keepAwake:U,network:W,pushNotifications:F,health:I,liveActivities:j,watchConnectivity:q,isNativeMobile:z,onReady:G,withFeedback:J};export{B as appReview,b as biometrics,S as camera,K as deepLinks,c as device,o as haptics,I as health,z as isNativeMobile,U as keepAwake,E as lifecycle,j as liveActivities,H as location,O as mobile,W as network,r as normalizeDeepLinkURL,P as notifications,G as onMobileReady,D as permissions,F as pushNotifications,T as secureStorage,M as share,q as watchConnectivity,J as withNativeFeedback};
1
+ import*as p from"craft-native/mobile";var{biometrics:f,camera:l,device:u,haptics:A,lifecycle:v,location:d,notifications:m,permissions:w,secureStorage:y,share:h,appReview:k,deepLinks:a,keepAwake:x,network:L,pushNotifications:b,health:g,liveActivities:R,watchConnectivity:C}=p,N=f,E=l,S=u,o=A,H=v,M=d,P=m,B=w,D=y,j=h,T=k;function c(t){if(typeof t==="string")return t.trim()||null;if(!t||typeof t!=="object")return null;let e=t.url;return typeof e==="string"&&e.trim()?e:null}var K={async getInitialURL(){return c(await a.getInitialURL?.())},onLink(t){return a.onLink?.((e)=>{let i=c(e);if(i)t(i)})??(()=>{})}},U=x,W=L,F=b,I=g,O=R,q=C;function s(){if(typeof window>"u")return;return window}function n(){let t=s()?.craft;if(!t||typeof t!=="object")return null;if(!("platform"in t)||t.platform!=="ios"&&t.platform!=="android")return null;let e="capabilities"in t?t.capabilities:void 0,i=e&&typeof e==="object"&&!Array.isArray(e)?Object.fromEntries(Object.entries(e).filter(([,r])=>typeof r==="boolean")):{};return{platform:t.platform,capabilities:i}}function z(){return n()!==null}function G(t){let e=s();if(!e)return()=>{};if(n()){let r=!1;return queueMicrotask(()=>{if(!r)t(new Event("craftReady"))}),()=>{r=!0}}let i=(r)=>{if(!n())return;e.removeEventListener("craftReady",i),t(r)};return e.addEventListener("craftReady",i),()=>e.removeEventListener("craftReady",i)}async function J(t){await o.impact("light");try{let e=await t();return await o.notification("success"),e}catch(e){throw await o.notification("error"),e}}var Q={get nativeBridge(){return n()},biometrics:N,camera:E,device:S,haptics:o,lifecycle:H,location:M,notifications:P,permissions:B,secureStorage:D,share:j,appReview:T,deepLinks:K,keepAwake:U,network:W,pushNotifications:F,health:I,liveActivities:O,watchConnectivity:q,isNativeMobile:z,onReady:G,withFeedback:J};export{T as appReview,N as biometrics,E as camera,K as deepLinks,S as device,n as getNativeMobileBridge,o as haptics,I as health,z as isNativeMobile,U as keepAwake,H as lifecycle,O as liveActivities,M as location,Q as mobile,W as network,c as normalizeDeepLinkURL,P as notifications,G as onMobileReady,B as permissions,F as pushNotifications,D as secureStorage,j as share,q as watchConnectivity,J as withNativeFeedback};
package/dist/types.d.ts CHANGED
@@ -1,3 +1,81 @@
1
+ /** Enabled native features, not the hardware report from device.getCapabilities(). */
2
+ export declare interface CraftMobileCapabilities {
3
+ haptics?: boolean
4
+ speechRecognition?: boolean
5
+ share?: boolean
6
+ camera?: boolean
7
+ biometric?: boolean
8
+ pushNotifications?: boolean
9
+ secureStorage?: boolean
10
+ geolocation?: boolean
11
+ backgroundLocation?: boolean
12
+ healthKit?: boolean
13
+ health?: boolean
14
+ liveActivities?: boolean
15
+ watchConnectivity?: boolean
16
+ clipboard?: boolean
17
+ contacts?: boolean
18
+ calendar?: boolean
19
+ localNotifications?: boolean
20
+ inAppPurchase?: boolean
21
+ keepAwake?: boolean
22
+ orientationLock?: boolean
23
+ deepLinks?: boolean
24
+ flashlight?: boolean
25
+ network?: boolean
26
+ deviceInfo?: boolean
27
+ badge?: boolean
28
+ appReview?: boolean
29
+ appBadge?: boolean
30
+ networkStatus?: boolean
31
+ openURL?: boolean
32
+ vibrationPattern?: boolean
33
+ appState?: boolean
34
+ qrScanner?: boolean
35
+ filePicker?: boolean
36
+ fileDownload?: boolean
37
+ googleSignIn?: boolean
38
+ audioRecording?: boolean
39
+ videoRecording?: boolean
40
+ motionSensors?: boolean
41
+ localDatabase?: boolean
42
+ bluetooth?: boolean
43
+ nfc?: boolean
44
+ screenCapture?: boolean
45
+ }
46
+ /** Browser-safe metadata from Craft's iOS or Android bridge. */
47
+ export declare interface CraftMobileBridge {
48
+ readonly platform: 'ios' | 'android'
49
+ readonly capabilities: Readonly<CraftMobileCapabilities>
50
+ }
51
+ export declare interface CraftReadyEvent extends Event {
52
+ detail?: { platform?: string }
53
+ }
54
+ /** Services retain their web fallbacks; nativeBridge describes only a native host. */
55
+ export declare interface MobileApi {
56
+ readonly nativeBridge: CraftMobileBridge | null
57
+ biometrics: BiometricsApi
58
+ camera: CameraApi
59
+ device: DeviceApi
60
+ haptics: HapticsApi
61
+ lifecycle: LifecycleApi
62
+ location: LocationApi
63
+ notifications: NotificationsApi
64
+ permissions: PermissionsApi
65
+ secureStorage: SecureStorageApi
66
+ share: ShareApi
67
+ appReview: AppReviewApi
68
+ deepLinks: DeepLinksApi
69
+ keepAwake: KeepAwakeApi
70
+ network: NetworkApi
71
+ pushNotifications: PushNotificationsApi
72
+ health: HealthApi
73
+ liveActivities: LiveActivitiesApi
74
+ watchConnectivity: WatchConnectivityApi
75
+ isNativeMobile: () => boolean
76
+ onReady: (callback: (event: CraftReadyEvent) => void) => () => void
77
+ withFeedback: <T>(action: () => T | Promise<T>) => Promise<T>
78
+ }
1
79
  export declare interface DeviceInfo {
2
80
  platform: 'ios' | 'android' | 'macos' | 'windows' | 'linux'
3
81
  osVersion: string
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/mobile",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.74.43",
5
+ "version": "0.74.46",
6
6
  "description": "Craft-powered native mobile runtime for Stacks and STX applications",
7
7
  "author": "Chris Breuer",
8
8
  "license": "MIT",
@@ -35,7 +35,7 @@
35
35
  "craft-native": ">=0.0.72"
36
36
  },
37
37
  "devDependencies": {
38
- "@stacksjs/build": "0.74.43",
38
+ "@stacksjs/build": "0.74.46",
39
39
  "better-dx": "^0.2.24"
40
40
  }
41
41
  }