@microsoft/power-apps-native-host 0.2.22 → 0.3.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 (156) hide show
  1. package/README.md +162 -26
  2. package/compatibility/current.json +87 -0
  3. package/lib/NOTICE.txt +140 -1226
  4. package/lib/commonjs/auth/AuthContext.js +74 -18
  5. package/lib/commonjs/auth/AuthContext.web.js +32 -0
  6. package/lib/commonjs/auth/completePowerAppsAuthSession.js +16 -0
  7. package/lib/commonjs/bridge/NativePowerAppsBridge.js +20 -17
  8. package/lib/commonjs/bridge/PowerPagesBridge.js +157 -0
  9. package/lib/commonjs/bridge/bridgeState.js +40 -0
  10. package/lib/commonjs/connections/ConnectionSetupScreen.js +459 -80
  11. package/lib/commonjs/connections/PowerPlatformConnectionApi.js +946 -0
  12. package/lib/commonjs/connections/connectionMetadata.js +127 -0
  13. package/lib/commonjs/connections/connectionState.js +96 -0
  14. package/lib/commonjs/connections/disambiguate.js +19 -11
  15. package/lib/commonjs/connections/resolveConnections.js +347 -0
  16. package/lib/commonjs/connections/types.js +106 -0
  17. package/lib/commonjs/{platform → connections}/useConnectionRefs.js +128 -65
  18. package/lib/commonjs/connections/useConnectionSetup.js +111 -52
  19. package/lib/commonjs/controls/FilePicker.js +6 -0
  20. package/lib/commonjs/controls/ImagePicker.js +8 -0
  21. package/lib/commonjs/index.js +188 -6
  22. package/lib/commonjs/logger.js +9 -6
  23. package/lib/commonjs/offline/createOfflineHostActivationCallbacks.js +61 -0
  24. package/lib/commonjs/offline/useOfflineIntegration.js +12 -0
  25. package/lib/commonjs/offline/useOfflineIntegration.web.js +11 -0
  26. package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.js +46 -0
  27. package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.web.js +13 -0
  28. package/lib/commonjs/platform/DataverseDataSources.js +14 -0
  29. package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +49 -0
  30. package/lib/commonjs/platform/GlobalDiscovery.js +51 -0
  31. package/lib/commonjs/platform/PowerPlatformEnvironmentApi.js +6 -106
  32. package/lib/commonjs/provider/PowerAppsHostProvider.js +156 -25
  33. package/lib/commonjs/provider/PowerAppsHostProvider.web.js +41 -0
  34. package/lib/commonjs/provider/PowerAppsProvider.js +229 -133
  35. package/lib/commonjs/provider/SessionDetailsGesture.js +216 -0
  36. package/lib/commonjs/telemetry/OfflineSdkTelemetry.js +95 -0
  37. package/lib/commonjs/telemetry/OfflineSdkTelemetry.web.js +9 -0
  38. package/lib/commonjs/telemetry/index.js +22 -3
  39. package/lib/module/auth/AuthContext.js +74 -18
  40. package/lib/module/auth/AuthContext.web.js +26 -0
  41. package/lib/module/auth/completePowerAppsAuthSession.js +11 -0
  42. package/lib/module/bridge/NativePowerAppsBridge.js +21 -18
  43. package/lib/module/bridge/PowerPagesBridge.js +151 -0
  44. package/lib/module/bridge/bridgeState.js +34 -0
  45. package/lib/module/connections/ConnectionSetupScreen.js +460 -81
  46. package/lib/module/connections/PowerPlatformConnectionApi.js +932 -0
  47. package/lib/module/connections/connectionMetadata.js +112 -0
  48. package/lib/module/connections/connectionState.js +82 -0
  49. package/lib/module/connections/disambiguate.js +19 -12
  50. package/lib/module/connections/resolveConnections.js +342 -0
  51. package/lib/module/connections/types.js +91 -0
  52. package/lib/module/{platform → connections}/useConnectionRefs.js +129 -66
  53. package/lib/module/connections/useConnectionSetup.js +113 -54
  54. package/lib/module/controls/FilePicker.js +6 -0
  55. package/lib/module/controls/ImagePicker.js +8 -0
  56. package/lib/module/index.js +14 -6
  57. package/lib/module/logger.js +8 -6
  58. package/lib/module/offline/createOfflineHostActivationCallbacks.js +55 -0
  59. package/lib/module/offline/useOfflineIntegration.js +3 -0
  60. package/lib/module/offline/useOfflineIntegration.web.js +7 -0
  61. package/lib/module/offline/useOfflineIntegrationWithStartupGate.js +42 -0
  62. package/lib/module/offline/useOfflineIntegrationWithStartupGate.web.js +9 -0
  63. package/lib/module/platform/DataverseDataSources.js +10 -0
  64. package/lib/module/platform/DataverseGlobalDiscoveryApi.js +45 -0
  65. package/lib/module/platform/GlobalDiscovery.js +45 -0
  66. package/lib/module/platform/PowerPlatformEnvironmentApi.js +5 -104
  67. package/lib/module/provider/PowerAppsHostProvider.js +159 -28
  68. package/lib/module/provider/PowerAppsHostProvider.web.js +36 -0
  69. package/lib/module/provider/PowerAppsProvider.js +233 -137
  70. package/lib/module/provider/SessionDetailsGesture.js +211 -0
  71. package/lib/module/telemetry/OfflineSdkTelemetry.js +91 -0
  72. package/lib/module/telemetry/OfflineSdkTelemetry.web.js +5 -0
  73. package/lib/module/telemetry/index.js +15 -4
  74. package/lib/typescript/commonjs/auth/AuthContext.d.ts +3 -1
  75. package/lib/typescript/commonjs/auth/AuthContext.web.d.ts +21 -0
  76. package/lib/typescript/commonjs/auth/completePowerAppsAuthSession.d.ts +8 -0
  77. package/lib/typescript/commonjs/bridge/PowerPagesBridge.d.ts +18 -0
  78. package/lib/typescript/commonjs/bridge/bridgeState.d.ts +5 -0
  79. package/lib/typescript/commonjs/connections/ConnectionSetupScreen.d.ts +28 -7
  80. package/lib/typescript/commonjs/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
  81. package/lib/typescript/commonjs/connections/connectionMetadata.d.ts +66 -0
  82. package/lib/typescript/commonjs/connections/connectionState.d.ts +16 -0
  83. package/lib/typescript/commonjs/connections/disambiguate.d.ts +4 -2
  84. package/lib/typescript/commonjs/{platform → connections}/resolveConnections.d.ts +19 -19
  85. package/lib/typescript/commonjs/connections/types.d.ts +129 -1
  86. package/lib/typescript/commonjs/{platform → connections}/useConnectionRefs.d.ts +33 -24
  87. package/lib/typescript/commonjs/connections/useConnectionSetup.d.ts +12 -8
  88. package/lib/typescript/commonjs/controls/FilePicker.d.ts +7 -1
  89. package/lib/typescript/commonjs/controls/ImagePicker.d.ts +9 -1
  90. package/lib/typescript/commonjs/index.d.ts +20 -10
  91. package/lib/typescript/commonjs/logger.d.ts +4 -4
  92. package/lib/typescript/commonjs/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
  93. package/lib/typescript/commonjs/offline/useOfflineIntegration.d.ts +2 -0
  94. package/lib/typescript/commonjs/offline/useOfflineIntegration.web.d.ts +4 -0
  95. package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
  96. package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
  97. package/lib/typescript/commonjs/platform/DataverseDataSources.d.ts +27 -0
  98. package/lib/typescript/commonjs/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
  99. package/lib/typescript/commonjs/platform/GlobalDiscovery.d.ts +4 -0
  100. package/lib/typescript/commonjs/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
  101. package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +29 -18
  102. package/lib/typescript/commonjs/provider/PowerAppsHostProvider.web.d.ts +5 -0
  103. package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +32 -10
  104. package/lib/typescript/commonjs/provider/SessionDetailsGesture.d.ts +13 -0
  105. package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.d.ts +15 -0
  106. package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
  107. package/lib/typescript/commonjs/telemetry/index.d.ts +13 -1
  108. package/lib/typescript/module/auth/AuthContext.d.ts +3 -1
  109. package/lib/typescript/module/auth/AuthContext.web.d.ts +21 -0
  110. package/lib/typescript/module/auth/completePowerAppsAuthSession.d.ts +8 -0
  111. package/lib/typescript/module/bridge/PowerPagesBridge.d.ts +18 -0
  112. package/lib/typescript/module/bridge/bridgeState.d.ts +5 -0
  113. package/lib/typescript/module/connections/ConnectionSetupScreen.d.ts +28 -7
  114. package/lib/typescript/module/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
  115. package/lib/typescript/module/connections/connectionMetadata.d.ts +66 -0
  116. package/lib/typescript/module/connections/connectionState.d.ts +16 -0
  117. package/lib/typescript/module/connections/disambiguate.d.ts +4 -2
  118. package/lib/typescript/module/{platform → connections}/resolveConnections.d.ts +19 -19
  119. package/lib/typescript/module/connections/types.d.ts +129 -1
  120. package/lib/typescript/module/{platform → connections}/useConnectionRefs.d.ts +33 -24
  121. package/lib/typescript/module/connections/useConnectionSetup.d.ts +12 -8
  122. package/lib/typescript/module/controls/FilePicker.d.ts +7 -1
  123. package/lib/typescript/module/controls/ImagePicker.d.ts +9 -1
  124. package/lib/typescript/module/index.d.ts +24 -14
  125. package/lib/typescript/module/logger.d.ts +4 -4
  126. package/lib/typescript/module/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
  127. package/lib/typescript/module/offline/useOfflineIntegration.d.ts +2 -0
  128. package/lib/typescript/module/offline/useOfflineIntegration.web.d.ts +4 -0
  129. package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
  130. package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
  131. package/lib/typescript/module/platform/DataverseDataSources.d.ts +27 -0
  132. package/lib/typescript/module/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
  133. package/lib/typescript/module/platform/GlobalDiscovery.d.ts +4 -0
  134. package/lib/typescript/module/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
  135. package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +29 -18
  136. package/lib/typescript/module/provider/PowerAppsHostProvider.web.d.ts +5 -0
  137. package/lib/typescript/module/provider/PowerAppsProvider.d.ts +34 -12
  138. package/lib/typescript/module/provider/SessionDetailsGesture.d.ts +13 -0
  139. package/lib/typescript/module/telemetry/OfflineSdkTelemetry.d.ts +15 -0
  140. package/lib/typescript/module/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
  141. package/lib/typescript/module/telemetry/index.d.ts +13 -1
  142. package/package.json +43 -18
  143. package/scripts/build-ios-base.js +66 -27
  144. package/scripts/bundle.js +212 -5
  145. package/scripts/generate-connector-schemas.js +8 -6
  146. package/scripts/metro-logger.js +213 -0
  147. package/scripts/start-web.mjs +137 -0
  148. package/scripts/upgrade/runtime.js +463 -0
  149. package/scripts/upgrade/system.js +244 -0
  150. package/scripts/upgrade-authoring.js +534 -0
  151. package/scripts/upgrade.js +188 -0
  152. package/scripts/wrap.js +536 -0
  153. package/lib/commonjs/platform/PowerPlatformConnectionApi.js +0 -606
  154. package/lib/commonjs/platform/resolveConnections.js +0 -264
  155. package/lib/module/platform/PowerPlatformConnectionApi.js +0 -595
  156. package/lib/module/platform/resolveConnections.js +0 -259
