@microsoft/power-apps-native-host 0.2.25 → 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 (129) hide show
  1. package/README.md +21 -2
  2. package/compatibility/current.json +0 -1
  3. package/lib/NOTICE.txt +6 -12
  4. package/lib/commonjs/auth/AuthContext.js +74 -18
  5. package/lib/commonjs/bridge/NativePowerAppsBridge.js +11 -18
  6. package/lib/commonjs/bridge/PowerPagesBridge.js +27 -8
  7. package/lib/commonjs/bridge/bridgeState.js +40 -0
  8. package/lib/commonjs/connections/ConnectionSetupScreen.js +459 -80
  9. package/lib/commonjs/connections/PowerPlatformConnectionApi.js +946 -0
  10. package/lib/commonjs/connections/connectionMetadata.js +127 -0
  11. package/lib/commonjs/connections/connectionState.js +96 -0
  12. package/lib/commonjs/connections/disambiguate.js +19 -11
  13. package/lib/commonjs/connections/resolveConnections.js +347 -0
  14. package/lib/commonjs/connections/types.js +106 -0
  15. package/lib/commonjs/{platform → connections}/useConnectionRefs.js +128 -65
  16. package/lib/commonjs/connections/useConnectionSetup.js +111 -52
  17. package/lib/commonjs/controls/FilePicker.js +6 -0
  18. package/lib/commonjs/controls/ImagePicker.js +8 -0
  19. package/lib/commonjs/index.js +166 -4
  20. package/lib/commonjs/logger.js +9 -6
  21. package/lib/commonjs/offline/createOfflineHostActivationCallbacks.js +61 -0
  22. package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.js +46 -0
  23. package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.web.js +13 -0
  24. package/lib/commonjs/platform/DataverseDataSources.js +14 -0
  25. package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +49 -0
  26. package/lib/commonjs/platform/GlobalDiscovery.js +51 -0
  27. package/lib/commonjs/platform/PowerPlatformEnvironmentApi.js +6 -106
  28. package/lib/commonjs/provider/PowerAppsHostProvider.js +156 -25
  29. package/lib/commonjs/provider/PowerAppsProvider.js +173 -134
  30. package/lib/commonjs/provider/SessionDetailsGesture.js +216 -0
  31. package/lib/commonjs/telemetry/OfflineSdkTelemetry.js +95 -0
  32. package/lib/commonjs/telemetry/OfflineSdkTelemetry.web.js +9 -0
  33. package/lib/commonjs/telemetry/index.js +22 -3
  34. package/lib/module/auth/AuthContext.js +74 -18
  35. package/lib/module/bridge/NativePowerAppsBridge.js +12 -19
  36. package/lib/module/bridge/PowerPagesBridge.js +27 -8
  37. package/lib/module/bridge/bridgeState.js +34 -0
  38. package/lib/module/connections/ConnectionSetupScreen.js +460 -81
  39. package/lib/module/connections/PowerPlatformConnectionApi.js +932 -0
  40. package/lib/module/connections/connectionMetadata.js +112 -0
  41. package/lib/module/connections/connectionState.js +82 -0
  42. package/lib/module/connections/disambiguate.js +19 -12
  43. package/lib/module/connections/resolveConnections.js +342 -0
  44. package/lib/module/connections/types.js +91 -0
  45. package/lib/module/{platform → connections}/useConnectionRefs.js +129 -66
  46. package/lib/module/connections/useConnectionSetup.js +113 -54
  47. package/lib/module/controls/FilePicker.js +6 -0
  48. package/lib/module/controls/ImagePicker.js +8 -0
  49. package/lib/module/index.js +10 -4
  50. package/lib/module/logger.js +8 -6
  51. package/lib/module/offline/createOfflineHostActivationCallbacks.js +55 -0
  52. package/lib/module/offline/useOfflineIntegrationWithStartupGate.js +42 -0
  53. package/lib/module/offline/useOfflineIntegrationWithStartupGate.web.js +9 -0
  54. package/lib/module/platform/DataverseDataSources.js +10 -0
  55. package/lib/module/platform/DataverseGlobalDiscoveryApi.js +45 -0
  56. package/lib/module/platform/GlobalDiscovery.js +45 -0
  57. package/lib/module/platform/PowerPlatformEnvironmentApi.js +5 -104
  58. package/lib/module/provider/PowerAppsHostProvider.js +159 -28
  59. package/lib/module/provider/PowerAppsProvider.js +177 -138
  60. package/lib/module/provider/SessionDetailsGesture.js +211 -0
  61. package/lib/module/telemetry/OfflineSdkTelemetry.js +91 -0
  62. package/lib/module/telemetry/OfflineSdkTelemetry.web.js +5 -0
  63. package/lib/module/telemetry/index.js +15 -4
  64. package/lib/typescript/commonjs/auth/AuthContext.d.ts +3 -1
  65. package/lib/typescript/commonjs/auth/AuthContext.web.d.ts +1 -0
  66. package/lib/typescript/commonjs/bridge/bridgeState.d.ts +5 -0
  67. package/lib/typescript/commonjs/connections/ConnectionSetupScreen.d.ts +28 -7
  68. package/lib/typescript/commonjs/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
  69. package/lib/typescript/commonjs/connections/connectionMetadata.d.ts +66 -0
  70. package/lib/typescript/commonjs/connections/connectionState.d.ts +16 -0
  71. package/lib/typescript/commonjs/connections/disambiguate.d.ts +4 -2
  72. package/lib/typescript/commonjs/{platform → connections}/resolveConnections.d.ts +19 -19
  73. package/lib/typescript/commonjs/connections/types.d.ts +129 -1
  74. package/lib/typescript/commonjs/{platform → connections}/useConnectionRefs.d.ts +33 -24
  75. package/lib/typescript/commonjs/connections/useConnectionSetup.d.ts +12 -8
  76. package/lib/typescript/commonjs/controls/FilePicker.d.ts +7 -1
  77. package/lib/typescript/commonjs/controls/ImagePicker.d.ts +9 -1
  78. package/lib/typescript/commonjs/index.d.ts +17 -10
  79. package/lib/typescript/commonjs/logger.d.ts +4 -4
  80. package/lib/typescript/commonjs/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
  81. package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
  82. package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
  83. package/lib/typescript/commonjs/platform/DataverseDataSources.d.ts +27 -0
  84. package/lib/typescript/commonjs/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
  85. package/lib/typescript/commonjs/platform/GlobalDiscovery.d.ts +4 -0
  86. package/lib/typescript/commonjs/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
  87. package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +29 -18
  88. package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +30 -8
  89. package/lib/typescript/commonjs/provider/SessionDetailsGesture.d.ts +13 -0
  90. package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.d.ts +15 -0
  91. package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
  92. package/lib/typescript/commonjs/telemetry/index.d.ts +13 -1
  93. package/lib/typescript/module/auth/AuthContext.d.ts +3 -1
  94. package/lib/typescript/module/auth/AuthContext.web.d.ts +1 -0
  95. package/lib/typescript/module/bridge/bridgeState.d.ts +5 -0
  96. package/lib/typescript/module/connections/ConnectionSetupScreen.d.ts +28 -7
  97. package/lib/typescript/module/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
  98. package/lib/typescript/module/connections/connectionMetadata.d.ts +66 -0
  99. package/lib/typescript/module/connections/connectionState.d.ts +16 -0
  100. package/lib/typescript/module/connections/disambiguate.d.ts +4 -2
  101. package/lib/typescript/module/{platform → connections}/resolveConnections.d.ts +19 -19
  102. package/lib/typescript/module/connections/types.d.ts +129 -1
  103. package/lib/typescript/module/{platform → connections}/useConnectionRefs.d.ts +33 -24
  104. package/lib/typescript/module/connections/useConnectionSetup.d.ts +12 -8
  105. package/lib/typescript/module/controls/FilePicker.d.ts +7 -1
  106. package/lib/typescript/module/controls/ImagePicker.d.ts +9 -1
  107. package/lib/typescript/module/index.d.ts +17 -10
  108. package/lib/typescript/module/logger.d.ts +4 -4
  109. package/lib/typescript/module/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
  110. package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
  111. package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
  112. package/lib/typescript/module/platform/DataverseDataSources.d.ts +27 -0
  113. package/lib/typescript/module/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
  114. package/lib/typescript/module/platform/GlobalDiscovery.d.ts +4 -0
  115. package/lib/typescript/module/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
  116. package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +29 -18
  117. package/lib/typescript/module/provider/PowerAppsProvider.d.ts +30 -8
  118. package/lib/typescript/module/provider/SessionDetailsGesture.d.ts +13 -0
  119. package/lib/typescript/module/telemetry/OfflineSdkTelemetry.d.ts +15 -0
  120. package/lib/typescript/module/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
  121. package/lib/typescript/module/telemetry/index.d.ts +13 -1
  122. package/package.json +11 -6
  123. package/scripts/metro-logger.js +213 -0
  124. package/scripts/upgrade/runtime.js +13 -7
  125. package/scripts/upgrade-authoring.js +5 -6
  126. package/lib/commonjs/platform/PowerPlatformConnectionApi.js +0 -606
  127. package/lib/commonjs/platform/resolveConnections.js +0 -264
  128. package/lib/module/platform/PowerPlatformConnectionApi.js +0 -595
  129. package/lib/module/platform/resolveConnections.js +0 -259
