@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
package/README.md CHANGED
@@ -104,8 +104,8 @@ npx --package @microsoft/power-apps-native-host@latest upgrade-template
104
104
 
105
105
  Each run upgrades one template version, installs compatible dependencies, and
106
106
  runs Expo validation. If another version is available, run the command again.
107
- The current version is stored in `.powerapps-native/version.json`; do not edit
108
- this file.
107
+ The current version is stored in `app.json` under
108
+ `expo.extra.powerappsNative`; do not edit this object.
109
109
 
110
110
  Apps created before this version file existed must specify their source template
111
111
  version:
@@ -161,6 +161,25 @@ export default function App() {
161
161
 
162
162
  See `src/index.ts` for the full exported API.
163
163
 
164
+ ## Metro Logger
165
+
166
+ Metro configuration can import the Node-only logger from
167
+ `@microsoft/power-apps-native-host/metro-logger`. Pass `projectRoot` when
168
+ wrapping middleware. An optional `onError(error, context)` callback receives the
169
+ first instrumentation failure for that project; `context` contains `phase` and
170
+ `logPath`. The callback is invoked at most once so diagnostics cannot recurse.
171
+ When it is omitted, the first failure is written once through the original
172
+ stderr stream. Logging and callback failures never propagate into Metro.
173
+
174
+ ```js
175
+ withPowerNativeMetroLogging(middleware, {
176
+ projectRoot: __dirname,
177
+ onError(error, { phase, logPath }) {
178
+ reportMetroLoggingFailure({ error, phase, logPath });
179
+ },
180
+ });
181
+ ```
182
+
164
183
  ## Bundled CLI Scripts
165
184
 
166
185
  This package exposes these CLI scripts through the `bin` field:
@@ -61,7 +61,6 @@
61
61
  "react-dom": "19.2.0",
62
62
  "react-hook-form": "7.54.0",
63
63
  "react-native": "0.83.6",
64
- "react-native-calendars": "1.1314.0",
65
64
  "react-native-gesture-handler": "2.30.1",
66
65
  "react-native-get-random-values": "^2.0.0",
67
66
  "react-native-reanimated": "4.2.1",
package/lib/NOTICE.txt CHANGED
@@ -396,7 +396,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
396
396
 
397
397
  ========================================================================
398
398
 
399
- framer-motion 12.42.2
399
+ framer-motion 12.43.0
400
400
  --
401
401
  The MIT License (MIT)
402
402
 
@@ -422,13 +422,7 @@ SOFTWARE.
422
422
 
423
423
  ========================================================================
424
424
 
425
- imurmurhash 0.1.4
426
- --
427
- Copyright Jens Taylor; licensed under MIT (https://spdx.org/licenses/MIT.html)
428
-
429
- ========================================================================
430
-
431
- motion 12.42.2
425
+ motion 12.43.0
432
426
  --
433
427
  The MIT License (MIT)
434
428
 
@@ -454,7 +448,7 @@ SOFTWARE.
454
448
 
455
449
  ========================================================================
456
450
 
457
- motion-dom 12.42.2
451
+ motion-dom 12.43.0
458
452
  --
459
453
  The MIT License (MIT)
460
454
 
@@ -532,11 +526,11 @@ Copyright (c) 2024-present Cedric van Putten <me@cedric.dev>
532
526
 
533
527
  ========================================================================
534
528
 
535
- signal-exit 3.0.7
529
+ signal-exit 4.1.0
536
530
  --
537
531
  The ISC License
538
532
 
539
- Copyright (c) 2015, Contributors
533
+ Copyright (c) 2015-2023 Benjamin Coe, Isaac Z. Schlueter, and Contributors
540
534
 
541
535
  Permission to use, copy, modify, and/or distribute this software
542
536
  for any purpose with or without fee is hereby granted, provided
@@ -570,7 +564,7 @@ PERFORMANCE OF THIS SOFTWARE.
570
564
 
571
565
  ========================================================================
572
566
 
573
- write-file-atomic 4.0.2
567
+ write-file-atomic 7.0.1
574
568
  --
575
569
  Copyright (c) 2015, Rebecca Turner
576
570
 
@@ -36,10 +36,49 @@ const SIGN_IN_SCOPES = ['https://api.powerplatform.com/.default'];
36
36
  function getSafeErrorName(error) {
37
37
  return error instanceof Error ? error.constructor.name : typeof error;
38
38
  }
39
+ function accountsMatch(left, right) {
40
+ return left.identifier === right.identifier && left.tenantId === right.tenantId;
41
+ }
42
+ async function validateRequiredScopes(pca, account, scopes) {
43
+ for (const scope of scopes) {
44
+ await pca.acquireTokenSilent({
45
+ scopes: [scope],
46
+ account
47
+ });
48
+ }
49
+ }
50
+ async function acquireRequiredScopesInteractively(pca, scopes) {
51
+ const [primaryScope, ...additionalScopes] = scopes;
52
+ const primaryResult = await pca.acquireToken({
53
+ scopes: [primaryScope],
54
+ prompt: 'select_account'
55
+ });
56
+ const signedInAccount = primaryResult.account;
57
+ for (const scope of additionalScopes) {
58
+ try {
59
+ await pca.acquireTokenSilent({
60
+ scopes: [scope],
61
+ account: signedInAccount
62
+ });
63
+ } catch {
64
+ const result = await pca.acquireToken({
65
+ scopes: [scope],
66
+ loginHint: signedInAccount.username,
67
+ prompt: 'login'
68
+ });
69
+ if (!accountsMatch(signedInAccount, result.account)) {
70
+ throw new Error('Sign in with the same account for all required Power Platform permissions.');
71
+ }
72
+ }
73
+ }
74
+ await validateRequiredScopes(pca, signedInAccount, scopes);
75
+ return signedInAccount;
76
+ }
39
77
  const AuthContext = /*#__PURE__*/(0, _react.createContext)(null);
40
78
  function AuthProvider({
41
79
  msalConfig,
42
80
  intuneConfig,
81
+ requiredScopes = [],
43
82
  children
44
83
  }) {
45
84
  const [isAuthReady, setIsAuthReady] = (0, _react.useState)(false);
@@ -104,6 +143,8 @@ function AuthProvider({
104
143
  // Skip MSAL entirely; treat the session as already signed in so the app
105
144
  // renders without requiring credentials.
106
145
  const noAuthMode = !effectiveClientId;
146
+ const requiredScopeKey = [...new Set(requiredScopes)].join('|');
147
+ const effectiveRequiredScopes = requiredScopeKey ? requiredScopeKey.split('|') : SIGN_IN_SCOPES;
107
148
  const pcaRef = (0, _react.useRef)(null);
108
149
  (0, _react.useEffect)(() => {
109
150
  if (noAuthMode) {
@@ -129,8 +170,20 @@ function AuthProvider({
129
170
  useBroker,
130
171
  platform: _reactNative.Platform.OS
131
172
  });
132
- pca.init().then(() => pca.getAccounts()).then(accounts => {
133
- const restoredAccount = accounts[0] ?? null;
173
+ pca.init().then(() => pca.getAccounts()).then(async accounts => {
174
+ let restoredAccount = accounts[0] ?? null;
175
+ if (restoredAccount) {
176
+ try {
177
+ await validateRequiredScopes(pca, restoredAccount, effectiveRequiredScopes);
178
+ } catch (tokenError) {
179
+ await pca.signOut({
180
+ account: restoredAccount
181
+ }).catch(() => undefined);
182
+ restoredAccount = null;
183
+ setError(new Error('Your session has expired. Sign in again to continue.'));
184
+ (0, _logger.debugWarn)('[AuthProvider] restored account is missing a required token:', tokenError);
185
+ }
186
+ }
134
187
  setAccount(restoredAccount);
135
188
  if (restoredAccount) {
136
189
  (0, _index.getAppLogger)().identifyUser({
@@ -154,7 +207,7 @@ function AuthProvider({
154
207
  });
155
208
  // Re-initialize only when the effective broker/identity settings change.
156
209
  // eslint-disable-next-line react-hooks/exhaustive-deps
157
- }, [effectiveClientId, effectiveTenantId, effectiveRedirectUri, useBroker]);
210
+ }, [effectiveClientId, effectiveTenantId, effectiveRedirectUri, requiredScopeKey, useBroker]);
158
211
  const signIn = (0, _react.useCallback)(async () => {
159
212
  const pca = pcaRef.current;
160
213
  if (!pca) return;
@@ -168,13 +221,11 @@ function AuthProvider({
168
221
  if (!pca.isInitialized) {
169
222
  await pca.init();
170
223
  }
171
- const result = await pca.acquireToken({
172
- scopes: SIGN_IN_SCOPES
173
- });
174
- setAccount(result.account);
224
+ const signedInAccount = await acquireRequiredScopesInteractively(pca, effectiveRequiredScopes);
225
+ setAccount(signedInAccount);
175
226
  (0, _index.getAppLogger)().identifyUser({
176
- objectId: result.account.identifier,
177
- tenantId: result.account.tenantId
227
+ objectId: signedInAccount.identifier,
228
+ tenantId: signedInAccount.tenantId
178
229
  });
179
230
  // Register the signed-in account with Intune MAM so the SDK can apply
180
231
  // app protection policies (PIN, wipe, copy-paste restriction, etc.).
@@ -183,9 +234,9 @@ function AuthProvider({
183
234
  if (intuneEnabled) {
184
235
  try {
185
236
  await (0, _powerAppsNativeAuth.registerAndEnrollAccount)({
186
- upn: result.account.username,
187
- aadId: result.account.identifier,
188
- tenantId: result.account.tenantId
237
+ upn: signedInAccount.username,
238
+ aadId: signedInAccount.identifier,
239
+ tenantId: signedInAccount.tenantId
189
240
  });
190
241
  (0, _index.getAppLogger)().trackActivityEvent(_index.HostOperationNames.AuthIntuneEnroll);
191
242
  } catch (intuneErr) {
@@ -206,7 +257,7 @@ function AuthProvider({
206
257
  } finally {
207
258
  setIsLoading(false);
208
259
  }
209
- }, [intuneEnabled, useBroker]);
260
+ }, [intuneEnabled, requiredScopeKey, useBroker]);
210
261
  const acquireToken = (0, _react.useCallback)(async scopes => {
211
262
  const pca = pcaRef.current;
212
263
  if (!pca || !account) return null;
@@ -217,18 +268,23 @@ function AuthProvider({
217
268
  });
218
269
  return result.accessToken;
219
270
  } catch (e) {
220
- // Silent acquisition failed — do NOT fall back to interactive here.
221
- // Interactive sign-in is handled exclusively by signIn(). Falling back
222
- // to interactive from acquireToken triggers an unexpected login webview
223
- // whenever a connector needs a token the cache doesn't hold yet.
224
271
  (0, _logger.debugWarn)('[AuthContext] acquireTokenSilent failed for scopes:', scopes, e);
225
272
  (0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.AuthAcquireTokenSilentFailed, {
226
273
  scopeCount: scopes.length,
227
274
  errorName: getSafeErrorName(e)
228
275
  });
276
+ if (scopes.some(scope => effectiveRequiredScopes.includes(scope))) {
277
+ await pca.signOut({
278
+ account
279
+ }).catch(signOutError => {
280
+ (0, _logger.debugWarn)('[AuthContext] failed to clear an invalid session:', signOutError);
281
+ });
282
+ setError(new Error('Your session has expired. Sign in again to continue.'));
283
+ setAccount(null);
284
+ }
229
285
  return null;
230
286
  }
231
- }, [account]);
287
+ }, [account, requiredScopeKey]);
232
288
  const signOut = (0, _react.useCallback)(async () => {
233
289
  const pca = pcaRef.current;
234
290
  if (!pca || !account) return;
@@ -7,7 +7,9 @@ exports.NativePowerAppsBridge = void 0;
7
7
  exports.buildConnectionReferences = buildConnectionReferences;
8
8
  var _logger = require("../logger.js");
9
9
  var _index = require("../telemetry/index.js");
10
+ var _http = require("@microsoft/power-apps-native-common/http");
10
11
  var _powerAppsNativeCommon = require("@microsoft/power-apps-native-common");
12
+ var _bridgeState = require("./bridgeState.js");
11
13
  /*!
12
14
  * power-apps-native-host — NativePowerAppsBridge
13
15
  *
@@ -120,7 +122,7 @@ class NativePowerAppsBridge {
120
122
  * via globalThis would have no effect — mutating options is the correct path.
121
123
  */
122
124
  updateOptions(options) {
123
- this._options = options;
125
+ this._options = (0, _bridgeState.updateBridgeOptionsInPlace)(this._options, options);
124
126
  }
125
127
  async initialize() {
126
128
  // No handshake required for the native path.
@@ -203,10 +205,6 @@ class NativePowerAppsBridge {
203
205
  method
204
206
  } = requestConfig;
205
207
  const urlLabel = (0, _powerAppsNativeCommon.sanitizeUrl)(url.replace(/^https?:\/\/[^/]+/, '').slice(0, 100));
206
- const scenario = (0, _index.getAppLogger)().trackScenario(_index.HostOperationNames.BridgeHttpRequest, {
207
- httpMethod: method,
208
- url: urlLabel
209
- });
210
208
  const isBlob = bodyParam instanceof Blob;
211
209
 
212
210
  // The SDK builds Authorization headers with the "paauth" scheme (its web-player
@@ -231,11 +229,12 @@ class NativePowerAppsBridge {
231
229
  // SDK passed a plain object — JSON-stringify it as a fallback
232
230
  bodyInit = JSON.stringify(bodyParam);
233
231
  }
232
+ bodyInit = (0, _bridgeState.getRequestBodyForMethod)(method, bodyInit);
234
233
  headers = normalizeJsonBodyHeaders(headers, bodyInit);
235
234
  (0, _logger.debugLog)(`[bridge] → ${method} ${urlLabel}`);
236
235
  let response;
237
236
  try {
238
- response = await fetch(url, {
237
+ response = await _index.bridgeHttpClient.request(url, {
239
238
  method,
240
239
  headers: headers,
241
240
  body: bodyInit
@@ -243,9 +242,6 @@ class NativePowerAppsBridge {
243
242
  } catch (fetchErr) {
244
243
  (0, _logger.debugWarn)(`[bridge] fetch THREW for ${method} ${urlLabel}:`, fetchErr);
245
244
  (0, _logger.debugLog)(`[bridge] fetch THREW for ${method} ${urlLabel} | req body: ${typeof bodyInit === 'string' ? bodyInit.slice(0, 500) : bodyInit != null ? '<non-string body>' : '<no body>'}`);
246
- scenario.fail(undefined, {
247
- errorName: getSafeErrorName(fetchErr)
248
- });
249
245
  throw fetchErr;
250
246
  }
251
247
  const responseHeaders = {};
@@ -257,14 +253,17 @@ class NativePowerAppsBridge {
257
253
  responseHeaders[sdkKey] = value;
258
254
  });
259
255
  (0, _logger.debugLog)(`[bridge] ${method} ${urlLabel} → ${response.status}`);
256
+ const correlationProperties = (0, _http.getHttpCorrelationProperties)(response.headers);
260
257
  let buffer;
261
258
  try {
262
259
  buffer = await response.arrayBuffer();
263
260
  } catch (readErr) {
264
261
  (0, _logger.debugWarn)(`[bridge] HTTP ${response.status} for ${method} ${urlLabel} | failed to read response body:`, readErr);
265
- scenario.fail(undefined, {
262
+ (0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.BridgeHttpRequest, {
263
+ phase: 'readResponseBody',
266
264
  errorName: getSafeErrorName(readErr),
267
- httpStatus: response.status
265
+ httpStatus: response.status,
266
+ ...correlationProperties
268
267
  });
269
268
  throw readErr;
270
269
  }
@@ -272,14 +271,8 @@ class NativePowerAppsBridge {
272
271
  const bodyText = new TextDecoder().decode(buffer).slice(0, 1000);
273
272
  (0, _logger.debugWarn)(`[bridge] HTTP ${response.status} for ${method} ${urlLabel} | res: ${bodyText}`);
274
273
  (0, _logger.debugLog)(`[bridge] HTTP ${response.status} for ${method} ${urlLabel} | req: ${typeof bodyInit === 'string' ? bodyInit.slice(0, 500) : bodyInit != null ? '<non-string body>' : '<no body>'} | res: ${bodyText}`);
275
- scenario.completeWithError({
276
- httpStatus: response.status
277
- });
278
- } else {
279
- scenario.complete({
280
- httpStatus: response.status
281
- });
282
274
  }
275
+ (0, _bridgeState.assertSuccessfulHttpStatus)(response.status, method);
283
276
  return [{
284
277
  status: response.status,
285
278
  headers: responseHeaders
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.PowerPagesBridge = void 0;
7
7
  exports.setupPowerPagesHost = setupPowerPagesHost;
8
+ var _index = require("../telemetry/index.js");
9
+ var _http = require("@microsoft/power-apps-native-common/http");
8
10
  function toPowerPagesUrl(url) {
9
11
  const parsedUrl = new URL(url, window.location.origin);
10
12
  if (parsedUrl.origin === window.location.origin && parsedUrl.pathname.startsWith('/_api/')) {
@@ -29,7 +31,7 @@ function toPowerPagesUrl(url) {
29
31
  let requestVerificationTokenPromise;
30
32
  function getRequestVerificationToken() {
31
33
  requestVerificationTokenPromise ??= (async () => {
32
- const response = await fetch('/_layout/tokenhtml', {
34
+ const response = await _index.bridgeHttpClient.request('/_layout/tokenhtml', {
33
35
  credentials: 'same-origin'
34
36
  });
35
37
  if (!response.ok) {
@@ -99,17 +101,34 @@ class PowerPagesBridge {
99
101
  const body = params[1];
100
102
  const powerPagesUrl = toPowerPagesUrl(request.url);
101
103
  const token = await getRequestVerificationToken();
102
- const response = await fetch(powerPagesUrl, {
103
- method: request.method,
104
- headers: createHeaders(request.headers, token),
105
- body: body || undefined,
106
- credentials: 'same-origin'
107
- });
104
+ let response;
105
+ try {
106
+ response = await _index.bridgeHttpClient.request(powerPagesUrl, {
107
+ method: request.method,
108
+ headers: createHeaders(request.headers, token),
109
+ body: body || undefined,
110
+ credentials: 'same-origin'
111
+ });
112
+ } catch (error) {
113
+ throw error;
114
+ }
115
+ const correlationProperties = (0, _http.getHttpCorrelationProperties)(response.headers);
108
116
  const responseHeaders = {};
109
117
  response.headers.forEach((value, name) => {
110
118
  responseHeaders[name === 'content-type' ? 'Content-Type' : name] = value;
111
119
  });
112
- const responseBody = await response.arrayBuffer();
120
+ let responseBody;
121
+ try {
122
+ responseBody = await response.arrayBuffer();
123
+ } catch (error) {
124
+ (0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.BridgeHttpRequest, {
125
+ phase: 'readResponseBody',
126
+ errorName: error instanceof Error ? error.constructor.name : typeof error,
127
+ httpStatus: response.status,
128
+ ...correlationProperties
129
+ });
130
+ throw error;
131
+ }
113
132
  if (!response.ok) {
114
133
  const responseText = new TextDecoder().decode(responseBody).slice(0, 1000);
115
134
  const detail = responseText ? `: ${responseText}` : '';
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.assertSuccessfulHttpStatus = assertSuccessfulHttpStatus;
7
+ exports.getRequestBodyForMethod = getRequestBodyForMethod;
8
+ exports.updateBridgeOptionsInPlace = updateBridgeOptionsInPlace;
9
+ function getRequestBodyForMethod(method, body) {
10
+ const normalizedMethod = method.toUpperCase();
11
+ return normalizedMethod === 'GET' || normalizedMethod === 'HEAD' ? null : body;
12
+ }
13
+ function assertSuccessfulHttpStatus(status, method) {
14
+ if (status >= 200 && status < 300) return;
15
+ throw new Error(`HTTP ${status} for ${method.toUpperCase()} request`);
16
+ }
17
+ function updateBridgeOptionsInPlace(current, next) {
18
+ const connections = current.connections;
19
+ for (const key of Object.keys(connections)) {
20
+ if (!(key in next.connections)) delete connections[key];
21
+ }
22
+ for (const [key, nextConnection] of Object.entries(next.connections)) {
23
+ const currentConnection = connections[key];
24
+ if (currentConnection) {
25
+ for (const property of Object.keys(currentConnection)) {
26
+ delete currentConnection[property];
27
+ }
28
+ Object.assign(currentConnection, nextConnection);
29
+ } else {
30
+ connections[key] = nextConnection;
31
+ }
32
+ }
33
+ const dataSources = current.dataSources;
34
+ dataSources.splice(0, dataSources.length, ...next.dataSources);
35
+ return {
36
+ ...next,
37
+ connections,
38
+ dataSources
39
+ };
40
+ }