@@ -7,7 +7,7 @@
7
7
  * Power Apps canvas app inside an Expo shell:
8
8
  *
9
9
  * GestureHandlerRootView
10
- * └── TamaguiProvider (defaults to @tamagui/config/v3)
10
+ * └── TamaguiProvider (defaults to @tamagui/config/v5)
11
11
  * └── PortalProvider (required by ToastViewport)
12
12
  * └── ToastProvider + ToastViewport
13
13
  * └── QueryClientProvider (own QueryClient, wired to NetInfo + AppState)
@@ -27,24 +27,28 @@
27
27
  * </PowerAppsProvider>
28
28
  */
29
29
 
30
- import React, { useCallback, useEffect, useMemo, useRef } from 'react';
31
- import { AppState, Platform } from 'react-native';
30
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
31
+ import { AppState, Platform, ScrollView, StyleSheet, Text } from 'react-native';
32
32
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
33
33
  import { createTamagui, TamaguiProvider } from '@tamagui/core';
34
34
  import { PortalProvider } from '@tamagui/portal';
35
- import { config as tamaguiDefaultConfig } from '@tamagui/config/v3';
35
+ import { defaultConfig as tamaguiDefaultConfig } from '@tamagui/config/v5';
36
36
  import { ToastProvider, ToastViewport } from '@tamagui/toast';