@@ -21,7 +21,10 @@ var _ErrorBoundary = require("../ErrorBoundary.js");
21
21
  var _isDevPlayer = require("../utils/isDevPlayer.js");
22
22
  var _expoConstants = _interopRequireDefault(require("expo-constants"));
23
23
  var _powerAppsNativeCommon = require("@microsoft/power-apps-native-common");
24
- var _useOfflineIntegration = require("../offline/useOfflineIntegration");
24
+ var _createOfflineHostActivationCallbacks = require("../offline/createOfflineHostActivationCallbacks.js");
25
+ var _useOfflineIntegrationWithStartupGate = require("../offline/useOfflineIntegrationWithStartupGate");
26
+ var _SessionDetailsGesture = require("./SessionDetailsGesture.js");
27
+ var _OfflineSdkTelemetry = require("../telemetry/OfflineSdkTelemetry");
25
28
  var _jsxRuntime = require("react/jsx-runtime");
26
29
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
27
30
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
@@ -62,7 +65,6 @@ _reactQuery.onlineManager.setEventListener(setOnline => {
62
65
  if (_reactNative.Platform.OS === 'web' && typeof window !== 'undefined') {
63
66
  const updateOnlineState = () => setOnline(window.navigator.onLine);
64
67
  window.addEventListener('online', updateOnlineState);
65
- window.addEventListener('offline', updateOnlineState);
66
68
  updateOnlineState();
67
69
  return () => {
68
70
  window.removeEventListener('online', updateOnlineState);
@@ -87,86 +89,14 @@ _reactQuery.focusManager.setEventListener(handleFocus => {
87
89
  // The bare @tamagui/config/v5 preset is only a last-resort fallback for tests
88
90
  // and storybook — it will not resolve custom $surface* tokens.
89
91
  const defaultTamaguiConfig = (0, _core.createTamagui)(_v.defaultConfig);
90
-
91
- // Whether the offline SDK will actually orchestrate for this app. Mirrors the
92
- // resolution that @microsoft/power-apps-native-offline's useOfflineIntegration
93
- // performs (offlineProfile prop → expo manifest `extra.__offlineProfile`;
94
- // enabled defaults true unless the profile's `appConfig.enabled` is explicitly
95
- // false), but computed synchronously here because the QueryClient's networkMode
96
- // must be fixed BEFORE the first query runs. The SDK's own `isEnabled` is only
97
- // known deep inside the tree (below QueryClientProvider), which is too late to
98
- // configure the client without a race. Kept deliberately narrow: it only
99
- // decides networkMode below.
100
- function isOfflineEnabled(offlineProfile) {
101
- if (_reactNative.Platform.OS === 'web') return false;
102
- // Only honour the prop when it carries a profileId — this mirrors the SDK's
103
- // useOfflineIntegration, which ignores a profileId-less prop and falls through
104
- // to the manifest. Without this guard a truthy-but-invalid prop (no profileId)
105
- // short-circuits to `false` here while the SDK still resolves enabled from the
106
- // manifest, leaving networkMode on 'online' (blank offline lists).
107
- const propProfile = offlineProfile;
108
- let profile = propProfile && propProfile.profileId ? propProfile : undefined;
109
- if (!profile) {
110
- const fromManifest = _expoConstants.default.expoConfig?.extra?.__offlineProfile;
111
- if (fromManifest && typeof fromManifest === 'object' && fromManifest.profileId) {
112
- profile = fromManifest;
113
- }
114
- }
115
- if (profile) {
116
- // Honour an explicit `appConfig.enabled: false`. This mirrors the SDK's
117
- // useOfflineIntegration, whose validateConfig classifies `enabled === false`
118
- // as "explicitly-disabled" and stays inert. A host shell's placeholder
119
- // profile (e.g. Power Apps Dev Player's offline-profile.json, enabled:false)
120
- // must NOT flip networkMode to 'always' for the loaded app — otherwise every
121
- // scanned app gets paused queries / blank lists. Any other resolved profile
122
- // (enabled true, or omitted → SDK defaults to true) enables offline.
123
- const appConfig = profile.appConfig;
124
- if (appConfig && appConfig.enabled === false) {
125
- return false;
126
- }
127
- return true;
128
- }
129
- return false;
130
- }
131
-
132
- // Builds the app's QueryClient. networkMode is gated on whether offline is
133
- // actually enabled:
134
- // • Offline enabled → 'always': the host is offline-first, so reads/writes are
135
- // served by the offline SDK from local SQLite regardless of connectivity.
136
- // onlineManager is wired to NetInfo (above) for refetchOnReconnect, but that
137
- // same wiring makes the default 'online' mode PAUSE every query/mutation while
138
- // offline (queryFn never runs → blank lists with WiFi off). 'always' makes
139
- // React Query ignore connectivity for both the initial attempt AND retries, so
140
- // local-SQLite reads/writes never get paused. ('offlineFirst' would still pause
141
- // *retries* offline — wrong for a purely-local SDK where a transient first
142
- // attempt error, e.g. SDK-not-ready at cold boot, would leave the query stuck
143
- // 'paused' until reconnect.)
144
- // • Offline disabled → undefined: React Query keeps its default 'online' mode,
145
- // i.e. the exact pre-offline behaviour for online-only apps.
146
- function createQueryClient(offlineEnabled) {
147
- const networkMode = offlineEnabled ? 'always' : undefined;
148
- return new _reactQuery.QueryClient({
149
- defaultOptions: {
150
- queries: {
151
- staleTime: 60_000,
152
- retry: 2,
153
- networkMode
154
- },
155
- mutations: {
156
- retry: 0,
157
- networkMode
158
- }
159
- }
160
- });
161
- }
162
92
  function hasConfiguredValue(value) {
163
93
  return typeof value === 'string' && value.trim().length > 0;
164
94
  }
165
95
  function hasEntries(value) {
166
96
  return Boolean(value && typeof value === 'object' && Object.keys(value).length > 0);
167
97
  }
168
- function requiresPowerPlatformAuth(powerConfig, offlineEnabled) {
169
- return Boolean(hasConfiguredValue(powerConfig.appId) || hasConfiguredValue(powerConfig.environmentId) || hasEntries(powerConfig.connectionReferences) || hasEntries(powerConfig.databaseReferences) || offlineEnabled);
98
+ function requiresPowerPlatformAuth(powerConfig) {
99
+ return Boolean(hasConfiguredValue(powerConfig.appId) || hasConfiguredValue(powerConfig.environmentId) || hasEntries(powerConfig.connectionReferences) || hasEntries(powerConfig.databaseReferences));
170
100
  }
171
101
  function MissingClientIdErrorScreen() {
172
102
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
@@ -183,11 +113,58 @@ function MissingClientIdErrorScreen() {
183
113
  })]
184
114
  });
185
115
  }
116
+ function MissingAppIdErrorScreen() {
117
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
118
+ contentContainerStyle: styles.errorContainer,
119
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
120
+ style: styles.errorTitle,
121
+ children: "App ID is missing"
122
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
123
+ style: styles.errorBody,
124
+ children: "This mobile app does not include an app ID. Push the mobile app to the Power Platform environment, rebuild it, and try again."
125
+ })]
126
+ });
127
+ }
128
+ function MissingEnvironmentIdErrorScreen() {
129
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
130
+ contentContainerStyle: styles.errorContainer,
131
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
132
+ style: styles.errorTitle,
133
+ children: "Environment ID is missing"
134
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
135
+ style: styles.errorBody,
136
+ children: "This mobile app does not include an environment ID. Push the mobile app to the Power Platform environment, rebuild it, and try again."
137
+ })]
138
+ });
139
+ }
186
140
 
