@microsoft/power-apps-native-host 0.3.3 → 0.5.1

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 (111) hide show
  1. package/README.md +60 -0
  2. package/compatibility/current.json +1 -1
  3. package/lib/NOTICE.txt +2957 -371
  4. package/lib/commonjs/auth/AuthContext.js +49 -22
  5. package/lib/commonjs/auth/targetAuthorization.js +53 -0
  6. package/lib/commonjs/bridge/NativePowerAppsBridge.js +33 -2
  7. package/lib/commonjs/bridge/PowerPagesBridge.js +1 -3
  8. package/lib/commonjs/bridge/setupNativeHost.js +6 -0
  9. package/lib/commonjs/config/expoConfig.js +14 -0
  10. package/lib/commonjs/config/runtimeTenantMode.js +16 -0
  11. package/lib/commonjs/config/runtimeTenantMode.web.js +9 -0
  12. package/lib/commonjs/config/tenantMode.js +68 -0
  13. package/lib/commonjs/connections/PowerPlatformConnectionApi.js +24 -27
  14. package/lib/commonjs/connections/connectionMetadata.js +2 -2
  15. package/lib/commonjs/connections/resolveConnections.js +8 -3
  16. package/lib/commonjs/connections/types.js +9 -2
  17. package/lib/commonjs/connections/useConnectionRefs.js +19 -10
  18. package/lib/commonjs/index.js +41 -2
  19. package/lib/commonjs/platform/CanvasAppResolution.js +33 -0
  20. package/lib/commonjs/platform/CanvasAppResolutionApi.js +69 -0
  21. package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +50 -13
  22. package/lib/commonjs/platform/GlobalDiscovery.js +40 -1
  23. package/lib/commonjs/provider/PowerAppsHostProvider.js +47 -8
  24. package/lib/commonjs/provider/PowerAppsProvider.js +175 -9
  25. package/lib/commonjs/target/DefaultPowerAppsExperience.js +564 -0
  26. package/lib/commonjs/target/PowerAppsExperience.js +41 -0
  27. package/lib/commonjs/target/PowerAppsTargetBootstrap.js +534 -0
  28. package/lib/commonjs/target/errors.js +24 -0
  29. package/lib/commonjs/target/types.js +1 -0
  30. package/lib/commonjs/telemetry/index.js +2 -2
  31. package/lib/module/auth/AuthContext.js +49 -22
  32. package/lib/module/auth/targetAuthorization.js +48 -0
  33. package/lib/module/bridge/NativePowerAppsBridge.js +33 -2
  34. package/lib/module/bridge/PowerPagesBridge.js +1 -3
  35. package/lib/module/bridge/setupNativeHost.js +5 -0
  36. package/lib/module/config/expoConfig.js +14 -0
  37. package/lib/module/config/runtimeTenantMode.js +12 -0
  38. package/lib/module/config/runtimeTenantMode.web.js +5 -0
  39. package/lib/module/config/tenantMode.js +61 -0
  40. package/lib/module/connections/PowerPlatformConnectionApi.js +24 -27
  41. package/lib/module/connections/connectionMetadata.js +2 -2
  42. package/lib/module/connections/resolveConnections.js +9 -4
  43. package/lib/module/connections/types.js +9 -2
  44. package/lib/module/connections/useConnectionRefs.js +20 -11
  45. package/lib/module/index.js +5 -2
  46. package/lib/module/platform/CanvasAppResolution.js +27 -0
  47. package/lib/module/platform/CanvasAppResolutionApi.js +65 -0
  48. package/lib/module/platform/DataverseGlobalDiscoveryApi.js +49 -14
  49. package/lib/module/platform/GlobalDiscovery.js +37 -1
  50. package/lib/module/provider/PowerAppsHostProvider.js +47 -8
  51. package/lib/module/provider/PowerAppsProvider.js +179 -13
  52. package/lib/module/target/DefaultPowerAppsExperience.js +559 -0
  53. package/lib/module/target/PowerAppsExperience.js +34 -0
  54. package/lib/module/target/PowerAppsTargetBootstrap.js +529 -0
  55. package/lib/module/target/errors.js +19 -0
  56. package/lib/module/target/types.js +1 -0
  57. package/lib/module/telemetry/index.js +1 -1
  58. package/lib/typescript/commonjs/auth/AuthContext.d.ts +11 -2
  59. package/lib/typescript/commonjs/auth/targetAuthorization.d.ts +15 -0
  60. package/lib/typescript/commonjs/bridge/NativePowerAppsBridge.d.ts +6 -0
  61. package/lib/typescript/commonjs/bridge/setupNativeHost.d.ts +2 -0
  62. package/lib/typescript/commonjs/config/runtimeTenantMode.d.ts +3 -0
  63. package/lib/typescript/commonjs/config/runtimeTenantMode.web.d.ts +3 -0
  64. package/lib/typescript/commonjs/config/tenantMode.d.ts +14 -0
  65. package/lib/typescript/commonjs/connections/PowerPlatformConnectionApi.d.ts +5 -2
  66. package/lib/typescript/commonjs/connections/connectionMetadata.d.ts +1 -1
  67. package/lib/typescript/commonjs/connections/types.d.ts +7 -1
  68. package/lib/typescript/commonjs/connections/useConnectionRefs.d.ts +11 -2
  69. package/lib/typescript/commonjs/index.d.ts +7 -3
  70. package/lib/typescript/commonjs/platform/CanvasAppResolution.d.ts +4 -0
  71. package/lib/typescript/commonjs/platform/CanvasAppResolutionApi.d.ts +6 -0
  72. package/lib/typescript/commonjs/platform/DataverseGlobalDiscoveryApi.d.ts +12 -2
  73. package/lib/typescript/commonjs/platform/GlobalDiscovery.d.ts +12 -0
  74. package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +9 -1
  75. package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +14 -16
  76. package/lib/typescript/commonjs/target/DefaultPowerAppsExperience.d.ts +6 -0
  77. package/lib/typescript/commonjs/target/PowerAppsExperience.d.ts +61 -0
  78. package/lib/typescript/commonjs/target/PowerAppsTargetBootstrap.d.ts +15 -0
  79. package/lib/typescript/commonjs/target/errors.d.ts +13 -0
  80. package/lib/typescript/commonjs/target/types.d.ts +34 -0
  81. package/lib/typescript/commonjs/telemetry/index.d.ts +2 -2
  82. package/lib/typescript/module/auth/AuthContext.d.ts +11 -2
  83. package/lib/typescript/module/auth/targetAuthorization.d.ts +15 -0
  84. package/lib/typescript/module/bridge/NativePowerAppsBridge.d.ts +6 -0
  85. package/lib/typescript/module/bridge/setupNativeHost.d.ts +2 -0
  86. package/lib/typescript/module/config/runtimeTenantMode.d.ts +3 -0
  87. package/lib/typescript/module/config/runtimeTenantMode.web.d.ts +3 -0
  88. package/lib/typescript/module/config/tenantMode.d.ts +14 -0
  89. package/lib/typescript/module/connections/PowerPlatformConnectionApi.d.ts +5 -2
  90. package/lib/typescript/module/connections/connectionMetadata.d.ts +1 -1
  91. package/lib/typescript/module/connections/types.d.ts +7 -1
  92. package/lib/typescript/module/connections/useConnectionRefs.d.ts +11 -2
  93. package/lib/typescript/module/index.d.ts +7 -3
  94. package/lib/typescript/module/platform/CanvasAppResolution.d.ts +4 -0
  95. package/lib/typescript/module/platform/CanvasAppResolutionApi.d.ts +6 -0
  96. package/lib/typescript/module/platform/DataverseGlobalDiscoveryApi.d.ts +12 -2
  97. package/lib/typescript/module/platform/GlobalDiscovery.d.ts +12 -0
  98. package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +9 -1
  99. package/lib/typescript/module/provider/PowerAppsProvider.d.ts +14 -16
  100. package/lib/typescript/module/target/DefaultPowerAppsExperience.d.ts +6 -0
  101. package/lib/typescript/module/target/PowerAppsExperience.d.ts +61 -0
  102. package/lib/typescript/module/target/PowerAppsTargetBootstrap.d.ts +15 -0
  103. package/lib/typescript/module/target/errors.d.ts +13 -0
  104. package/lib/typescript/module/target/types.d.ts +34 -0
  105. package/lib/typescript/module/telemetry/index.d.ts +2 -2
  106. package/package.json +12 -5
  107. package/scripts/build-codegen-package.js +34 -3
  108. package/scripts/dev.js +405 -0
  109. package/scripts/upgrade-authoring.js +85 -28
  110. package/scripts/wrap.js +42 -5
  111. package/src/config/generated-config.d.ts +21 -0