37
- import { QueryClient, QueryClientProvider, focusManager, onlineManager } from '@tanstack/react-query';
37
+ import { QueryClientProvider, focusManager, onlineManager } from '@tanstack/react-query';
38
38
  import NetInfo from '@react-native-community/netinfo';
39
- import { AuthProvider, useAuth } from "../auth/AuthContext.js";
40
- import { PowerAppsHostProvider } from "./PowerAppsHostProvider.js";
41
- import { fetchInstanceUrl } from "../platform/PowerPlatformEnvironmentApi.js";
39
+ import { AuthProvider, useAuth } from '../auth/AuthContext';
40
+ import { PowerAppsHostProvider } from './PowerAppsHostProvider';
41
+ import { powerPlatformScope } from "../platform/PowerPlatformEnvironmentApi.js";
42
42
  import { ThemeProvider } from "../theme/ThemeProvider.js";
43
43
  import { ErrorBoundary } from "../ErrorBoundary.js";
44
+ import { isDevPlayer } from "../utils/isDevPlayer.js";
44
45
  import Constants from 'expo-constants';
45
- import { configureAppLogger, getAppLogger, Logger, OneDsTelemetryProvider, resolveDeviceId, resolveTelemetryLocation, regionToClusterCategory, suppressKnownWarnings } from '@microsoft/power-apps-native-common';
46
+ import { ApplicationInsightsTelemetryProvider, configureAppLogger, configureCustomerTelemetryLogger, getAppLogger, Logger, OneDsTelemetryProvider, resolveDeviceId, resolveTelemetryLocation, regionToClusterCategory, suppressKnownWarnings } from '@microsoft/power-apps-native-common';
46
47
  suppressKnownWarnings();