187
141
  // ---- Types ----
188
142
 
189
143
  /** Shape of power.config.json consumed by the provider. */
190
144
 
145
+ function readAppInsightsConfig(value) {
146
+ if (!value || typeof value !== 'object') return undefined;
147
+ const config = value;
148
+ const minSeverity = config.minSeverity;
149
+ return {
150
+ enabled: config.enabled === true,
151
+ connectionString: typeof config.connectionString === 'string' ? config.connectionString : '',
152
+ appId: typeof config.appId === 'string' ? config.appId : '',
153
+ ...(typeof config.appVersion === 'string' ? {
154
+ appVersion: config.appVersion
155
+ } : {}),
156
+ ...(typeof config.environment === 'string' ? {
157
+ environment: config.environment
158
+ } : {}),
159
+ ...(minSeverity === 'verbose' || minSeverity === 'info' || minSeverity === 'warning' || minSeverity === 'error' ? {
160
+ minSeverity
161
+ } : {}),
162
+ ...(typeof config.includeUserId === 'boolean' ? {
163
+ includeUserId: config.includeUserId
164
+ } : {})
165
+ };
166
+ }
167
+
191
168
  // ---- Inner component ----
192
169
  // Separate from PowerAppsProvider only because useAuth() must be called inside
193
170
  // AuthProvider. All auth-dependent wiring lives here.
