@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.
- package/README.md +21 -2
- package/compatibility/current.json +0 -1
- package/lib/NOTICE.txt +6 -12
- package/lib/commonjs/auth/AuthContext.js +74 -18
- package/lib/commonjs/bridge/NativePowerAppsBridge.js +11 -18
- package/lib/commonjs/bridge/PowerPagesBridge.js +27 -8
- package/lib/commonjs/bridge/bridgeState.js +40 -0
- package/lib/commonjs/connections/ConnectionSetupScreen.js +459 -80
- package/lib/commonjs/connections/PowerPlatformConnectionApi.js +946 -0
- package/lib/commonjs/connections/connectionMetadata.js +127 -0
- package/lib/commonjs/connections/connectionState.js +96 -0
- package/lib/commonjs/connections/disambiguate.js +19 -11
- package/lib/commonjs/connections/resolveConnections.js +347 -0
- package/lib/commonjs/connections/types.js +106 -0
- package/lib/commonjs/{platform → connections}/useConnectionRefs.js +128 -65
- package/lib/commonjs/connections/useConnectionSetup.js +111 -52
- package/lib/commonjs/controls/FilePicker.js +6 -0
- package/lib/commonjs/controls/ImagePicker.js +8 -0
- package/lib/commonjs/index.js +166 -4
- package/lib/commonjs/logger.js +9 -6
- package/lib/commonjs/offline/createOfflineHostActivationCallbacks.js +61 -0
- package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.js +46 -0
- package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.web.js +13 -0
- package/lib/commonjs/platform/DataverseDataSources.js +14 -0
- package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +49 -0
- package/lib/commonjs/platform/GlobalDiscovery.js +51 -0
- package/lib/commonjs/platform/PowerPlatformEnvironmentApi.js +6 -106
- package/lib/commonjs/provider/PowerAppsHostProvider.js +156 -25
- package/lib/commonjs/provider/PowerAppsProvider.js +173 -134
- package/lib/commonjs/provider/SessionDetailsGesture.js +216 -0
- package/lib/commonjs/telemetry/OfflineSdkTelemetry.js +95 -0
- package/lib/commonjs/telemetry/OfflineSdkTelemetry.web.js +9 -0
- package/lib/commonjs/telemetry/index.js +22 -3
- package/lib/module/auth/AuthContext.js +74 -18
- package/lib/module/bridge/NativePowerAppsBridge.js +12 -19
- package/lib/module/bridge/PowerPagesBridge.js +27 -8
- package/lib/module/bridge/bridgeState.js +34 -0
- package/lib/module/connections/ConnectionSetupScreen.js +460 -81
- package/lib/module/connections/PowerPlatformConnectionApi.js +932 -0
- package/lib/module/connections/connectionMetadata.js +112 -0
- package/lib/module/connections/connectionState.js +82 -0
- package/lib/module/connections/disambiguate.js +19 -12
- package/lib/module/connections/resolveConnections.js +342 -0
- package/lib/module/connections/types.js +91 -0
- package/lib/module/{platform → connections}/useConnectionRefs.js +129 -66
- package/lib/module/connections/useConnectionSetup.js +113 -54
- package/lib/module/controls/FilePicker.js +6 -0
- package/lib/module/controls/ImagePicker.js +8 -0
- package/lib/module/index.js +10 -4
- package/lib/module/logger.js +8 -6
- package/lib/module/offline/createOfflineHostActivationCallbacks.js +55 -0
- package/lib/module/offline/useOfflineIntegrationWithStartupGate.js +42 -0
- package/lib/module/offline/useOfflineIntegrationWithStartupGate.web.js +9 -0
- package/lib/module/platform/DataverseDataSources.js +10 -0
- package/lib/module/platform/DataverseGlobalDiscoveryApi.js +45 -0
- package/lib/module/platform/GlobalDiscovery.js +45 -0
- package/lib/module/platform/PowerPlatformEnvironmentApi.js +5 -104
- package/lib/module/provider/PowerAppsHostProvider.js +159 -28
- package/lib/module/provider/PowerAppsProvider.js +177 -138
- package/lib/module/provider/SessionDetailsGesture.js +211 -0
- package/lib/module/telemetry/OfflineSdkTelemetry.js +91 -0
- package/lib/module/telemetry/OfflineSdkTelemetry.web.js +5 -0
- package/lib/module/telemetry/index.js +15 -4
- package/lib/typescript/commonjs/auth/AuthContext.d.ts +3 -1
- package/lib/typescript/commonjs/auth/AuthContext.web.d.ts +1 -0
- package/lib/typescript/commonjs/bridge/bridgeState.d.ts +5 -0
- package/lib/typescript/commonjs/connections/ConnectionSetupScreen.d.ts +28 -7
- package/lib/typescript/commonjs/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
- package/lib/typescript/commonjs/connections/connectionMetadata.d.ts +66 -0
- package/lib/typescript/commonjs/connections/connectionState.d.ts +16 -0
- package/lib/typescript/commonjs/connections/disambiguate.d.ts +4 -2
- package/lib/typescript/commonjs/{platform → connections}/resolveConnections.d.ts +19 -19
- package/lib/typescript/commonjs/connections/types.d.ts +129 -1
- package/lib/typescript/commonjs/{platform → connections}/useConnectionRefs.d.ts +33 -24
- package/lib/typescript/commonjs/connections/useConnectionSetup.d.ts +12 -8
- package/lib/typescript/commonjs/controls/FilePicker.d.ts +7 -1
- package/lib/typescript/commonjs/controls/ImagePicker.d.ts +9 -1
- package/lib/typescript/commonjs/index.d.ts +17 -10
- package/lib/typescript/commonjs/logger.d.ts +4 -4
- package/lib/typescript/commonjs/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
- package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
- package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
- package/lib/typescript/commonjs/platform/DataverseDataSources.d.ts +27 -0
- package/lib/typescript/commonjs/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
- package/lib/typescript/commonjs/platform/GlobalDiscovery.d.ts +4 -0
- package/lib/typescript/commonjs/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
- package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +29 -18
- package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +30 -8
- package/lib/typescript/commonjs/provider/SessionDetailsGesture.d.ts +13 -0
- package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.d.ts +15 -0
- package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
- package/lib/typescript/commonjs/telemetry/index.d.ts +13 -1
- package/lib/typescript/module/auth/AuthContext.d.ts +3 -1
- package/lib/typescript/module/auth/AuthContext.web.d.ts +1 -0
- package/lib/typescript/module/bridge/bridgeState.d.ts +5 -0
- package/lib/typescript/module/connections/ConnectionSetupScreen.d.ts +28 -7
- package/lib/typescript/module/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
- package/lib/typescript/module/connections/connectionMetadata.d.ts +66 -0
- package/lib/typescript/module/connections/connectionState.d.ts +16 -0
- package/lib/typescript/module/connections/disambiguate.d.ts +4 -2
- package/lib/typescript/module/{platform → connections}/resolveConnections.d.ts +19 -19
- package/lib/typescript/module/connections/types.d.ts +129 -1
- package/lib/typescript/module/{platform → connections}/useConnectionRefs.d.ts +33 -24
- package/lib/typescript/module/connections/useConnectionSetup.d.ts +12 -8
- package/lib/typescript/module/controls/FilePicker.d.ts +7 -1
- package/lib/typescript/module/controls/ImagePicker.d.ts +9 -1
- package/lib/typescript/module/index.d.ts +17 -10
- package/lib/typescript/module/logger.d.ts +4 -4
- package/lib/typescript/module/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
- package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
- package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
- package/lib/typescript/module/platform/DataverseDataSources.d.ts +27 -0
- package/lib/typescript/module/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
- package/lib/typescript/module/platform/GlobalDiscovery.d.ts +4 -0
- package/lib/typescript/module/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
- package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +29 -18
- package/lib/typescript/module/provider/PowerAppsProvider.d.ts +30 -8
- package/lib/typescript/module/provider/SessionDetailsGesture.d.ts +13 -0
- package/lib/typescript/module/telemetry/OfflineSdkTelemetry.d.ts +15 -0
- package/lib/typescript/module/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
- package/lib/typescript/module/telemetry/index.d.ts +13 -1
- package/package.json +11 -6
- package/scripts/metro-logger.js +213 -0
- package/scripts/upgrade/runtime.js +13 -7
- package/scripts/upgrade-authoring.js +5 -6
- package/lib/commonjs/platform/PowerPlatformConnectionApi.js +0 -606
- package/lib/commonjs/platform/resolveConnections.js +0 -264
- package/lib/module/platform/PowerPlatformConnectionApi.js +0 -595
- 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
|
|
108
|
-
this
|
|
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:
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
172
|
-
|
|
173
|
-
});
|
|
174
|
-
setAccount(result.account);
|
|
224
|
+
const signedInAccount = await acquireRequiredScopesInteractively(pca, effectiveRequiredScopes);
|
|
225
|
+
setAccount(signedInAccount);
|
|
175
226
|
(0, _index.getAppLogger)().identifyUser({
|
|
176
|
-
objectId:
|
|
177
|
-
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:
|
|
187
|
-
aadId:
|
|
188
|
-
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
+
}
|