47
- import { useOfflineIntegration } from '@microsoft/power-apps-native-offline';
48
+ import { createHostQueryClient } from "../offline/createOfflineHostActivationCallbacks.js";
49
+ import { useOfflineIntegrationWithStartupGate } from '../offline/useOfflineIntegrationWithStartupGate';
50
+ import { SessionDetailsGesture } from "./SessionDetailsGesture.js";
51
+ import { configureOfflineSdkTelemetry } from '../telemetry/OfflineSdkTelemetry';
48
52
 
49
53
  // ---- React-Query wiring (runs once at module load) ----
50
54
 
@@ -52,6 +56,15 @@ import { useOfflineIntegration } from '@microsoft/power-apps-native-offline';
52
56
  // (the default browser-event-based detection is a no-op in React Native).
53
57
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
54
58
  onlineManager.setEventListener(setOnline => {
59
+ if (Platform.OS === 'web' && typeof window !== 'undefined') {
60
+ const updateOnlineState = () => setOnline(window.navigator.onLine);
61
+ window.addEventListener('online', updateOnlineState);
62
+ updateOnlineState();
63
+ return () => {
64
+ window.removeEventListener('online', updateOnlineState);
65
+ window.removeEventListener('offline', updateOnlineState);
66
+ };
67
+ }
55
68
  return NetInfo.addEventListener(state => setOnline(!!state.isConnected));
56
69
  });
57
70
 