@@ -198,53 +175,49 @@ function InnerProviders({
198
175
  schemaMap,
199
176
  renderOAuthWebView,
200
177
  onDisambiguationNeeded,
178
+ onConnectionConsent,
179
+ onResourceConsent,
180
+ onConnectionConsentDeclined,
201
181
  offlineProfile,
182
+ onOfflineEnabledChange,
202
183
  children
203
184
  }) {
204
185
  const {
205
186
  acquireToken,
206
187
  isSignedIn,
207
- hasRealAccount
188
+ hasRealAccount,
189
+ isAuthReady,
190
+ signOut
208
191
  } = (0, _AuthContext.useAuth)();
209
- const connectionReferences = (0, _react.useMemo)(() => powerConfig.connectionReferences ?? {}, [powerConfig.connectionReferences]);
210
-
211
- // ---- Offline (all logic lives in the SDK) ----
212
- // Adapter over the host's fetchInstanceUrl: the offline SDK's contract passes a
213
- // 4th `signal?: AbortSignal` arg (which fetchInstanceUrl treats as bearerToken)
214
- // and expects a `string | null` return, so drop the extra arg and normalise
215
- // `undefined` -> `null`.
216
- const resolveInstanceUrlForOffline = (0, _react.useCallback)(async (environmentId, region, acqToken, _signal) => {
217
- const url = await (0, _PowerPlatformEnvironmentApi.fetchInstanceUrl)(environmentId, region, acqToken);
218
- return url ?? null;
219
- }, []);
220
-
221
- // Gate offline engagement on a REAL account (`hasRealAccount`), not the
222
- // `isSignedIn` flag — `isSignedIn` is inflated by noAuthMode (login-bypassed /
223
- // demo sessions report signed-in but have no account, so `acquireToken` always
224
- // returns null). Passing the inflated flag makes the SDK orchestrator engage
225
- // and then spin in a perpetual "waiting-for-auth" 3s retry loop (each retry
226
- // re-renders the provider subtree). With no obtainable token offline cannot
227
- // sync anyway, so we only engage once a usable token exists.
228
- const offline = (0, _useOfflineIntegration.useOfflineIntegration)({
192
+ const connectionReferences = (0, _react.useMemo)(() => _isDevPlayer.isDevPlayer ? powerConfig.connectionReferences ?? {} : {}, [powerConfig.connectionReferences, _isDevPlayer.isDevPlayer]);
193
+ const runtimeSchemaMap = (0, _react.useMemo)(() => _isDevPlayer.isDevPlayer ? schemaMap : {}, [schemaMap, _isDevPlayer.isDevPlayer]);
194
+ const offline = (0, _useOfflineIntegrationWithStartupGate.useOfflineIntegrationWithStartupGate)({
229
195
  powerConfig,
230
196
  offlineProfile,
231
197
  acquireToken,
232
- isSignedIn: hasRealAccount,
233
- fetchInstanceUrl: resolveInstanceUrlForOffline
198
+ hasRealAccount,
199
+ isAuthReady
234
200
  });
201
+ (0, _react.useEffect)(() => {
202
+ onOfflineEnabledChange(offline.isEnabled);
203
+ }, [offline.isEnabled, onOfflineEnabledChange]);
235
204
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_PowerAppsHostProvider.PowerAppsHostProvider, {
236
205
  acquireToken: acquireToken,
237
206
  isSignedIn: isSignedIn,
207
+ onSignOut: signOut,
238
208
  appId: powerConfig.appId ?? undefined,
239
209
  environmentId: powerConfig.environmentId ?? '',
240
210
  region: powerConfig.region ?? 'prod',
241
211
  connectionReferences: connectionReferences,
242
- schemaMap: schemaMap,
243
- databaseReferences: powerConfig.databaseReferences,
212
+ schemaMap: runtimeSchemaMap,
213
+ databaseReferences: _isDevPlayer.isDevPlayer ? powerConfig.databaseReferences : undefined,
244
214
  isvClientId: msalConfig.clientId,
245
215
  renderOAuthWebView: renderOAuthWebView,
246
216
  onDisambiguationNeeded: onDisambiguationNeeded,
247
- children: [children, offline.StatusOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(offline.StatusOverlay, {})]
217
+ onConnectionConsent: onConnectionConsent,
218
+ onResourceConsent: onResourceConsent,
219
+ onConnectionConsentDeclined: onConnectionConsentDeclined,
220
+ children: [offline.shouldMountChildren && children, offline.StatusOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(offline.StatusOverlay, {})]
248
221
  });
249
222
  }
250
223
 
@@ -271,8 +244,13 @@ function PowerAppsProvider({
271
244
  followSystemTheme = true,
272
245
  renderOAuthWebView,
273
246
  onDisambiguationNeeded,
247
+ onConnectionConsent,
248
+ onResourceConsent,
249
+ onConnectionConsentDeclined,
274
250
  appName,
275
251
  logger: loggerProp,
252
+ showSessionDetailsOnSwipe = true,
253
+ appInsightsConfig,
276
254
  offlineProfile,
277
255
  children
278
256
  }) {
@@ -280,14 +258,17 @@ function PowerAppsProvider({
280
258
  // is stable (loggerProp is an object reference; re-running on every render
281
259
  // would reconfigure and leak the previous provider).
282
260
  const loggerPropRef = (0, _react.useRef)(loggerProp);
283
-
284
- // Gate the offline-first networkMode on whether offline is actually enabled
285
- // for this app. Keyed on the resolved boolean (not the prop object refs) so the
286
- // QueryClient identity stays stable across renders even if the caller passes
287
- // fresh offlineProfile literals.
288
- const offlineEnabled = (0, _react.useMemo)(() => isOfflineEnabled(offlineProfile), [offlineProfile]);
289
- const missingClientIdInDevPlayer = _reactNative.Platform.OS !== 'web' && _isDevPlayer.isDevPlayer && !hasConfiguredValue(msalConfig.clientId) && requiresPowerPlatformAuth(powerConfig, offlineEnabled);
290
- const queryClient = (0, _react.useMemo)(() => createQueryClient(offlineEnabled), [offlineEnabled]);
261
+ const appInsightsConfigRef = (0, _react.useRef)(appInsightsConfig ?? readAppInsightsConfig(_expoConstants.default.expoConfig?.extra?.appInsightsConfig));
262
+ const [offlineTelemetryEnabled, setOfflineTelemetryEnabled] = (0, _react.useState)();
263
+ const missingClientIdInDevPlayer = _reactNative.Platform.OS !== 'web' && _isDevPlayer.isDevPlayer && !hasConfiguredValue(msalConfig.clientId) && requiresPowerPlatformAuth(powerConfig);
264
+ const missingAppId = !_isDevPlayer.isDevPlayer && !hasConfiguredValue(powerConfig.appId);
265
+ const missingEnvironmentId = !_isDevPlayer.isDevPlayer && !hasConfiguredValue(powerConfig.environmentId);
266
+ const appId = typeof powerConfig.appId === 'string' ? powerConfig.appId.trim() : '';
267
+ const bundleId = _reactNative.Platform.OS === 'ios' ? _expoConstants.default.expoConfig?.ios?.bundleIdentifier : _reactNative.Platform.OS === 'android' ? _expoConstants.default.expoConfig?.android?.package : undefined;
268
+ const appInstanceId = _expoConstants.default.expoConfig?.extra?.telemetry?.appInstanceId;
269
+ const skillCorrelationId = typeof appInstanceId === 'string' ? appInstanceId.trim() : '';
270
+ const requiredAuthScopes = (0, _react.useMemo)(() => [(0, _PowerPlatformEnvironmentApi.powerPlatformScope)(powerConfig.region ?? 'prod')], [powerConfig.region]);
271
+ const queryClient = (0, _react.useMemo)(() => (0, _createOfflineHostActivationCallbacks.createHostQueryClient)(), []);
291
272
 
292
273
  // Configure the app logger exactly once per mount inside useEffect so that
293
274
  // side effects (provider construction, async resolveDeviceId) never run
@@ -295,6 +276,9 @@ function PowerAppsProvider({
295
276
  // silent no-op Logger, so any telemetry emitted before the first commit is
296
277
  // silently dropped rather than crashing.
297
278
  (0, _react.useEffect)(() => {
279
+ // Customer telemetry is a no-op until a valid Application Insights
280
+ // provider is configured below.
281
+ (0, _powerAppsNativeCommon.configureCustomerTelemetryLogger)(new _powerAppsNativeCommon.Logger());
298
282
  const lp = loggerPropRef.current;
299
283
  if (lp === null) {
300
284
  // Explicit opt-out — replace with a silent no-op Logger.
@@ -310,26 +294,63 @@ function PowerAppsProvider({
310
294
  clientType: appName ?? 'PowerAppsNative',
311
295
  appVersion: _expoConstants.default.expoConfig?.version,
312
296
  minSeverity: __DEV__ ? 'verbose' : 'info',
313
- telemetryLocation: (0, _powerAppsNativeCommon.resolveTelemetryLocation)(powerConfig.geoRegion)
297
+ telemetryLocation: (0, _powerAppsNativeCommon.resolveTelemetryLocation)(powerConfig.geoRegion),
298
+ commonProperties: {
299
+ ...(appId ? {
300
+ appId
301
+ } : {}),
302
+ ...(bundleId ? {
303
+ bundleId
304
+ } : {}),
305
+ ...(skillCorrelationId ? {
306
+ skillCorrelationId
307
+ } : {})
308
+ }
309
+ };
310
+ const configureDefaultLogger = (deviceId = 'react-native') => {
311
+ const oneDsProvider = new _powerAppsNativeCommon.OneDsTelemetryProvider({
312
+ ...providerConfig,
313
+ deviceId
314
+ });
315
+ const providers = [oneDsProvider];
316
+ let appInsightsEnabled = false;
317
+ const configuredAppInsights = appInsightsConfigRef.current;
318
+ if (configuredAppInsights?.enabled) {
319
+ try {
320
+ const customerProvider = new _powerAppsNativeCommon.ApplicationInsightsTelemetryProvider({
321
+ ...configuredAppInsights,
322
+ appVersion: configuredAppInsights.appVersion ?? _expoConstants.default.expoConfig?.version,
323
+ deviceId,
324
+ sessionId: oneDsProvider.sessionId
325
+ });
326
+ providers.push(customerProvider);
327
+ (0, _powerAppsNativeCommon.configureCustomerTelemetryLogger)(new _powerAppsNativeCommon.Logger({
328
+ providers: [customerProvider]
329
+ }));
330
+ appInsightsEnabled = true;
331
+ } catch (error) {
332
+ console.error('[PowerAppsProvider] Application Insights configuration is invalid.', error);
333
+ }
334
+ }
335
+ const logger = new _powerAppsNativeCommon.Logger({
336
+ providers
337
+ });
338
+ (0, _powerAppsNativeCommon.configureAppLogger)(logger);
339
+ if (appInsightsEnabled) {
340
+ logger.trackActivityEvent('PowerAppsNative.ApplicationStarted', {
341
+ app_id: configuredAppInsights?.appId
342
+ });
343
+ }
314
344
  };
315
345
  // Resolve the stable device ID first so every event — including the
316
346
  // very first — carries the real GUID rather than the 'react-native'
317
347
  // fallback. Events emitted while this resolves are silently dropped
318
348
  // by the no-op Logger that is active until configureAppLogger is called.
319
349
  (0, _powerAppsNativeCommon.resolveDeviceId)().then(deviceId => {
320
- const provider = new _powerAppsNativeCommon.OneDsTelemetryProvider({
321
- ...providerConfig,
322
- deviceId
323
- });
324
- (0, _powerAppsNativeCommon.configureAppLogger)(new _powerAppsNativeCommon.Logger({
325
- providers: [provider]
326
- }));
350
+ configureDefaultLogger(deviceId);
327
351
  }).catch(_err => {
328
352
  // SecureStore unavailable — fall back to a provider without a stable ID.
329
- const provider = new _powerAppsNativeCommon.OneDsTelemetryProvider(providerConfig);
330
- (0, _powerAppsNativeCommon.configureAppLogger)(new _powerAppsNativeCommon.Logger({
331
- providers: [provider]
332
- }));
353
+ configureDefaultLogger();
333
354
  });
334
355
  }
335
356
 
@@ -342,6 +363,11 @@ function PowerAppsProvider({
342
363
  };
343
364
  // eslint-disable-next-line react-hooks/exhaustive-deps
344
365
  }, []); // intentionally empty — logger is configured once per mount
366
+
367
+ (0, _react.useEffect)(() => {
368
+ if (offlineTelemetryEnabled === undefined) return undefined;
369
+ return (0, _OfflineSdkTelemetry.configureOfflineSdkTelemetry)(offlineTelemetryEnabled);
370
+ }, [offlineTelemetryEnabled]);
345
371
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorBoundary.ErrorBoundary, {
346
372
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureHandlerRootView, {
347
373
  style: {
@@ -359,19 +385,32 @@ function PowerAppsProvider({
359
385
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_toast.ToastProvider, {
360
386
  swipeDirection: "horizontal",
361
387
  duration: 3000,
362
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactQuery.QueryClientProvider, {
363
- client: queryClient,
364
- children: missingClientIdInDevPlayer ? /*#__PURE__*/(0, _jsxRuntime.jsx)(MissingClientIdErrorScreen, {}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_AuthContext.AuthProvider, {
365
- msalConfig: msalConfig,
366
- intuneConfig: intuneConfig,
367
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(InnerProviders, {
388
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_SessionDetailsGesture.SessionDetailsGesture, {
389
+ enabled: showSessionDetailsOnSwipe,
390
+ appName: appName ?? _expoConstants.default.expoConfig?.name ?? 'Power Apps',
391
+ appId: appId || undefined,
392
+ environmentId: powerConfig.environmentId,
393
+ region: powerConfig.region ?? 'prod',
394
+ appVersion: _expoConstants.default.expoConfig?.version,
395
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactQuery.QueryClientProvider, {
396
+ client: queryClient,
397
+ children: missingAppId ? /*#__PURE__*/(0, _jsxRuntime.jsx)(MissingAppIdErrorScreen, {}) : missingEnvironmentId ? /*#__PURE__*/(0, _jsxRuntime.jsx)(MissingEnvironmentIdErrorScreen, {}) : missingClientIdInDevPlayer ? /*#__PURE__*/(0, _jsxRuntime.jsx)(MissingClientIdErrorScreen, {}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_AuthContext.AuthProvider, {
368
398
  msalConfig: msalConfig,
369
- powerConfig: powerConfig,
370
- schemaMap: schemaMap,
371
- renderOAuthWebView: renderOAuthWebView,
372
- onDisambiguationNeeded: onDisambiguationNeeded,
373
- offlineProfile: offlineProfile,
374
- children: children
399
+ intuneConfig: intuneConfig,
400
+ requiredScopes: requiredAuthScopes,
401
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(InnerProviders, {
402
+ msalConfig: msalConfig,
403
+ powerConfig: powerConfig,
404
+ schemaMap: schemaMap,
405
+ renderOAuthWebView: renderOAuthWebView,
406
+ onDisambiguationNeeded: onDisambiguationNeeded,
407
+ onConnectionConsent: onConnectionConsent,
408
+ onResourceConsent: onResourceConsent,
409
+ onConnectionConsentDeclined: onConnectionConsentDeclined,
410
+ offlineProfile: offlineProfile,
411
+ onOfflineEnabledChange: setOfflineTelemetryEnabled,
412
+ children: children
413
+ })
375
414
  })
376
415
  })
377
416
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_toast.ToastViewport, {
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SessionDetailsGesture = SessionDetailsGesture;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var Clipboard = _interopRequireWildcard(require("expo-clipboard"));
10
+ var _reactNativeGestureHandler = require("react-native-gesture-handler");
11
+ var _index = require("../telemetry/index.js");
12
+ var _jsxRuntime = require("react/jsx-runtime");
13
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
14
+ const SWIPE_DISTANCE = 80;
15
+ function SessionDetailsGesture({
16
+ enabled,
17
+ appName,
18
+ appId,
19
+ environmentId,
20
+ region,
21
+ appVersion,
22
+ children
23
+ }) {
24
+ const [visible, setVisible] = (0, _react.useState)(false);
25
+ const [sessionId, setSessionId] = (0, _react.useState)();
26
+ const [copied, setCopied] = (0, _react.useState)(false);
27
+ const showSessionDetails = (0, _react.useCallback)(() => {
28
+ setSessionId((0, _index.getAppLogger)().sessionId);
29
+ setCopied(false);
30
+ setVisible(true);
31
+ }, []);
32
+ const gesture = (0, _react.useMemo)(() => _reactNativeGestureHandler.Gesture.Pan().enabled(enabled && _reactNative.Platform.OS !== 'web').minPointers(3).maxPointers(3).runOnJS(true).onEnd(({
33
+ translationX,
34
+ translationY
35
+ }) => {
36
+ if (translationY >= SWIPE_DISTANCE && translationY > Math.abs(translationX)) {
37
+ showSessionDetails();
38
+ }
39
+ }), [enabled, showSessionDetails]);
40
+ const close = (0, _react.useCallback)(() => setVisible(false), []);
41
+ const copySessionId = (0, _react.useCallback)(async () => {
42
+ if (!sessionId) return;
43
+ await Clipboard.setStringAsync(sessionId);
44
+ setCopied(true);
45
+ }, [sessionId]);
46
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
47
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, {
48
+ gesture: gesture,
49
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
50
+ style: styles.content,
51
+ children: children
52
+ })
53
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
54
+ animationType: "fade",
55
+ onRequestClose: close,
56
+ transparent: true,
57
+ visible: visible,
58
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
59
+ accessibilityRole: "button",
60
+ accessibilityLabel: "Close session details",
61
+ onPress: close,
62
+ style: styles.backdrop,
63
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
64
+ accessible: false,
65
+ onPress: event => event.stopPropagation(),
66
+ style: styles.panel,
67
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
68
+ style: styles.title,
69
+ children: "Session details"
70
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SessionDetail, {
71
+ label: "Telemetry session ID",
72
+ value: sessionId ?? 'Not available',
73
+ selectable: true,
74
+ singleLine: true
75
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
76
+ accessibilityRole: "button",
77
+ disabled: !sessionId,
78
+ onPress: () => {
79
+ void copySessionId();
80
+ },
81
+ style: ({
82
+ pressed
83
+ }) => [styles.copyButton, !sessionId && styles.disabledButton, pressed && styles.pressedButton],
84
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
85
+ style: styles.copyButtonText,
86
+ children: copied ? 'Copied' : 'Copy session ID'
87
+ })
88
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SessionDetail, {
89
+ label: "App",
90
+ value: appName
91
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SessionDetail, {
92
+ label: "App ID",
93
+ value: appId ?? 'Not available',
94
+ selectable: true
95
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SessionDetail, {
96
+ label: "Environment ID",
97
+ value: environmentId ?? 'Not available',
98
+ selectable: true
99
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SessionDetail, {
100
+ label: "Region",
101
+ value: region
102
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SessionDetail, {
103
+ label: "App version",
104
+ value: appVersion ?? 'Not available'
105
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
106
+ accessibilityRole: "button",
107
+ onPress: close,
108
+ style: styles.closeButton,
109
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
110
+ style: styles.closeButtonText,
111
+ children: "Close"
112
+ })
113
+ })]
114
+ })
115
+ })
116
+ })]
117
+ });
118
+ }
119
+ function SessionDetail({
120
+ label,
121
+ value,
122
+ selectable = false,
123
+ singleLine = false
124
+ }) {
125
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
126
+ style: styles.row,
127
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
128
+ style: styles.label,
129
+ children: label
130
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
131
+ adjustsFontSizeToFit: singleLine,
132
+ minimumFontScale: 0.85,
133
+ numberOfLines: singleLine ? 1 : undefined,
134
+ selectable: selectable,
135
+ style: styles.value,
136
+ children: value
137
+ })]
138
+ });
139
+ }
140
+ const styles = _reactNative.StyleSheet.create({
141
+ content: {
142
+ flex: 1
143
+ },
144
+ backdrop: {
145
+ alignItems: 'center',
146
+ backgroundColor: 'rgba(0, 0, 0, 0.48)',
147
+ flex: 1,
148
+ justifyContent: 'center',
149
+ paddingHorizontal: 12,
150
+ paddingVertical: 24
151
+ },
152
+ panel: {
153
+ backgroundColor: '#FFFFFF',
154
+ borderRadius: 8,
155
+ maxWidth: 520,
156
+ paddingHorizontal: 16,
157
+ paddingVertical: 24,
158
+ width: '100%'
159
+ },
160
+ title: {
161
+ color: '#242424',
162
+ fontSize: 20,
163
+ fontWeight: '700',
164
+ marginBottom: 20
165
+ },
166
+ row: {
167
+ marginBottom: 14
168
+ },
169
+ label: {
170
+ color: '#616161',
171
+ fontSize: 12,
172
+ fontWeight: '600',
173
+ marginBottom: 3
174
+ },
175
+ value: {
176
+ color: '#242424',
177
+ fontSize: 15,
178
+ lineHeight: 21
179
+ },
180
+ copyButton: {
181
+ alignSelf: 'flex-start',
182
+ borderColor: '#0F6CBD',
183
+ borderRadius: 4,
184
+ borderWidth: 1,
185
+ marginBottom: 14,
186
+ marginTop: -4,
187
+ paddingHorizontal: 12,
188
+ paddingVertical: 7
189
+ },
190
+ copyButtonText: {
191
+ color: '#0F6CBD',
192
+ fontSize: 13,
193
+ fontWeight: '600'
194
+ },
195
+ disabledButton: {
196
+ opacity: 0.45
197
+ },
198
+ pressedButton: {
199
+ opacity: 0.7
200
+ },
201
+ closeButton: {
202
+ alignItems: 'center',
203
+ alignSelf: 'flex-end',
204
+ backgroundColor: '#0F6CBD',
205
+ borderRadius: 4,
206
+ marginTop: 8,
207
+ minWidth: 88,
208
+ paddingHorizontal: 18,
209
+ paddingVertical: 10
210
+ },
211
+ closeButtonText: {
212
+ color: '#FFFFFF',
213
+ fontSize: 14,
214
+ fontWeight: '600'
215
+ }
216
+ });