@@ -0,0 +1,529 @@
1
+ "use strict";
2
+
3
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
4
+ import { ActivityIndicator, Pressable, Text, View } from 'react-native';
5
+ import * as SecureStore from 'expo-secure-store';
6
+ import { AuthProvider, useAuth } from '../auth/AuthContext';
7
+ import { fetchAccessibleEnvironments } from "../platform/DataverseGlobalDiscoveryApi.js";
8
+ import { areDataverseOriginAliases, canonicalizeDataverseOrigin } from "../platform/CanvasAppResolution.js";
9
+ import { resolveCanvasApp } from "../platform/CanvasAppResolutionApi.js";
10
+ import { globalDiscoveryScope } from "../platform/GlobalDiscovery.js";
11
+ import { connectionUserConsentScope, powerPlatformScope } from "../platform/PowerPlatformEnvironmentApi.js";
12
+ import { TargetRuntimeError } from "./errors.js";
13
+ import { isTenantAllowed, MULTI_TENANT_AUTHORITY } from "../config/tenantMode.js";
14
+ import { PowerAppsExperienceBoundary } from "./PowerAppsExperience.js";
15
+ import { DefaultPowerAppsExperience } from "./DefaultPowerAppsExperience.js";
16
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
+ const TARGET_STORAGE_KEY = 'microsoft.power-apps-native-host.selected-target.v1';
18
+ const EMPTY_ENVIRONMENT_STABILITY_MS = 500;
19
+ function LoadingScreen({
20
+ message
21
+ }) {
22
+ return /*#__PURE__*/_jsxs(View, {
23
+ style: {
24
+ flex: 1,
25
+ alignItems: 'center',
26
+ justifyContent: 'center',
27
+ gap: 12,
28
+ padding: 24
29
+ },
30
+ children: [/*#__PURE__*/_jsx(ActivityIndicator, {
31
+ size: "large"
32
+ }), /*#__PURE__*/_jsx(Text, {
33
+ style: {
34
+ color: '#605E5C'
35
+ },
36
+ children: message
37
+ })]
38
+ });
39
+ }
40
+ function toEnvironmentSummary(target) {
41
+ return {
42
+ id: target.id,
43
+ label: target.label
44
+ };
45
+ }
46
+ function toExperienceError(error) {
47
+ return {
48
+ code: error.code,
49
+ message: error.userMessage,
50
+ retryable: error.retryable
51
+ };
52
+ }
53
+ function TargetRuntime({
54
+ target,
55
+ generation,
56
+ msalConfig,
57
+ isCurrent,
58
+ children,
59
+ onBack,
60
+ experienceComponent
61
+ }) {
62
+ const auth = useAuth();
63
+ const [state, setState] = useState('authenticating');
64
+ const [resolvedTarget, setResolvedTarget] = useState();
65
+ const [error, setError] = useState();
66
+ const [retry, setRetry] = useState(0);
67
+ useEffect(() => {
68
+ if (!auth.isAuthReady || !auth.account) return;
69
+ let active = true;
70
+ setState('resolving');
71
+ setError(undefined);
72
+ void (async () => {
73
+ try {
74
+ const dataverseApiUrl = target.dataverseApiUrl;
75
+ const resolved = await resolveCanvasApp(target, dataverseApiUrl, auth.acquireToken, {
76
+ value: generation,
77
+ isCurrent
78
+ });
79
+ if (!active || !isCurrent(generation)) return;
80
+ setResolvedTarget(resolved);
81
+ setState('ready');
82
+ } catch (cause) {
83
+ if (!active || !isCurrent(generation)) return;
84
+ setError(cause instanceof TargetRuntimeError ? cause : new TargetRuntimeError('service-unavailable', 'discovery', true, 'The selected environment could not be loaded. Try again.', target.id, cause));
85
+ setState('error');
86
+ }
87
+ })();
88
+ return () => {
89
+ active = false;
90
+ };
91
+ }, [auth.account, auth.acquireToken, auth.isAuthReady, generation, isCurrent, retry, target]);
92
+ if (auth.isLoading || !auth.isAuthReady) {
93
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
94
+ experience: {
95
+ status: 'connecting',
96
+ environment: toEnvironmentSummary(target)
97
+ },
98
+ appDisplayName: target.appDisplayName,
99
+ component: experienceComponent,
100
+ fallback: /*#__PURE__*/_jsx(LoadingScreen, {
101
+ message: "Connecting to the selected environment..."
102
+ })
103
+ });
104
+ }
105
+ if (!auth.account) {
106
+ const fallback = /*#__PURE__*/_jsxs(View, {
107
+ style: {
108
+ flex: 1,
109
+ alignItems: 'center',
110
+ justifyContent: 'center',
111
+ gap: 16,
112
+ padding: 24
113
+ },
114
+ children: [/*#__PURE__*/_jsx(Text, {
115
+ children: auth.error?.message ?? 'Sign in to access the selected environment.'
116
+ }), /*#__PURE__*/_jsx(Pressable, {
117
+ accessibilityRole: "button",
118
+ onPress: () => {
119
+ void auth.signIn();
120
+ },
121
+ children: /*#__PURE__*/_jsx(Text, {
122
+ style: {
123
+ color: '#0F6CBD',
124
+ fontWeight: '600'
125
+ },
126
+ children: "Sign in"
127
+ })
128
+ }), /*#__PURE__*/_jsx(Pressable, {
129
+ accessibilityRole: "button",
130
+ onPress: onBack,
131
+ children: /*#__PURE__*/_jsx(Text, {
132
+ children: "Choose another environment"
133
+ })
134
+ })]
135
+ });
136
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
137
+ experience: {
138
+ status: 'signIn',
139
+ error: auth.error?.message,
140
+ signIn: auth.signIn,
141
+ goBack: onBack
142
+ },
143
+ appDisplayName: target.appDisplayName,
144
+ component: experienceComponent,
145
+ fallback: fallback
146
+ });
147
+ }
148
+ if (state === 'error' && error) {
149
+ const retryResolution = () => setRetry(value => value + 1);
150
+ const fallback = /*#__PURE__*/_jsxs(View, {
151
+ style: {
152
+ flex: 1,
153
+ alignItems: 'center',
154
+ justifyContent: 'center',
155
+ gap: 16,
156
+ padding: 24
157
+ },
158
+ children: [/*#__PURE__*/_jsx(Text, {
159
+ style: {
160
+ fontWeight: '700'
161
+ },
162
+ children: "Unable to open environment"
163
+ }), /*#__PURE__*/_jsx(Text, {
164
+ children: error.userMessage
165
+ }), error.retryable ? /*#__PURE__*/_jsx(Pressable, {
166
+ accessibilityRole: "button",
167
+ onPress: retryResolution,
168
+ children: /*#__PURE__*/_jsx(Text, {
169
+ style: {
170
+ color: '#0F6CBD',
171
+ fontWeight: '600'
172
+ },
173
+ children: "Try again"
174
+ })
175
+ }) : null, /*#__PURE__*/_jsx(Pressable, {
176
+ accessibilityRole: "button",
177
+ onPress: onBack,
178
+ children: /*#__PURE__*/_jsx(Text, {
179
+ style: {
180
+ color: '#0F6CBD'
181
+ },
182
+ children: "Choose another environment"
183
+ })
184
+ })]
185
+ });
186
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
187
+ experience: {
188
+ status: 'error',
189
+ error: toExperienceError(error),
190
+ retry: error.retryable ? retryResolution : undefined,
191
+ goBack: onBack
192
+ },
193
+ appDisplayName: target.appDisplayName,
194
+ component: experienceComponent,
195
+ fallback: fallback
196
+ });
197
+ }
198
+ if (!resolvedTarget) {
199
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
200
+ experience: {
201
+ status: 'resolvingApp',
202
+ environment: toEnvironmentSummary(target)
203
+ },
204
+ appDisplayName: target.appDisplayName,
205
+ component: experienceComponent,
206
+ fallback: /*#__PURE__*/_jsx(LoadingScreen, {
207
+ message: "Loading app details..."
208
+ })
209
+ });
210
+ }
211
+ const broker = {
212
+ tenantId: target.tenantId,
213
+ generation,
214
+ acquireGlobalDiscoveryToken: async () => requireToken(await auth.acquireToken([globalDiscoveryScope(target.region)])),
215
+ acquireDataverseToken: async apiUrl => {
216
+ if (!areDataverseOriginAliases(apiUrl, resolvedTarget.dataverseApiUrl)) {
217
+ throw new Error('Dataverse token audience does not match the selected environment');
218
+ }
219
+ return requireToken(await auth.acquireToken([`${canonicalizeDataverseOrigin(apiUrl)}/.default`]));
220
+ },
221
+ acquirePowerPlatformToken: async region => {
222
+ if (region.toLowerCase() !== target.region.toLowerCase()) throw new Error('Power Platform region does not match the selected environment');
223
+ return requireToken(await auth.acquireToken([powerPlatformScope(target.region)]));
224
+ },
225
+ acquireConnectionUserConsentToken: async region => {
226
+ if (region.toLowerCase() !== target.region.toLowerCase()) throw new Error('Power Platform region does not match the selected environment');
227
+ return requireToken(await auth.acquireToken([connectionUserConsentScope(target.region)]));
228
+ },
229
+ acquireApiHubToken: async () => requireToken(await auth.acquireToken(['https://apihub.azure.com/.default']))
230
+ };
231
+ const runtime = children(resolvedTarget, {
232
+ clientId: msalConfig.clientId,
233
+ accountIdentifier: auth.account.identifier,
234
+ accountTenantId: auth.account.tenantId,
235
+ generation,
236
+ tokenBroker: broker,
237
+ signOut: auth.signOut
238
+ });
239
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
240
+ experience: {
241
+ status: 'ready',
242
+ environment: toEnvironmentSummary(resolvedTarget)
243
+ },
244
+ appDisplayName: target.appDisplayName,
245
+ component: experienceComponent,
246
+ fallback: runtime,
247
+ children: runtime
248
+ });
249
+ }
250
+ function requireToken(token) {
251
+ if (!token) throw new Error('Authentication token is unavailable');
252
+ return token;
253
+ }
254
+ function EnvironmentSelector({
255
+ appDisplayName,
256
+ appName,
257
+ tenantPolicy,
258
+ onSelect,
259
+ experienceComponent
260
+ }) {
261
+ const auth = useAuth();
262
+ const [environments, setEnvironments] = useState();
263
+ const [emptyResultReady, setEmptyResultReady] = useState(false);
264
+ const [error, setError] = useState();
265
+ const [retry, setRetry] = useState(0);
266
+ useEffect(() => {
267
+ if (!auth.account) return;
268
+ const accountIdentifier = auth.account.identifier;
269
+ let active = true;
270
+ let emptyResultTimer;
271
+ setEnvironments(undefined);
272
+ setEmptyResultReady(false);
273
+ setError(undefined);
274
+ if (!isTenantAllowed(auth.account.tenantId, tenantPolicy)) {
275
+ setError(new TargetRuntimeError('tenant-mismatch', 'authentication', false, 'Your organization is not approved to use this app.'));
276
+ return () => {
277
+ active = false;
278
+ };
279
+ }
280
+ void fetchAccessibleEnvironments(auth.account.tenantId, appDisplayName, appName, auth.acquireToken).then(async discovered => {
281
+ if (!active) return;
282
+ const storedId = await SecureStore.getItemAsync(TARGET_STORAGE_KEY).catch(() => null);
283
+ if (!active) return;
284
+ const restored = discovered.find(environment => environment.id === storedId);
285
+ if (restored) onSelect(restored, accountIdentifier);else {
286
+ if (storedId) void SecureStore.deleteItemAsync(TARGET_STORAGE_KEY);
287
+ setEnvironments(discovered);
288
+ if (discovered.length === 0) {
289
+ emptyResultTimer = setTimeout(() => {
290
+ if (active) setEmptyResultReady(true);
291
+ }, EMPTY_ENVIRONMENT_STABILITY_MS);
292
+ }
293
+ }
294
+ }).catch(cause => {
295
+ if (!active) return;
296
+ setError(cause instanceof TargetRuntimeError ? cause : new TargetRuntimeError('discovery-failed', 'discovery', true, 'Your environments could not be loaded. Try again.', undefined, cause));
297
+ });
298
+ return () => {
299
+ active = false;
300
+ if (emptyResultTimer) clearTimeout(emptyResultTimer);
301
+ };
302
+ }, [appDisplayName, appName, auth.account, auth.acquireToken, onSelect, retry, tenantPolicy]);
303
+ if (auth.isLoading || !auth.isAuthReady) {
304
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
305
+ experience: {
306
+ status: 'preparing'
307
+ },
308
+ appDisplayName: appDisplayName,
309
+ component: experienceComponent,
310
+ fallback: /*#__PURE__*/_jsx(LoadingScreen, {
311
+ message: "Preparing your environments..."
312
+ })
313
+ });
314
+ }
315
+ if (!auth.account) {
316
+ const fallback = /*#__PURE__*/_jsxs(View, {
317
+ style: {
318
+ flex: 1,
319
+ alignItems: 'center',
320
+ justifyContent: 'center',
321
+ gap: 16,
322
+ padding: 24
323
+ },
324
+ children: [/*#__PURE__*/_jsx(Text, {
325
+ children: auth.error?.message ?? 'Sign in with your work or school account.'
326
+ }), /*#__PURE__*/_jsx(Pressable, {
327
+ accessibilityRole: "button",
328
+ onPress: () => {
329
+ void auth.signIn();
330
+ },
331
+ children: /*#__PURE__*/_jsx(Text, {
332
+ style: {
333
+ color: '#0F6CBD',
334
+ fontWeight: '600'
335
+ },
336
+ children: "Sign in"
337
+ })
338
+ })]
339
+ });
340
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
341
+ experience: {
342
+ status: 'signIn',
343
+ error: auth.error?.message,
344
+ signIn: auth.signIn
345
+ },
346
+ appDisplayName: appDisplayName,
347
+ component: experienceComponent,
348
+ fallback: fallback
349
+ });
350
+ }
351
+ if (error) {
352
+ const retryDiscovery = () => setRetry(value => value + 1);
353
+ const fallback = /*#__PURE__*/_jsxs(View, {
354
+ style: {
355
+ flex: 1,
356
+ alignItems: 'center',
357
+ justifyContent: 'center',
358
+ gap: 16,
359
+ padding: 24
360
+ },
361
+ children: [/*#__PURE__*/_jsx(Text, {
362
+ style: {
363
+ fontWeight: '700'
364
+ },
365
+ children: "Unable to load environments"
366
+ }), /*#__PURE__*/_jsx(Text, {
367
+ children: error.userMessage
368
+ }), error.retryable ? /*#__PURE__*/_jsx(Pressable, {
369
+ accessibilityRole: "button",
370
+ onPress: retryDiscovery,
371
+ children: /*#__PURE__*/_jsx(Text, {
372
+ style: {
373
+ color: '#0F6CBD'
374
+ },
375
+ children: "Try again"
376
+ })
377
+ }) : null, /*#__PURE__*/_jsx(Pressable, {
378
+ accessibilityRole: "button",
379
+ onPress: () => {
380
+ void auth.signOut();
381
+ },
382
+ children: /*#__PURE__*/_jsx(Text, {
383
+ children: "Sign in with another account"
384
+ })
385
+ })]
386
+ });
387
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
388
+ experience: {
389
+ status: 'error',
390
+ error: toExperienceError(error),
391
+ retry: error.retryable ? retryDiscovery : undefined,
392
+ signOut: auth.signOut
393
+ },
394
+ appDisplayName: appDisplayName,
395
+ component: experienceComponent,
396
+ fallback: fallback
397
+ });
398
+ }
399
+ if (!environments || environments.length === 0 && !emptyResultReady) {
400
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
401
+ experience: {
402
+ status: 'discovering'
403
+ },
404
+ appDisplayName: appDisplayName,
405
+ component: experienceComponent,
406
+ fallback: /*#__PURE__*/_jsx(LoadingScreen, {
407
+ message: "Finding your environments..."
408
+ })
409
+ });
410
+ }
411
+ const fallback = /*#__PURE__*/_jsxs(View, {
412
+ style: {
413
+ flex: 1,
414
+ justifyContent: 'center',
415
+ gap: 12,
416
+ padding: 24
417
+ },
418
+ children: [/*#__PURE__*/_jsx(Text, {
419
+ style: {
420
+ fontSize: 20,
421
+ fontWeight: '700'
422
+ },
423
+ children: "Choose an environment"
424
+ }), environments.length === 0 ? /*#__PURE__*/_jsx(Text, {
425
+ children: "No accessible Dataverse environments were found."
426
+ }) : environments.map(target => /*#__PURE__*/_jsx(Pressable, {
427
+ accessibilityRole: "button",
428
+ onPress: () => onSelect(target, auth.account.identifier),
429
+ style: {
430
+ paddingVertical: 12
431
+ },
432
+ children: /*#__PURE__*/_jsx(Text, {
433
+ style: {
434
+ color: '#0F6CBD',
435
+ fontWeight: '600'
436
+ },
437
+ children: target.label
438
+ })
439
+ }, target.id)), /*#__PURE__*/_jsx(Pressable, {
440
+ accessibilityRole: "button",
441
+ onPress: () => {
442
+ void auth.signOut();
443
+ },
444
+ children: /*#__PURE__*/_jsx(Text, {
445
+ children: "Sign out"
446
+ })
447
+ })]
448
+ });
449
+ if (environments.length === 0) {
450
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
451
+ experience: {
452
+ status: 'empty',
453
+ retry: () => setRetry(value => value + 1),
454
+ signOut: auth.signOut
455
+ },
456
+ appDisplayName: appDisplayName,
457
+ component: experienceComponent,
458
+ fallback: fallback
459
+ });
460
+ }
461
+ const selectEnvironment = environmentId => {
462
+ const target = environments.find(environment => environment.id === environmentId);
463
+ if (target) onSelect(target, auth.account.identifier);
464
+ };
465
+ return /*#__PURE__*/_jsx(PowerAppsExperienceBoundary, {
466
+ experience: {
467
+ status: 'selectEnvironment',
468
+ environments: environments.map(toEnvironmentSummary),
469
+ selectEnvironment,
470
+ signOut: auth.signOut
471
+ },
472
+ appDisplayName: appDisplayName,
473
+ component: experienceComponent,
474
+ fallback: fallback
475
+ });
476
+ }
477
+ export function PowerAppsTargetBootstrap({
478
+ msalConfig,
479
+ appDisplayName,
480
+ appName,
481
+ tenantPolicy,
482
+ experienceComponent,
483
+ children
484
+ }) {
485
+ const ExperienceComponent = experienceComponent ?? DefaultPowerAppsExperience;
486
+ const [selected, setSelected] = useState();
487
+ const [bootstrapAccountIdentifier, setBootstrapAccountIdentifier] = useState();
488
+ const generationRef = useRef(0);
489
+ const [generation, setGeneration] = useState(0);
490
+ const isCurrentGeneration = useCallback(value => generationRef.current === value, []);
491
+ const select = useCallback((target, accountIdentifier) => {
492
+ generationRef.current += 1;
493
+ setGeneration(generationRef.current);
494
+ setSelected(target);
495
+ setBootstrapAccountIdentifier(accountIdentifier);
496
+ if (target) void SecureStore.setItemAsync(TARGET_STORAGE_KEY, target.id);else void SecureStore.deleteItemAsync(TARGET_STORAGE_KEY);
497
+ }, []);
498
+ if (!selected) {
499
+ return /*#__PURE__*/_jsx(AuthProvider, {
500
+ msalConfig: msalConfig,
501
+ authorityTenant: MULTI_TENANT_AUTHORITY,
502
+ requiredScopes: [globalDiscoveryScope('prod')],
503
+ children: /*#__PURE__*/_jsx(EnvironmentSelector, {
504
+ appDisplayName: appDisplayName,
505
+ appName: appName,
506
+ tenantPolicy: tenantPolicy,
507
+ onSelect: select,
508
+ experienceComponent: ExperienceComponent
509
+ })
510
+ }, `bootstrap:${generation}`);
511
+ }
512
+ return /*#__PURE__*/_jsx(AuthProvider, {
513
+ msalConfig: msalConfig,
514
+ authorityTenant: selected.tenantId,
515
+ expectedTenantId: selected.tenantId,
516
+ preferredAccountIdentifier: bootstrapAccountIdentifier,
517
+ autoAuthorizeMissingScopes: true,
518
+ requiredScopes: [`${selected.dataverseApiUrl}/user_impersonation`, powerPlatformScope(selected.region), 'https://apihub.azure.com/.default'],
519
+ children: /*#__PURE__*/_jsx(TargetRuntime, {
520
+ target: selected,
521
+ generation: generation,
522
+ msalConfig: msalConfig,
523
+ isCurrent: isCurrentGeneration,
524
+ onBack: () => select(undefined),
525
+ experienceComponent: ExperienceComponent,
526
+ children: children
527
+ })
528
+ }, `${selected.id}:${generation}`);
529
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ export class TargetRuntimeError extends Error {
4
+ name = 'TargetRuntimeError';
5
+ constructor(code, stage, retryable, userMessage, targetId, cause) {
6
+ super(userMessage);
7
+ this.code = code;
8
+ this.stage = stage;
9
+ this.retryable = retryable;
10
+ this.userMessage = userMessage;
11
+ this.targetId = targetId;
12
+ Object.defineProperty(this, 'cause', {
13
+ value: cause,
14
+ enumerable: false,
15
+ configurable: false,
16
+ writable: false
17
+ });
18
+ }
19
+ }
@@ -0,0 +1 @@
1
+ "use strict";
@@ -12,7 +12,7 @@
12
12
 
13
13
  import { HttpClient } from '@microsoft/power-apps-native-common/http';
14
14
  import { isDebugLoggingEnabled } from "../logger.js";
15
- export { getAppLogger, configureAppLogger, getCustomerTelemetryLogger } from '@microsoft/power-apps-native-common';
15
+ export { getAppLogger, configureAppLogger, getCustomEventsLogger } from '@microsoft/power-apps-native-common';
16
16
  /**
17
17
  * Canonical operation names for host package telemetry.
18
18
  * Follow the `PowerAppsHost.<Subsystem>.<Action>` convention so events are
@@ -14,7 +14,7 @@
14
14
  * </AuthProvider>
15
15
  */
16
16
  import React from 'react';
17
- import type { MsalAuthConfig, IntuneConfig } from '@microsoft/power-apps-native-auth';
17
+ import type { MSALAccount, MsalAuthConfig, IntuneConfig } from '@microsoft/power-apps-native-auth';
18
18
  export interface AuthState {
19
19
  isLoading: boolean;
20
20
  isAuthReady: boolean;
@@ -28,6 +28,7 @@ export interface AuthState {
28
28
  * in a perpetual "waiting-for-auth" retry loop.
29
29
  */
30
30
  hasRealAccount: boolean;
31
+ account: MSALAccount | null;
31
32
  error: Error | null;
32
33
  acquireToken: (scopes: string[]) => Promise<string | null>;
33
34
  signIn: () => Promise<void>;
@@ -48,9 +49,17 @@ export interface AuthProviderProps {
48
49
  intuneConfig?: IntuneConfig;
49
50
  /** Token audiences that must be available before the account is considered signed in. */
50
51
  requiredScopes?: string[];
52
+ /** Tenant that every cached and newly acquired account must belong to. */
53
+ expectedTenantId?: string;
54
+ /** Authority segment used to initialize MSAL, such as `organizations` or a tenant GUID. */
55
+ authorityTenant?: string;
56
+ /** Cached account to restore when moving between authority-specific providers. */
57
+ preferredAccountIdentifier?: string;
58
+ /** Automatically request missing consent for the preferred cached account. */
59
+ autoAuthorizeMissingScopes?: boolean;
51
60
  children: React.ReactNode;
52
61
  }
53
- export declare function AuthProvider({ msalConfig, intuneConfig, requiredScopes, children, }: AuthProviderProps): React.JSX.Element;
62
+ export declare function AuthProvider({ msalConfig, intuneConfig, requiredScopes, expectedTenantId, authorityTenant, preferredAccountIdentifier, autoAuthorizeMissingScopes, children, }: AuthProviderProps): React.JSX.Element;
54
63
  /**
55
64
  * Returns the current auth state from the nearest <AuthProvider>.
56
65
  * Must be called within the AuthProvider tree.
@@ -0,0 +1,15 @@
1
+ import type { MSALAccount, MSALResult } from '@microsoft/power-apps-native-auth';
2
+ interface TargetAuthorizationClient {
3
+ acquireToken(params: {
4
+ scopes: string[];
5
+ loginHint?: string;
6
+ }): Promise<MSALResult>;
7
+ acquireTokenSilent(params: {
8
+ scopes: string[];
9
+ account: MSALAccount;
10
+ }): Promise<MSALResult>;
11
+ }
12
+ export declare function isInteractionRequired(error: unknown): boolean;
13
+ export declare function authorizeRequiredScopes(client: TargetAuthorizationClient, account: MSALAccount, scopes: string[], expectedTenantId?: string): Promise<MSALAccount>;
14
+ export {};
15
+ //# sourceMappingURL=targetAuthorization.d.ts.map
@@ -53,6 +53,10 @@ export declare function buildConnectionReferences(powerConfigConnectionReference
53
53
  [key: string]: unknown;
54
54
  }>): Record<string, AppConnectionReference>;
55
55
  export type NativeBridgeOptions = {
56
+ /** Active target generation. Changes invalidate in-flight bridge requests. */
57
+ targetGeneration?: number;
58
+ /** Target and account identity represented by the current bridge options. */
59
+ runtimeIdentity?: string;
56
60
  /**
57
61
  * Return an auth token scoped to a connector API endpoint.
58
62
  * @param apiId - Connection API identifier (e.g. "shared_sharepointonline").
@@ -76,6 +80,7 @@ export type NativeBridgeOptions = {
76
80
  };
77
81
  export declare class NativePowerAppsBridge {
78
82
  private _options;
83
+ private _runtimeEpoch;
79
84
  constructor(_options: NativeBridgeOptions);
80
85
  /**
81
86
  * Updates the bridge's options in-place.
@@ -86,6 +91,7 @@ export declare class NativePowerAppsBridge {
86
91
  * via globalThis would have no effect — mutating options is the correct path.
87
92
  */
88
93
  updateOptions(options: NativeBridgeOptions): void;
94
+ invalidateRuntime(): void;
89
95
  initialize(): Promise<void>;
90
96
  /**
91
97
  * Hermes-safe body construction. Returns the raw body value so that
@@ -36,4 +36,6 @@ import type { NativeBridgeOptions } from './NativePowerAppsBridge';
36
36
  * ```
37
37
  */
38
38
  export declare function setupNativeHost(options: NativeBridgeOptions): void;
39
+ /** Invalidates in-flight work without replacing the SDK-cached bridge singleton. */
40
+ export declare function invalidateNativeHostRuntime(): void;
39
41
  //# sourceMappingURL=setupNativeHost.d.ts.map
@@ -0,0 +1,3 @@
1
+ import type { PowerAppsTenantMode } from './tenantMode';
2
+ export declare function assertNativeTenantMode(tenantMode: PowerAppsTenantMode): void;
3
+ //# sourceMappingURL=runtimeTenantMode.d.ts.map
@@ -0,0 +1,3 @@
1
+ import type { PowerAppsTenantMode } from './tenantMode';
2
+ export declare function assertNativeTenantMode(_tenantMode: PowerAppsTenantMode): void;
3
+ //# sourceMappingURL=runtimeTenantMode.web.d.ts.map
@@ -0,0 +1,14 @@
1
+ export type PowerAppsTenantMode = 'single' | 'multi';
2
+ export type PowerAppsTenantPolicy = 'organizations' | {
3
+ allowedTenantIds: string[];
4
+ };
5
+ export declare const MULTI_TENANT_AUTHORITY = "common";
6
+ export type PowerAppsMultiTenantConfig = {
7
+ appDisplayName: string;
8
+ appName: string;
9
+ tenantPolicy: PowerAppsTenantPolicy;
10
+ };
11
+ export declare function resolveTenantMode(appConfig: unknown): PowerAppsTenantMode;
12
+ export declare function resolveMultiTenantConfig(appConfig: unknown): PowerAppsMultiTenantConfig;
13
+ export declare function isTenantAllowed(tenantId: string, policy: PowerAppsTenantPolicy): boolean;
14
+ //# sourceMappingURL=tenantMode.d.ts.map