@@ -67,78 +80,55 @@ focusManager.setEventListener(handleFocus => {
67
80
 
68
81
  // The app's tamagui.config.ts (generated by /tamagui-design-system) is always
69
82
  // passed as the `tamaguiConfig` prop and owns all $surface* / $accent* tokens.
70
- // The bare @tamagui/config/v3 preset is only a last-resort fallback for tests
83
+ // The bare @tamagui/config/v5 preset is only a last-resort fallback for tests
71
84
  // and storybook — it will not resolve custom $surface* tokens.
72
85
  const defaultTamaguiConfig = createTamagui(tamaguiDefaultConfig);
73
-
74
- // Whether the offline SDK will actually orchestrate for this app. Mirrors the
75
- // resolution that @microsoft/power-apps-native-offline's useOfflineIntegration
76
- // performs (offlineProfile prop → expo manifest `extra.__offlineProfile`;
77
- // enabled defaults true unless the profile's `appConfig.enabled` is explicitly
78
- // false), but computed synchronously here because the QueryClient's networkMode
79
- // must be fixed BEFORE the first query runs. The SDK's own `isEnabled` is only
80
- // known deep inside the tree (below QueryClientProvider), which is too late to
81
- // configure the client without a race. Kept deliberately narrow: it only
82
- // decides networkMode below.
83
- function isOfflineEnabled(offlineProfile) {
84
- // Only honour the prop when it carries a profileId — this mirrors the SDK's
85
- // useOfflineIntegration, which ignores a profileId-less prop and falls through
86
- // to the manifest. Without this guard a truthy-but-invalid prop (no profileId)
87
- // short-circuits to `false` here while the SDK still resolves enabled from the
88
- // manifest, leaving networkMode on 'online' (blank offline lists).
89
- const propProfile = offlineProfile;
90
- let profile = propProfile && propProfile.profileId ? propProfile : undefined;
91
- if (!profile) {
92
- const fromManifest = Constants.expoConfig?.extra?.__offlineProfile;
93
- if (fromManifest && typeof fromManifest === 'object' && fromManifest.profileId) {
94
- profile = fromManifest;
95
- }
96
- }
97
- if (profile) {
98
- // Honour an explicit `appConfig.enabled: false`. This mirrors the SDK's
99
- // useOfflineIntegration, whose validateConfig classifies `enabled === false`
100
- // as "explicitly-disabled" and stays inert. A host shell's placeholder
101
- // profile (e.g. Power Apps Dev Player's offline-profile.json, enabled:false)
102
- // must NOT flip networkMode to 'always' for the loaded app — otherwise every
103
- // scanned app gets paused queries / blank lists. Any other resolved profile
104
- // (enabled true, or omitted → SDK defaults to true) enables offline.
105
- const appConfig = profile.appConfig;
106
- if (appConfig && appConfig.enabled === false) {
107
- return false;
108
- }
109
- return true;
110
- }
111
- return false;
86
+ function hasConfiguredValue(value) {
87
+ return typeof value === 'string' && value.trim().length > 0;
112
88
  }
113
-
114
- // Builds the app's QueryClient. networkMode is gated on whether offline is
115
- // actually enabled:
116
- // • Offline enabled → 'always': the host is offline-first, so reads/writes are
117
- // served by the offline SDK from local SQLite regardless of connectivity.
118
- // onlineManager is wired to NetInfo (above) for refetchOnReconnect, but that
119
- // same wiring makes the default 'online' mode PAUSE every query/mutation while
120
- // offline (queryFn never runs → blank lists with WiFi off). 'always' makes
121
- // React Query ignore connectivity for both the initial attempt AND retries, so
122
- // local-SQLite reads/writes never get paused. ('offlineFirst' would still pause
123
- // *retries* offline — wrong for a purely-local SDK where a transient first
124
- // attempt error, e.g. SDK-not-ready at cold boot, would leave the query stuck
125
- // 'paused' until reconnect.)
126
- // • Offline disabled → undefined: React Query keeps its default 'online' mode,
127
- // i.e. the exact pre-offline behaviour for online-only apps.
128
- function createQueryClient(offlineEnabled) {
129
- const networkMode = offlineEnabled ? 'always' : undefined;
130
- return new QueryClient({
131
- defaultOptions: {
132
- queries: {
133
- staleTime: 60_000,
134
- retry: 2,
135
- networkMode
136
- },
137
- mutations: {
138
- retry: 0,
139
- networkMode
140
- }
141
- }
89
+ function hasEntries(value) {
90
+ return Boolean(value && typeof value === 'object' && Object.keys(value).length > 0);
91
+ }
92
+ function requiresPowerPlatformAuth(powerConfig) {
93
+ return Boolean(hasConfiguredValue(powerConfig.appId) || hasConfiguredValue(powerConfig.environmentId) || hasEntries(powerConfig.connectionReferences) || hasEntries(powerConfig.databaseReferences));
94
+ }
95
+ function MissingClientIdErrorScreen() {
96
+ return /*#__PURE__*/_jsxs(ScrollView, {
97
+ contentContainerStyle: styles.errorContainer,
98
+ children: [/*#__PURE__*/_jsx(Text, {
99
+ style: styles.errorTitle,
100
+ children: "Missing auth client ID"
101
+ }), /*#__PURE__*/_jsx(Text, {
102
+ style: styles.errorBody,
103
+ children: "This app needs Power Platform authentication, but `auth.config.json` does not include `msal.clientId`."
104
+ }), /*#__PURE__*/_jsx(Text, {
105
+ style: styles.errorBody,
106
+ children: "Add the Entra app registration client ID to `auth.config.json`, rebuild the app bundle, and run it again in Power Apps Dev Player."
107
+ })]
108
+ });
109
+ }
110
+ function MissingAppIdErrorScreen() {
111
+ return /*#__PURE__*/_jsxs(ScrollView, {
112
+ contentContainerStyle: styles.errorContainer,
113
+ children: [/*#__PURE__*/_jsx(Text, {
114
+ style: styles.errorTitle,
115
+ children: "App ID is missing"
116
+ }), /*#__PURE__*/_jsx(Text, {
117
+ style: styles.errorBody,
118
+ children: "This mobile app does not include an app ID. Push the mobile app to the Power Platform environment, rebuild it, and try again."
119
+ })]
120
+ });
121
+ }
122
+ function MissingEnvironmentIdErrorScreen() {
123
+ return /*#__PURE__*/_jsxs(ScrollView, {
124
+ contentContainerStyle: styles.errorContainer,
125
+ children: [/*#__PURE__*/_jsx(Text, {
126
+ style: styles.errorTitle,
127
+ children: "Environment ID is missing"
128
+ }), /*#__PURE__*/_jsx(Text, {
129
+ style: styles.errorBody,
130
+ children: "This mobile app does not include an environment ID. Push the mobile app to the Power Platform environment, rebuild it, and try again."
131
+ })]
142
132
  });
143
133
  }
144
134
 
@@ -146,6 +136,29 @@ function createQueryClient(offlineEnabled) {
146
136
 
147
137
  /** Shape of power.config.json consumed by the provider. */
148
138
 
139
+ function readAppInsightsConfig(value) {
140
+ if (!value || typeof value !== 'object') return undefined;
141
+ const config = value;
142
+ const minSeverity = config.minSeverity;
143
+ return {
144
+ enabled: config.enabled === true,
145
+ connectionString: typeof config.connectionString === 'string' ? config.connectionString : '',
146
+ appId: typeof config.appId === 'string' ? config.appId : '',
147
+ ...(typeof config.appVersion === 'string' ? {
148
+ appVersion: config.appVersion
149
+ } : {}),
150
+ ...(typeof config.environment === 'string' ? {
151
+ environment: config.environment
152
+ } : {}),
153
+ ...(minSeverity === 'verbose' || minSeverity === 'info' || minSeverity === 'warning' || minSeverity === 'error' ? {
154
+ minSeverity
155
+ } : {}),
156
+ ...(typeof config.includeUserId === 'boolean' ? {
157
+ includeUserId: config.includeUserId
158
+ } : {})
159
+ };
160
+ }
161
+
149
162
  // ---- Inner component ----
150
163
  // Separate from PowerAppsProvider only because useAuth() must be called inside
151
164
  // AuthProvider. All auth-dependent wiring lives here.
@@ -156,53 +169,49 @@ function InnerProviders({
156
169
  schemaMap,
157
170
  renderOAuthWebView,
158
171
  onDisambiguationNeeded,
172
+ onConnectionConsent,
173
+ onResourceConsent,
174
+ onConnectionConsentDeclined,
159
175
  offlineProfile,
176
+ onOfflineEnabledChange,
160
177
  children
161
178
  }) {
162
179
  const {
163
180
  acquireToken,
164
181
  isSignedIn,
165
- hasRealAccount
182
+ hasRealAccount,
183
+ isAuthReady,
184
+ signOut
166
185
  } = useAuth();
167
- const connectionReferences = useMemo(() => powerConfig.connectionReferences ?? {}, [powerConfig.connectionReferences]);
168
-
169
- // ---- Offline (all logic lives in the SDK) ----
170
- // Adapter over the host's fetchInstanceUrl: the offline SDK's contract passes a
171
- // 4th `signal?: AbortSignal` arg (which fetchInstanceUrl treats as bearerToken)
172
- // and expects a `string | null` return, so drop the extra arg and normalise
173
- // `undefined` -> `null`.
174
- const resolveInstanceUrlForOffline = useCallback(async (environmentId, region, acqToken, _signal) => {
175
- const url = await fetchInstanceUrl(environmentId, region, acqToken);
176
- return url ?? null;
177
- }, []);
178
-
179
- // Gate offline engagement on a REAL account (`hasRealAccount`), not the
180
- // `isSignedIn` flag — `isSignedIn` is inflated by noAuthMode (login-bypassed /
181
- // demo sessions report signed-in but have no account, so `acquireToken` always
182
- // returns null). Passing the inflated flag makes the SDK orchestrator engage
183
- // and then spin in a perpetual "waiting-for-auth" 3s retry loop (each retry
184
- // re-renders the provider subtree). With no obtainable token offline cannot
185
- // sync anyway, so we only engage once a usable token exists.
186
- const offline = useOfflineIntegration({
186
+ const connectionReferences = useMemo(() => isDevPlayer ? powerConfig.connectionReferences ?? {} : {}, [powerConfig.connectionReferences, isDevPlayer]);
187
+ const runtimeSchemaMap = useMemo(() => isDevPlayer ? schemaMap : {}, [schemaMap, isDevPlayer]);
188
+ const offline = useOfflineIntegrationWithStartupGate({
187
189
  powerConfig,
188
190
  offlineProfile,
189
191
  acquireToken,
190
- isSignedIn: hasRealAccount,
191
- fetchInstanceUrl: resolveInstanceUrlForOffline
192
+ hasRealAccount,
193
+ isAuthReady
192
194
  });
195
+ useEffect(() => {
196
+ onOfflineEnabledChange(offline.isEnabled);
197
+ }, [offline.isEnabled, onOfflineEnabledChange]);
193
198
  return /*#__PURE__*/_jsxs(PowerAppsHostProvider, {
194
199
  acquireToken: acquireToken,
195
200
  isSignedIn: isSignedIn,
201
+ onSignOut: signOut,
196
202
  appId: powerConfig.appId ?? undefined,
197
203
  environmentId: powerConfig.environmentId ?? '',
198
204
  region: powerConfig.region ?? 'prod',
199
205
  connectionReferences: connectionReferences,
200
- schemaMap: schemaMap,
201
- databaseReferences: powerConfig.databaseReferences,
206
+ schemaMap: runtimeSchemaMap,
207
+ databaseReferences: isDevPlayer ? powerConfig.databaseReferences : undefined,
202
208
  isvClientId: msalConfig.clientId,
203
209
  renderOAuthWebView: renderOAuthWebView,
204
210
  onDisambiguationNeeded: onDisambiguationNeeded,
205
- children: [children, offline.StatusOverlay && /*#__PURE__*/_jsx(offline.StatusOverlay, {})]
211
+ onConnectionConsent: onConnectionConsent,
212
+ onResourceConsent: onResourceConsent,
213
+ onConnectionConsentDeclined: onConnectionConsentDeclined,
214
+ children: [offline.shouldMountChildren && children, offline.StatusOverlay && /*#__PURE__*/_jsx(offline.StatusOverlay, {})]
206
215
  });
207
216
  }
208
217
 
@@ -229,8 +238,13 @@ export function PowerAppsProvider({
229
238
  followSystemTheme = true,
230
239
  renderOAuthWebView,
231
240
  onDisambiguationNeeded,
241
+ onConnectionConsent,
242
+ onResourceConsent,
243
+ onConnectionConsentDeclined,
232
244
  appName,
233
245
  logger: loggerProp,
246
+ showSessionDetailsOnSwipe = true,
247
+ appInsightsConfig,
234
248
  offlineProfile,
235
249
  children
236
250
  }) {
@@ -238,13 +252,17 @@ export function PowerAppsProvider({
238
252
  // is stable (loggerProp is an object reference; re-running on every render
239
253
  // would reconfigure and leak the previous provider).
240
254
  const loggerPropRef = useRef(loggerProp);
241
-
242
- // Gate the offline-first networkMode on whether offline is actually enabled
243
- // for this app. Keyed on the resolved boolean (not the prop object refs) so the
244
- // QueryClient identity stays stable across renders even if the caller passes
245
- // fresh offlineProfile literals.
246
- const offlineEnabled = useMemo(() => isOfflineEnabled(offlineProfile), [offlineProfile]);
247
- const queryClient = useMemo(() => createQueryClient(offlineEnabled), [offlineEnabled]);
255
+ const appInsightsConfigRef = useRef(appInsightsConfig ?? readAppInsightsConfig(Constants.expoConfig?.extra?.appInsightsConfig));
256
+ const [offlineTelemetryEnabled, setOfflineTelemetryEnabled] = useState();
257
+ const missingClientIdInDevPlayer = Platform.OS !== 'web' && isDevPlayer && !hasConfiguredValue(msalConfig.clientId) && requiresPowerPlatformAuth(powerConfig);
258
+ const missingAppId = !isDevPlayer && !hasConfiguredValue(powerConfig.appId);
259
+ const missingEnvironmentId = !isDevPlayer && !hasConfiguredValue(powerConfig.environmentId);
260
+ const appId = typeof powerConfig.appId === 'string' ? powerConfig.appId.trim() : '';
261
+ const bundleId = Platform.OS === 'ios' ? Constants.expoConfig?.ios?.bundleIdentifier : Platform.OS === 'android' ? Constants.expoConfig?.android?.package : undefined;
262
+ const appInstanceId = Constants.expoConfig?.extra?.telemetry?.appInstanceId;
263
+ const skillCorrelationId = typeof appInstanceId === 'string' ? appInstanceId.trim() : '';
264
+ const requiredAuthScopes = useMemo(() => [powerPlatformScope(powerConfig.region ?? 'prod')], [powerConfig.region]);
265
+ const queryClient = useMemo(() => createHostQueryClient(), []);
248
266
 
249
267
  // Configure the app logger exactly once per mount inside useEffect so that
250
268
  // side effects (provider construction, async resolveDeviceId) never run
@@ -252,6 +270,9 @@ export function PowerAppsProvider({
252
270
  // silent no-op Logger, so any telemetry emitted before the first commit is
253
271
  // silently dropped rather than crashing.
254
272
  useEffect(() => {
273
+ // Customer telemetry is a no-op until a valid Application Insights
274
+ // provider is configured below.
275
+ configureCustomerTelemetryLogger(new Logger());
255
276
  const lp = loggerPropRef.current;
256
277
  if (lp === null) {
257
278
  // Explicit opt-out — replace with a silent no-op Logger.
@@ -267,26 +288,63 @@ export function PowerAppsProvider({
267
288
  clientType: appName ?? 'PowerAppsNative',
268
289
  appVersion: Constants.expoConfig?.version,
269
290
  minSeverity: __DEV__ ? 'verbose' : 'info',
270
- telemetryLocation: resolveTelemetryLocation(powerConfig.geoRegion)
291
+ telemetryLocation: resolveTelemetryLocation(powerConfig.geoRegion),
292
+ commonProperties: {
293
+ ...(appId ? {
294
+ appId
295
+ } : {}),
296
+ ...(bundleId ? {
297
+ bundleId
298
+ } : {}),
299
+ ...(skillCorrelationId ? {
300
+ skillCorrelationId
301
+ } : {})
302
+ }
303
+ };
304
+ const configureDefaultLogger = (deviceId = 'react-native') => {
305
+ const oneDsProvider = new OneDsTelemetryProvider({
306
+ ...providerConfig,
307
+ deviceId
308
+ });
309
+ const providers = [oneDsProvider];
310
+ let appInsightsEnabled = false;
311
+ const configuredAppInsights = appInsightsConfigRef.current;
312
+ if (configuredAppInsights?.enabled) {
313
+ try {
314
+ const customerProvider = new ApplicationInsightsTelemetryProvider({
315
+ ...configuredAppInsights,
316
+ appVersion: configuredAppInsights.appVersion ?? Constants.expoConfig?.version,
317
+ deviceId,
318
+ sessionId: oneDsProvider.sessionId
319
+ });
320
+ providers.push(customerProvider);
321
+ configureCustomerTelemetryLogger(new Logger({
322
+ providers: [customerProvider]
323
+ }));
324
+ appInsightsEnabled = true;
325
+ } catch (error) {
326
+ console.error('[PowerAppsProvider] Application Insights configuration is invalid.', error);
327
+ }
328
+ }
329
+ const logger = new Logger({
330
+ providers
331
+ });
332
+ configureAppLogger(logger);
333
+ if (appInsightsEnabled) {
334
+ logger.trackActivityEvent('PowerAppsNative.ApplicationStarted', {
335
+ app_id: configuredAppInsights?.appId
336
+ });
337
+ }
271
338
  };
272
339
  // Resolve the stable device ID first so every event — including the
273
340
  // very first — carries the real GUID rather than the 'react-native'
274
341
  // fallback. Events emitted while this resolves are silently dropped
275
342
  // by the no-op Logger that is active until configureAppLogger is called.
276
343
  resolveDeviceId().then(deviceId => {
277
- const provider = new OneDsTelemetryProvider({
278
- ...providerConfig,
279
- deviceId
280
- });
281
- configureAppLogger(new Logger({
282
- providers: [provider]
283
- }));
344
+ configureDefaultLogger(deviceId);
284
345
  }).catch(_err => {
285
346
  // SecureStore unavailable — fall back to a provider without a stable ID.
286
- const provider = new OneDsTelemetryProvider(providerConfig);
287
- configureAppLogger(new Logger({
288
- providers: [provider]
289
- }));
347
+ configureDefaultLogger();
290
348
  });
291
349
  }
292
350
 
@@ -299,6 +357,11 @@ export function PowerAppsProvider({
299
357
  };
300
358
  // eslint-disable-next-line react-hooks/exhaustive-deps
301
359
  }, []); // intentionally empty — logger is configured once per mount
360
+
361
+ useEffect(() => {
362
+ if (offlineTelemetryEnabled === undefined) return undefined;
363
+ return configureOfflineSdkTelemetry(offlineTelemetryEnabled);
364
+ }, [offlineTelemetryEnabled]);
302
365
  return /*#__PURE__*/_jsx(ErrorBoundary, {
303
366
  children: /*#__PURE__*/_jsx(GestureHandlerRootView, {
304
367
  style: {
@@ -316,19 +379,32 @@ export function PowerAppsProvider({
316
379
  children: /*#__PURE__*/_jsxs(ToastProvider, {
317
380
  swipeDirection: "horizontal",
318
381
  duration: 3000,
319
- children: [/*#__PURE__*/_jsx(QueryClientProvider, {
320
- client: queryClient,
321
- children: /*#__PURE__*/_jsx(AuthProvider, {
322
- msalConfig: msalConfig,
323
- intuneConfig: intuneConfig,
324
- children: /*#__PURE__*/_jsx(InnerProviders, {
382
+ children: [/*#__PURE__*/_jsx(SessionDetailsGesture, {
383
+ enabled: showSessionDetailsOnSwipe,
384
+ appName: appName ?? Constants.expoConfig?.name ?? 'Power Apps',
385
+ appId: appId || undefined,
386
+ environmentId: powerConfig.environmentId,
387
+ region: powerConfig.region ?? 'prod',
388
+ appVersion: Constants.expoConfig?.version,
389
+ children: /*#__PURE__*/_jsx(QueryClientProvider, {
390
+ client: queryClient,
391
+ children: missingAppId ? /*#__PURE__*/_jsx(MissingAppIdErrorScreen, {}) : missingEnvironmentId ? /*#__PURE__*/_jsx(MissingEnvironmentIdErrorScreen, {}) : missingClientIdInDevPlayer ? /*#__PURE__*/_jsx(MissingClientIdErrorScreen, {}) : /*#__PURE__*/_jsx(AuthProvider, {
325
392
  msalConfig: msalConfig,
326
- powerConfig: powerConfig,
327
- schemaMap: schemaMap,
328
- renderOAuthWebView: renderOAuthWebView,
329
- onDisambiguationNeeded: onDisambiguationNeeded,
330
- offlineProfile: offlineProfile,
331
- children: children
393
+ intuneConfig: intuneConfig,
394
+ requiredScopes: requiredAuthScopes,
395
+ children: /*#__PURE__*/_jsx(InnerProviders, {
396
+ msalConfig: msalConfig,
397
+ powerConfig: powerConfig,
398
+ schemaMap: schemaMap,
399
+ renderOAuthWebView: renderOAuthWebView,
400
+ onDisambiguationNeeded: onDisambiguationNeeded,
401
+ onConnectionConsent: onConnectionConsent,
402
+ onResourceConsent: onResourceConsent,
403
+ onConnectionConsentDeclined: onConnectionConsentDeclined,
404
+ offlineProfile: offlineProfile,
405
+ onOfflineEnabledChange: setOfflineTelemetryEnabled,
406
+ children: children
407
+ })
332
408
  })
333
409
  })
334
410
  }), /*#__PURE__*/_jsx(ToastViewport, {
@@ -342,4 +418,24 @@ export function PowerAppsProvider({
342
418
  })
343
419
  })
344
420
  });
345
- }
421
+ }
422
+ const styles = StyleSheet.create({
423
+ errorContainer: {
424
+ flexGrow: 1,
425
+ justifyContent: 'center',
426
+ padding: 24,
427
+ backgroundColor: '#F8F8F8'
428
+ },
429
+ errorTitle: {
430
+ color: '#A4262C',
431
+ fontSize: 22,
432
+ fontWeight: '700',
433
+ marginBottom: 12
434
+ },
435
+ errorBody: {
436
+ color: '#323130',
437
+ fontSize: 15,
438
+ lineHeight: 22,
439
+ marginBottom: 10
440
+ }
441
+ });