@microsoft/power-apps-native-host 0.2.22 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +162 -26
- package/compatibility/current.json +87 -0
- package/lib/NOTICE.txt +140 -1226
- package/lib/commonjs/auth/AuthContext.js +74 -18
- package/lib/commonjs/auth/AuthContext.web.js +32 -0
- package/lib/commonjs/auth/completePowerAppsAuthSession.js +16 -0
- package/lib/commonjs/bridge/NativePowerAppsBridge.js +20 -17
- package/lib/commonjs/bridge/PowerPagesBridge.js +157 -0
- 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 +188 -6
- package/lib/commonjs/logger.js +9 -6
- package/lib/commonjs/offline/createOfflineHostActivationCallbacks.js +61 -0
- package/lib/commonjs/offline/useOfflineIntegration.js +12 -0
- package/lib/commonjs/offline/useOfflineIntegration.web.js +11 -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/PowerAppsHostProvider.web.js +41 -0
- package/lib/commonjs/provider/PowerAppsProvider.js +229 -133
- 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/auth/AuthContext.web.js +26 -0
- package/lib/module/auth/completePowerAppsAuthSession.js +11 -0
- package/lib/module/bridge/NativePowerAppsBridge.js +21 -18
- package/lib/module/bridge/PowerPagesBridge.js +151 -0
- 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 +14 -6
- package/lib/module/logger.js +8 -6
- package/lib/module/offline/createOfflineHostActivationCallbacks.js +55 -0
- package/lib/module/offline/useOfflineIntegration.js +3 -0
- package/lib/module/offline/useOfflineIntegration.web.js +7 -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/PowerAppsHostProvider.web.js +36 -0
- package/lib/module/provider/PowerAppsProvider.js +233 -137
- 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 +21 -0
- package/lib/typescript/commonjs/auth/completePowerAppsAuthSession.d.ts +8 -0
- package/lib/typescript/commonjs/bridge/PowerPagesBridge.d.ts +18 -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 +20 -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/useOfflineIntegration.d.ts +2 -0
- package/lib/typescript/commonjs/offline/useOfflineIntegration.web.d.ts +4 -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/PowerAppsHostProvider.web.d.ts +5 -0
- package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +32 -10
- 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 +21 -0
- package/lib/typescript/module/auth/completePowerAppsAuthSession.d.ts +8 -0
- package/lib/typescript/module/bridge/PowerPagesBridge.d.ts +18 -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 +24 -14
- package/lib/typescript/module/logger.d.ts +4 -4
- package/lib/typescript/module/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
- package/lib/typescript/module/offline/useOfflineIntegration.d.ts +2 -0
- package/lib/typescript/module/offline/useOfflineIntegration.web.d.ts +4 -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/PowerAppsHostProvider.web.d.ts +5 -0
- package/lib/typescript/module/provider/PowerAppsProvider.d.ts +34 -12
- 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 +43 -18
- package/scripts/build-ios-base.js +66 -27
- package/scripts/bundle.js +212 -5
- package/scripts/generate-connector-schemas.js +8 -6
- package/scripts/metro-logger.js +213 -0
- package/scripts/start-web.mjs +137 -0
- package/scripts/upgrade/runtime.js +463 -0
- package/scripts/upgrade/system.js +244 -0
- package/scripts/upgrade-authoring.js +534 -0
- package/scripts/upgrade.js +188 -0
- package/scripts/wrap.js +536 -0
- 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
|
@@ -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;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.AuthProvider = AuthProvider;
|
|
7
|
+
exports.useAuth = useAuth;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
+
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); }
|
|
11
|
+
const hostedWebAuthState = {
|
|
12
|
+
isLoading: false,
|
|
13
|
+
isAuthReady: true,
|
|
14
|
+
isSignedIn: true,
|
|
15
|
+
hasRealAccount: false,
|
|
16
|
+
error: null,
|
|
17
|
+
acquireToken: async () => null,
|
|
18
|
+
signIn: async () => undefined,
|
|
19
|
+
signOut: async () => undefined
|
|
20
|
+
};
|
|
21
|
+
const AuthContext = /*#__PURE__*/(0, _react.createContext)(hostedWebAuthState);
|
|
22
|
+
function AuthProvider({
|
|
23
|
+
children
|
|
24
|
+
}) {
|
|
25
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(AuthContext.Provider, {
|
|
26
|
+
value: hostedWebAuthState,
|
|
27
|
+
children: children
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function useAuth() {
|
|
31
|
+
return (0, _react.useContext)(AuthContext);
|
|
32
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.completePowerAppsAuthSession = completePowerAppsAuthSession;
|
|
7
|
+
var WebBrowser = _interopRequireWildcard(require("expo-web-browser"));
|
|
8
|
+
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); }
|
|
9
|
+
/**
|
|
10
|
+
* Completes an Expo web authentication popup when the current route is its
|
|
11
|
+
* redirect target. Call this at module initialization in the OAuth callback
|
|
12
|
+
* route; navigation after completion remains application-owned.
|
|
13
|
+
*/
|
|
14
|
+
function completePowerAppsAuthSession(options) {
|
|
15
|
+
return WebBrowser.maybeCompleteAuthSession(options);
|
|
16
|
+
}
|
|
@@ -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 = {};
|
|
@@ -256,20 +252,27 @@ class NativePowerAppsBridge {
|
|
|
256
252
|
const sdkKey = key === 'content-type' ? 'Content-Type' : key;
|
|
257
253
|
responseHeaders[sdkKey] = value;
|
|
258
254
|
});
|
|
259
|
-
const buffer = await response.arrayBuffer();
|
|
260
255
|
(0, _logger.debugLog)(`[bridge] ${method} ${urlLabel} → ${response.status}`);
|
|
256
|
+
const correlationProperties = (0, _http.getHttpCorrelationProperties)(response.headers);
|
|
257
|
+
let buffer;
|
|
258
|
+
try {
|
|
259
|
+
buffer = await response.arrayBuffer();
|
|
260
|
+
} catch (readErr) {
|
|
261
|
+
(0, _logger.debugWarn)(`[bridge] HTTP ${response.status} for ${method} ${urlLabel} | failed to read response body:`, readErr);
|
|
262
|
+
(0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.BridgeHttpRequest, {
|
|
263
|
+
phase: 'readResponseBody',
|
|
264
|
+
errorName: getSafeErrorName(readErr),
|
|
265
|
+
httpStatus: response.status,
|
|
266
|
+
...correlationProperties
|
|
267
|
+
});
|
|
268
|
+
throw readErr;
|
|
269
|
+
}
|
|
261
270
|
if (!response.ok) {
|
|
262
271
|
const bodyText = new TextDecoder().decode(buffer).slice(0, 1000);
|
|
263
272
|
(0, _logger.debugWarn)(`[bridge] HTTP ${response.status} for ${method} ${urlLabel} | res: ${bodyText}`);
|
|
264
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}`);
|
|
265
|
-
scenario.completeWithError({
|
|
266
|
-
httpStatus: response.status
|
|
267
|
-
});
|
|
268
|
-
} else {
|
|
269
|
-
scenario.complete({
|
|
270
|
-
httpStatus: response.status
|
|
271
|
-
});
|
|
272
274
|
}
|
|
275
|
+
(0, _bridgeState.assertSuccessfulHttpStatus)(response.status, method);
|
|
273
276
|
return [{
|
|
274
277
|
status: response.status,
|
|
275
278
|
headers: responseHeaders
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.PowerPagesBridge = void 0;
|
|
7
|
+
exports.setupPowerPagesHost = setupPowerPagesHost;
|
|
8
|
+
var _index = require("../telemetry/index.js");
|
|
9
|
+
var _http = require("@microsoft/power-apps-native-common/http");
|
|
10
|
+
function toPowerPagesUrl(url) {
|
|
11
|
+
const parsedUrl = new URL(url, window.location.origin);
|
|
12
|
+
if (parsedUrl.origin === window.location.origin && parsedUrl.pathname.startsWith('/_api/')) {
|
|
13
|
+
return `${parsedUrl.pathname}${parsedUrl.search}`;
|
|
14
|
+
}
|
|
15
|
+
const lowerPath = parsedUrl.pathname.toLowerCase();
|
|
16
|
+
if (lowerPath.includes('/$metadata') || lowerPath.includes('/entitydefinitions') || lowerPath.includes('/$batch') || lowerPath.includes('/microsoft.dynamics.crm.')) {
|
|
17
|
+
throw new Error(`PowerPagesBridge does not support this Dataverse operation: ${parsedUrl.pathname}`);
|
|
18
|
+
}
|
|
19
|
+
const dataverseApiPattern = /\/api\/data\/v\d+(?:\.\d+)+\//gi;
|
|
20
|
+
let finalMatch = null;
|
|
21
|
+
let match;
|
|
22
|
+
while ((match = dataverseApiPattern.exec(parsedUrl.pathname)) !== null) {
|
|
23
|
+
finalMatch = match;
|
|
24
|
+
}
|
|
25
|
+
if (finalMatch?.index === undefined) {
|
|
26
|
+
throw new Error(`PowerPagesBridge only supports Dataverse Web API requests: ${parsedUrl.pathname}`);
|
|
27
|
+
}
|
|
28
|
+
const resourcePath = parsedUrl.pathname.slice(finalMatch.index + finalMatch[0].length);
|
|
29
|
+
return `/_api/${resourcePath}${parsedUrl.search}`;
|
|
30
|
+
}
|
|
31
|
+
let requestVerificationTokenPromise;
|
|
32
|
+
function getRequestVerificationToken() {
|
|
33
|
+
requestVerificationTokenPromise ??= (async () => {
|
|
34
|
+
const response = await _index.bridgeHttpClient.request('/_layout/tokenhtml', {
|
|
35
|
+
credentials: 'same-origin'
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
throw new Error(`PowerPagesBridge could not get a CSRF token (${response.status})`);
|
|
39
|
+
}
|
|
40
|
+
const tokenDocument = new DOMParser().parseFromString(await response.text(), 'text/html');
|
|
41
|
+
const token = tokenDocument.querySelector('input[name="__RequestVerificationToken"]')?.getAttribute('value');
|
|
42
|
+
if (!token) {
|
|
43
|
+
throw new Error('PowerPagesBridge could not find a CSRF token in /_layout/tokenhtml');
|
|
44
|
+
}
|
|
45
|
+
return token;
|
|
46
|
+
})().catch(error => {
|
|
47
|
+
requestVerificationTokenPromise = undefined;
|
|
48
|
+
throw error;
|
|
49
|
+
});
|
|
50
|
+
return requestVerificationTokenPromise;
|
|
51
|
+
}
|
|
52
|
+
function createHeaders(headers, token) {
|
|
53
|
+
const result = new Headers();
|
|
54
|
+
for (const [name, value] of Object.entries(headers ?? {})) {
|
|
55
|
+
if (name.toLowerCase() !== 'authorization') {
|
|
56
|
+
result.set(name, value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
result.set('__RequestVerificationToken', token);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
class PowerPagesBridge {
|
|
63
|
+
constructor(options) {
|
|
64
|
+
this.options = options;
|
|
65
|
+
}
|
|
66
|
+
updateOptions(options) {
|
|
67
|
+
this.options = options;
|
|
68
|
+
}
|
|
69
|
+
async initialize() {}
|
|
70
|
+
createRequestBody(config) {
|
|
71
|
+
return config.body ?? '';
|
|
72
|
+
}
|
|
73
|
+
async executePluginAsync(pluginName, pluginAction, params) {
|
|
74
|
+
const pluginKey = `${pluginName}.${pluginAction}`;
|
|
75
|
+
switch (pluginKey) {
|
|
76
|
+
case 'AppIdentityServicePlugin.getAppAccessTokenAsync':
|
|
77
|
+
case 'AppIdentityServicePlugin.getAppDynamicResourceAccessTokenAsync':
|
|
78
|
+
return '';
|
|
79
|
+
case 'AppPowerAppsClientPlugin.getAppCdsDataSourceConfigsAsync':
|
|
80
|
+
return this.options.dataSources;
|
|
81
|
+
case 'AppPowerAppsClientPlugin.loadAppConnectionsAsync_v2':
|
|
82
|
+
case 'AppPowerAppsClientPlugin.loadNonCompositeConnectionsAsync':
|
|
83
|
+
return this.options.connections;
|
|
84
|
+
case 'AppPowerAppsClientPlugin.resolveCompositeConnectionsAsync':
|
|
85
|
+
return null;
|
|
86
|
+
case 'AppHttpClientPlugin.sendHttpAsync':
|
|
87
|
+
return this.sendHttpAsync(params);
|
|
88
|
+
case 'PublishedAppTelemetry.trackEvent':
|
|
89
|
+
case 'PublishedAppTelemetry.trackException':
|
|
90
|
+
case 'PublishedAppTelemetry.trackMetric':
|
|
91
|
+
case 'PublishedAppTelemetry.startScenario':
|
|
92
|
+
case 'PublishedAppTelemetry.endScenario':
|
|
93
|
+
case 'PublishedAppTelemetry.setDefaultProperties':
|
|
94
|
+
return undefined;
|
|
95
|
+
default:
|
|
96
|
+
throw new Error(`PowerPagesBridge does not support ${pluginKey}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async sendHttpAsync(params) {
|
|
100
|
+
const request = params[0];
|
|
101
|
+
const body = params[1];
|
|
102
|
+
const powerPagesUrl = toPowerPagesUrl(request.url);
|
|
103
|
+
const token = await getRequestVerificationToken();
|
|
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);
|
|
116
|
+
const responseHeaders = {};
|
|
117
|
+
response.headers.forEach((value, name) => {
|
|
118
|
+
responseHeaders[name === 'content-type' ? 'Content-Type' : name] = value;
|
|
119
|
+
});
|
|
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
|
+
}
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
const responseText = new TextDecoder().decode(responseBody).slice(0, 1000);
|
|
134
|
+
const detail = responseText ? `: ${responseText}` : '';
|
|
135
|
+
// RuntimeDataClient treats JSON tuples as successful before inspecting status.
|
|
136
|
+
// Reject with its HTTP-plugin error shape so 4xx/5xx responses remain failures.
|
|
137
|
+
throw [[`Power Pages Web API request failed (${response.status})${detail}`, undefined, {
|
|
138
|
+
status: response.status,
|
|
139
|
+
headers: responseHeaders
|
|
140
|
+
}], responseBody];
|
|
141
|
+
}
|
|
142
|
+
return [{
|
|
143
|
+
status: response.status,
|
|
144
|
+
headers: responseHeaders
|
|
145
|
+
}, responseBody];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.PowerPagesBridge = PowerPagesBridge;
|
|
149
|
+
let bridge;
|
|
150
|
+
function setupPowerPagesHost(options) {
|
|
151
|
+
if (bridge) {
|
|
152
|
+
bridge.updateOptions(options);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
bridge = new PowerPagesBridge(options);
|
|
156
|
+
globalThis.powerAppsBridge = bridge;
|
|
157
|
+
}
|
|
@@ -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
|
+
}
|