@microsoft/power-apps-native-host 0.2.25 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +21 -2
  2. package/compatibility/current.json +0 -1
  3. package/lib/NOTICE.txt +6 -12
  4. package/lib/commonjs/auth/AuthContext.js +74 -18
  5. package/lib/commonjs/bridge/NativePowerAppsBridge.js +11 -18
  6. package/lib/commonjs/bridge/PowerPagesBridge.js +27 -8
  7. package/lib/commonjs/bridge/bridgeState.js +40 -0
  8. package/lib/commonjs/connections/ConnectionSetupScreen.js +459 -80
  9. package/lib/commonjs/connections/PowerPlatformConnectionApi.js +946 -0
  10. package/lib/commonjs/connections/connectionMetadata.js +127 -0
  11. package/lib/commonjs/connections/connectionState.js +96 -0
  12. package/lib/commonjs/connections/disambiguate.js +19 -11
  13. package/lib/commonjs/connections/resolveConnections.js +347 -0
  14. package/lib/commonjs/connections/types.js +106 -0
  15. package/lib/commonjs/{platform → connections}/useConnectionRefs.js +128 -65
  16. package/lib/commonjs/connections/useConnectionSetup.js +111 -52
  17. package/lib/commonjs/controls/FilePicker.js +6 -0
  18. package/lib/commonjs/controls/ImagePicker.js +8 -0
  19. package/lib/commonjs/index.js +166 -4
  20. package/lib/commonjs/logger.js +9 -6
  21. package/lib/commonjs/offline/createOfflineHostActivationCallbacks.js +61 -0
  22. package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.js +46 -0
  23. package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.web.js +13 -0
  24. package/lib/commonjs/platform/DataverseDataSources.js +14 -0
  25. package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +49 -0
  26. package/lib/commonjs/platform/GlobalDiscovery.js +51 -0
  27. package/lib/commonjs/platform/PowerPlatformEnvironmentApi.js +6 -106
  28. package/lib/commonjs/provider/PowerAppsHostProvider.js +156 -25
  29. package/lib/commonjs/provider/PowerAppsProvider.js +173 -134
  30. package/lib/commonjs/provider/SessionDetailsGesture.js +216 -0
  31. package/lib/commonjs/telemetry/OfflineSdkTelemetry.js +95 -0
  32. package/lib/commonjs/telemetry/OfflineSdkTelemetry.web.js +9 -0
  33. package/lib/commonjs/telemetry/index.js +22 -3
  34. package/lib/module/auth/AuthContext.js +74 -18
  35. package/lib/module/bridge/NativePowerAppsBridge.js +12 -19
  36. package/lib/module/bridge/PowerPagesBridge.js +27 -8
  37. package/lib/module/bridge/bridgeState.js +34 -0
  38. package/lib/module/connections/ConnectionSetupScreen.js +460 -81
  39. package/lib/module/connections/PowerPlatformConnectionApi.js +932 -0
  40. package/lib/module/connections/connectionMetadata.js +112 -0
  41. package/lib/module/connections/connectionState.js +82 -0
  42. package/lib/module/connections/disambiguate.js +19 -12
  43. package/lib/module/connections/resolveConnections.js +342 -0
  44. package/lib/module/connections/types.js +91 -0
  45. package/lib/module/{platform → connections}/useConnectionRefs.js +129 -66
  46. package/lib/module/connections/useConnectionSetup.js +113 -54
  47. package/lib/module/controls/FilePicker.js +6 -0
  48. package/lib/module/controls/ImagePicker.js +8 -0
  49. package/lib/module/index.js +10 -4
  50. package/lib/module/logger.js +8 -6
  51. package/lib/module/offline/createOfflineHostActivationCallbacks.js +55 -0
  52. package/lib/module/offline/useOfflineIntegrationWithStartupGate.js +42 -0
  53. package/lib/module/offline/useOfflineIntegrationWithStartupGate.web.js +9 -0
  54. package/lib/module/platform/DataverseDataSources.js +10 -0
  55. package/lib/module/platform/DataverseGlobalDiscoveryApi.js +45 -0
  56. package/lib/module/platform/GlobalDiscovery.js +45 -0
  57. package/lib/module/platform/PowerPlatformEnvironmentApi.js +5 -104
  58. package/lib/module/provider/PowerAppsHostProvider.js +159 -28
  59. package/lib/module/provider/PowerAppsProvider.js +177 -138
  60. package/lib/module/provider/SessionDetailsGesture.js +211 -0
  61. package/lib/module/telemetry/OfflineSdkTelemetry.js +91 -0
  62. package/lib/module/telemetry/OfflineSdkTelemetry.web.js +5 -0
  63. package/lib/module/telemetry/index.js +15 -4
  64. package/lib/typescript/commonjs/auth/AuthContext.d.ts +3 -1
  65. package/lib/typescript/commonjs/auth/AuthContext.web.d.ts +1 -0
  66. package/lib/typescript/commonjs/bridge/bridgeState.d.ts +5 -0
  67. package/lib/typescript/commonjs/connections/ConnectionSetupScreen.d.ts +28 -7
  68. package/lib/typescript/commonjs/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
  69. package/lib/typescript/commonjs/connections/connectionMetadata.d.ts +66 -0
  70. package/lib/typescript/commonjs/connections/connectionState.d.ts +16 -0
  71. package/lib/typescript/commonjs/connections/disambiguate.d.ts +4 -2
  72. package/lib/typescript/commonjs/{platform → connections}/resolveConnections.d.ts +19 -19
  73. package/lib/typescript/commonjs/connections/types.d.ts +129 -1
  74. package/lib/typescript/commonjs/{platform → connections}/useConnectionRefs.d.ts +33 -24
  75. package/lib/typescript/commonjs/connections/useConnectionSetup.d.ts +12 -8
  76. package/lib/typescript/commonjs/controls/FilePicker.d.ts +7 -1
  77. package/lib/typescript/commonjs/controls/ImagePicker.d.ts +9 -1
  78. package/lib/typescript/commonjs/index.d.ts +17 -10
  79. package/lib/typescript/commonjs/logger.d.ts +4 -4
  80. package/lib/typescript/commonjs/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
  81. package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
  82. package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
  83. package/lib/typescript/commonjs/platform/DataverseDataSources.d.ts +27 -0
  84. package/lib/typescript/commonjs/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
  85. package/lib/typescript/commonjs/platform/GlobalDiscovery.d.ts +4 -0
  86. package/lib/typescript/commonjs/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
  87. package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +29 -18
  88. package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +30 -8
  89. package/lib/typescript/commonjs/provider/SessionDetailsGesture.d.ts +13 -0
  90. package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.d.ts +15 -0
  91. package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
  92. package/lib/typescript/commonjs/telemetry/index.d.ts +13 -1
  93. package/lib/typescript/module/auth/AuthContext.d.ts +3 -1
  94. package/lib/typescript/module/auth/AuthContext.web.d.ts +1 -0
  95. package/lib/typescript/module/bridge/bridgeState.d.ts +5 -0
  96. package/lib/typescript/module/connections/ConnectionSetupScreen.d.ts +28 -7
  97. package/lib/typescript/module/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
  98. package/lib/typescript/module/connections/connectionMetadata.d.ts +66 -0
  99. package/lib/typescript/module/connections/connectionState.d.ts +16 -0
  100. package/lib/typescript/module/connections/disambiguate.d.ts +4 -2
  101. package/lib/typescript/module/{platform → connections}/resolveConnections.d.ts +19 -19
  102. package/lib/typescript/module/connections/types.d.ts +129 -1
  103. package/lib/typescript/module/{platform → connections}/useConnectionRefs.d.ts +33 -24
  104. package/lib/typescript/module/connections/useConnectionSetup.d.ts +12 -8
  105. package/lib/typescript/module/controls/FilePicker.d.ts +7 -1
  106. package/lib/typescript/module/controls/ImagePicker.d.ts +9 -1
  107. package/lib/typescript/module/index.d.ts +17 -10
  108. package/lib/typescript/module/logger.d.ts +4 -4
  109. package/lib/typescript/module/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
  110. package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
  111. package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
  112. package/lib/typescript/module/platform/DataverseDataSources.d.ts +27 -0
  113. package/lib/typescript/module/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
  114. package/lib/typescript/module/platform/GlobalDiscovery.d.ts +4 -0
  115. package/lib/typescript/module/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
  116. package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +29 -18
  117. package/lib/typescript/module/provider/PowerAppsProvider.d.ts +30 -8
  118. package/lib/typescript/module/provider/SessionDetailsGesture.d.ts +13 -0
  119. package/lib/typescript/module/telemetry/OfflineSdkTelemetry.d.ts +15 -0
  120. package/lib/typescript/module/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
  121. package/lib/typescript/module/telemetry/index.d.ts +13 -1
  122. package/package.json +11 -6
  123. package/scripts/metro-logger.js +213 -0
  124. package/scripts/upgrade/runtime.js +13 -7
  125. package/scripts/upgrade-authoring.js +5 -6
  126. package/lib/commonjs/platform/PowerPlatformConnectionApi.js +0 -606
  127. package/lib/commonjs/platform/resolveConnections.js +0 -264
  128. package/lib/module/platform/PowerPlatformConnectionApi.js +0 -595
  129. package/lib/module/platform/resolveConnections.js +0 -259
@@ -4,6 +4,16 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.ConnectionStatus = void 0;
7
+ exports.buildConnectionRequirements = buildConnectionRequirements;
8
+ exports.buildConnectionResolutionKey = buildConnectionResolutionKey;
9
+ exports.findConnectionDataSourceConflicts = findConnectionDataSourceConflicts;
10
+ exports.getConnectionDatasetName = getConnectionDatasetName;
11
+ exports.getConnectorId = getConnectorId;
12
+ exports.getFlowConnectionName = getFlowConnectionName;
13
+ exports.getFlowDataSourceNames = getFlowDataSourceNames;
14
+ exports.hasCompleteStaticConnectionConfig = hasCompleteStaticConnectionConfig;
15
+ exports.isCompositeConnectionApiId = isCompositeConnectionApiId;
16
+ exports.normalizeApiId = normalizeApiId;
7
17
  /*!
8
18
  * power-apps-native-host — connection disambiguation types
9
19
  *
@@ -24,7 +34,100 @@ let ConnectionStatus = exports.ConnectionStatus = /*#__PURE__*/function (Connect
24
34
  ConnectionStatus["NotConnected"] = "NotConnected";
25
35
  return ConnectionStatus;
26
36
  }({});
37
+ function normalizeApiId(apiId) {
38
+ const connectorId = apiId.split('/').filter(Boolean).pop() ?? apiId;
39
+ return `/providers/Microsoft.PowerApps/apis/${connectorId.toLowerCase()}`;
40
+ }
41
+ function getConnectorId(apiId) {
42
+ return (apiId.split('/').filter(Boolean).pop() ?? apiId).toLowerCase();
43
+ }
44
+ function getConnectionDatasetName(reference) {
45
+ const datasetNames = Object.keys(reference.dataSets ?? {});
46
+ return datasetNames.length === 1 ? datasetNames[0] : '';
47
+ }
48
+ function getFlowConnectionName(reference) {
49
+ const workflowNameHint = reference.parameterHintsV2?.workflowName?.value;
50
+ const workflowEntityIdHint = reference.parameterHintsV2?.workflowEntityId?.value;
51
+ return reference.workflowDetails?.workflowName ?? (typeof workflowNameHint === 'string' ? workflowNameHint : undefined) ?? reference.workflowDetails?.workflowEntityId ?? (typeof workflowEntityIdHint === 'string' ? workflowEntityIdHint : '');
52
+ }
53
+ function getFlowDataSourceNames(reference) {
54
+ const names = new Set(reference.dataSources);
55
+ const workflowDisplayNameHint = reference.parameterHintsV2?.workflowDisplayName?.value;
56
+ const displayName = reference.workflowDetails?.workflowDisplayName ?? (typeof workflowDisplayNameHint === 'string' ? workflowDisplayNameHint : undefined);
57
+ if (displayName) names.add(`${displayName.charAt(0).toLowerCase()}${displayName.slice(1)}`);
58
+ return [...names];
59
+ }
60
+ function buildConnectionRequirements(connectionReferences, scopesByApiId = {}) {
61
+ return Object.entries(connectionReferences).filter(([, reference]) => !isCompositeConnectionApiId(reference.id)).map(([localRefId, reference]) => {
62
+ const appActions = new Set(reference.appActions ?? []);
63
+ let isFlowDependent = false;
64
+ for (const dependentId of reference.dependents ?? []) {
65
+ const dependent = connectionReferences[dependentId];
66
+ if (!dependent || getConnectorId(dependent.id) !== 'shared_logicflows') continue;
67
+ isFlowDependent = true;
68
+ for (const action of dependent.nestedActions ?? []) {
69
+ const hint = action.flowConnectionRef ? dependent.parameterHintsV2?.[action.flowConnectionRef] : undefined;
70
+ if (hint?.value === localRefId && action.actionName) appActions.add(action.actionName);
71
+ }
72
+ }
73
+ const apiId = normalizeApiId(reference.id);
74
+ return {
75
+ localRefId,
76
+ apiId,
77
+ connectorId: getConnectorId(reference.id),
78
+ dataSources: reference.dataSources,
79
+ displayName: reference.displayName,
80
+ iconUri: reference.iconUri,
81
+ appActions: [...appActions],
82
+ sharedConnectionId: reference.sharedConnectionId,
83
+ gatewayObjectIdHint: reference.gatewayObjectIdHint,
84
+ authenticationType: reference.authenticationType,
85
+ parameterSetName: reference.parameterSetName,
86
+ apiScopes: scopesByApiId[apiId] ?? {
87
+ will: [],
88
+ wont: []
89
+ },
90
+ isFlowDependent
91
+ };
92
+ });
93
+ }
94
+ function isCompositeConnectionApiId(apiId) {
95
+ const connectorId = getConnectorId(apiId);
96
+ return connectorId === 'shared_excel' || connectorId === 'shared_logicflows';
97
+ }
98
+ function findConnectionDataSourceConflicts(requirements) {
99
+ const owners = new Map();
100
+ const conflicts = new Set();
101
+ for (const requirement of requirements) {
102
+ for (const dataSource of requirement.dataSources) {
103
+ const owner = owners.get(dataSource);
104
+ if (owner && owner !== requirement.localRefId) conflicts.add(dataSource);else owners.set(dataSource, requirement.localRefId);
105
+ }
106
+ }
107
+ return [...conflicts];
108
+ }
109
+ function hasCompleteStaticConnectionConfig(connectionReferences, schemaMap) {
110
+ const references = Object.values(connectionReferences);
111
+ const directReferences = references.filter(reference => !isCompositeConnectionApiId(reference.id));
112
+ return directReferences.length > 0 && directReferences.every(reference => Boolean(reference.id) && reference.dataSources.length > 0 && reference.dataSources.every(dataSource => Boolean(schemaMap[dataSource]?.name && schemaMap[dataSource]?.properties?.primaryRuntimeUrl)));
113
+ }
114
+ function buildConnectionResolutionKey(appId, environmentId, connectionReferences) {
115
+ const references = Object.entries(connectionReferences).sort(([left], [right]) => left.localeCompare(right)).map(([localRefId, reference]) => ({
116
+ localRefId,
117
+ id: reference.id,
118
+ dataSources: [...reference.dataSources].sort(),
119
+ sharedConnectionId: reference.sharedConnectionId ?? '',
120
+ parameterSetName: reference.parameterSetName ?? ''
121
+ }));
122
+ return JSON.stringify({
123
+ appId: appId ?? '',
124
+ environmentId,
125
+ references
126
+ });
127
+ }
128
+
27
129
  /** A single connection instance returned by the Power Platform connectivity API. */
130
+
28
131
  /**
29
132
  * Result of attempting to match a single connection reference to an instance.
30
133
  *
@@ -32,10 +135,12 @@ let ConnectionStatus = exports.ConnectionStatus = /*#__PURE__*/function (Connect
32
135
  * - `missing` — no connections found for this connector in the user's environment.
33
136
  * - `ambiguous` — multiple Connected connections found; the app must pick one.
34
137
  */
138
+
35
139
  /**
36
140
  * Disambiguation result for a single connection reference (one apiId, possibly
37
141
  * mapped to multiple data source names in the final connections map).
38
142
  */
143
+
39
144
  /**
40
145
  * Callback invoked when a connection reference has multiple Connected candidate
41
146
  * connections. The consuming app should present a native picker UI (e.g. a
@@ -48,6 +153,7 @@ let ConnectionStatus = exports.ConnectionStatus = /*#__PURE__*/function (Connect
48
153
  * @param context.apiId — connector API identifier
49
154
  * @param context.candidates — list of Connected connection instances to choose from
50
155
  */
156
+
51
157
  /**
52
158
  * Callback invoked when the user has no connection for a required connector.
53
159
  *
@@ -6,13 +6,14 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.useConnectionRefs = useConnectionRefs;
7
7
  var _react = require("react");
8
8
  var _isDevPlayer = require("../utils/isDevPlayer.js");
9
- var _PowerPlatformEnvironmentApi = require("./PowerPlatformEnvironmentApi.js");
10
- var _types = require("../connections/types.js");
9
+ var _PowerPlatformEnvironmentApi = require("../platform/PowerPlatformEnvironmentApi.js");
11
10
  var _PowerPlatformConnectionApi = require("./PowerPlatformConnectionApi.js");
12
11
  var _resolveConnections = require("./resolveConnections.js");
12
+ var _connectionState = require("./connectionState.js");
13
13
  var _logger = require("../logger.js");
14
14
  var _index = require("../telemetry/index.js");
15
15
  var _NativePowerAppsBridge = require("../bridge/NativePowerAppsBridge.js");
16
+ var _types = require("./types.js");
16
17
  /*!
17
18
  * power-apps-native-host — useConnectionRefs
18
19
  *
@@ -54,17 +55,10 @@ var _NativePowerAppsBridge = require("../bridge/NativePowerAppsBridge.js");
54
55
  * disambiguation support for environments where multiple connections exist for
55
56
  * the same connector.
56
57
  *
57
- * On sign-in this hook:
58
- * 1. If `appId` is set (production template), fetches connection references
59
- * from the PowerApps RP `/apps/{appId}/launch` endpoint and uses those
60
- * in place of the static `connectionReferences` from power.config.json.
61
- * 2. Acquires a Power Platform API token.
62
- * 3. In parallel: fetches the Dataverse instanceUrl and ALL connections for
63
- * each configured connector.
64
- * 4. Runs disambiguation: resolves single matches, invokes `onDisambiguationNeeded`
65
- * for multiple matches, and invokes `onMissingConnection` for absent ones.
66
- * 5. Pre-warms the Dataverse token in the MSAL cache.
67
- * 6. Sets isReady=true with the fully-populated connections map.
58
+ * Outside Dev Player, the published app launch endpoint is authoritative.
59
+ * Dev Player resolves unpublished local references through per-connector APIs.
60
+ * Both paths disambiguate candidates, pre-warm Dataverse authentication, and
61
+ * return a fully populated bridge configuration.
68
62
  */
69
63
  function useConnectionRefs({
70
64
  isSignedIn,
@@ -81,31 +75,37 @@ function useConnectionRefs({
81
75
  const [connectionRefs, setConnectionRefs] = (0, _react.useState)({});
82
76
  const [isReady, setIsReady] = (0, _react.useState)(false);
83
77
  const [error, setError] = (0, _react.useState)(null);
78
+ const [errorKind, setErrorKind] = (0, _react.useState)(null);
84
79
  const [missingConnectors, setMissingConnectors] = (0, _react.useState)([]);
80
+ const [missingConnectionReferences, setMissingConnectionReferences] = (0, _react.useState)([]);
81
+ const [connectionRequirements, setConnectionRequirements] = (0, _react.useState)([]);
82
+ const [resourceConsents, setResourceConsents] = (0, _react.useState)([]);
83
+ const [configurationErrors, setConfigurationErrors] = (0, _react.useState)([]);
84
+ const [requiresConnectionConsent, setRequiresConnectionConsent] = (0, _react.useState)(false);
85
+ const [resolvedConfigKey, setResolvedConfigKey] = (0, _react.useState)(null);
85
86
  const [dataverseOrgUrl, setDataverseOrgUrl] = (0, _react.useState)('');
87
+ const [databaseReferences, setDatabaseReferences] = (0, _react.useState)({});
86
88
 
87
- // Bounded re-resolution for the transient null-token case. A null token at
88
- // cold boot is usually a silent-auth miss (the account exists, but MSAL hasn't
89
- // produced a token yet); without a retry the effect — keyed on isSignedIn —
90
- // would never re-resolve once a token becomes available, leaving the bridge on
91
- // the static fallback refs (no runtimeUrl) for the whole session. retryTick
92
- // re-runs the effect; tokenRetryCountRef caps the attempts so it can never loop.
89
+ // Bounded re-resolution for transient silent-token misses.
93
90
  const [retryTick, setRetryTick] = (0, _react.useState)(0);
94
91
  const tokenRetryCountRef = (0, _react.useRef)(0);
95
- // True only when this effect run was triggered by the bounded background
96
- // retry below (not an initial resolution or a sign-in). Used to avoid
97
- // clobbering an already-ready UI: a retry must re-resolve in the background
98
- // without dropping isReady, which would otherwise unmount the whole signed-in
99
- // subtree (spinner) on every tick.
92
+ // Dev Player retries in the background without dropping its locally ready UI.
100
93
  const isBackgroundRetryRef = (0, _react.useRef)(false);
101
94
  const MAX_TOKEN_RETRIES = 5;
102
95
  const TOKEN_RETRY_DELAY_MS = 2000;
96
+ const currentConfigKey = (0, _types.buildConnectionResolutionKey)(appId, environmentId, _isDevPlayer.isDevPlayer ? connectionReferences : {});
103
97
  (0, _react.useEffect)(() => {
104
98
  if (!isSignedIn) {
105
99
  tokenRetryCountRef.current = 0;
106
100
  isBackgroundRetryRef.current = false;
107
101
  return;
108
102
  }
103
+ if (!_isDevPlayer.isDevPlayer && (!appId || !environmentId)) {
104
+ setErrorKind('generic');
105
+ setError('The production app requires both an app ID and an environment ID before it can load from Power Platform.');
106
+ setIsReady(true);
107
+ return;
108
+ }
109
109
 
110
110
  // Consume the background-retry flag once per run so a later initial run or
111
111
  // sign-in can never inherit a stale value.
@@ -123,6 +123,7 @@ function useConnectionRefs({
123
123
  // an online (offline-disabled) played app never resolves its instanceUrl in
124
124
  // the Dev Player and every Dataverse read fails with "No instanceUrl found".
125
125
  if (_isDevPlayer.isDevPlayer && !hasConnectionRefs && !environmentId) {
126
+ setResolvedConfigKey(currentConfigKey);
126
127
  setIsReady(true);
127
128
  return;
128
129
  }
@@ -131,50 +132,63 @@ function useConnectionRefs({
131
132
  // (and no appId to fetch from the launch API).
132
133
  // Mark ready immediately so the bridge can initialise without any network calls.
133
134
  if (!environmentId && !appId) {
135
+ setResolvedConfigKey(currentConfigKey);
134
136
  setIsReady(true);
135
137
  return;
136
138
  }
137
139
  let cancelled = false;
138
140
  let retryTimer;
139
- // Skip the reset on a background-retry run: the UI is already mounted with
140
- // static fallback refs, so re-resolution must happen in the background. The
141
- // full-resolution path below still sets missingConnectors/error from the
142
- // real result. Resetting here would drop isReady → unmount the subtree.
141
+ const resolutionScenario = (0, _index.getAppLogger)().trackScenario(_index.HostOperationNames.PlatformResolveConnectionRefs, {
142
+ mode: _isDevPlayer.isDevPlayer ? 'devPlayer' : 'publishedApp',
143
+ hasAppId: Boolean(appId),
144
+ connectionReferenceCount: Object.keys(connectionReferences).length
145
+ });
146
+ // Preserve the locally ready Dev Player UI during a background retry.
143
147
  if (!isBackgroundRetry) {
144
148
  setIsReady(false);
145
149
  setError(null);
150
+ setErrorKind(null);
146
151
  setMissingConnectors([]);
152
+ setMissingConnectionReferences([]);
153
+ setConnectionRequirements([]);
154
+ setResourceConsents([]);
155
+ setConfigurationErrors([]);
156
+ setRequiresConnectionConsent(false);
157
+ setResolvedConfigKey(null);
158
+ setDataverseOrgUrl('');
159
+ setDatabaseReferences({});
147
160
  }
148
161
  async function loadConnections() {
149
162
  const token = await acquireToken([(0, _PowerPlatformEnvironmentApi.powerPlatformScope)(region)]);
150
- if (cancelled) return;
163
+ if (cancelled) {
164
+ resolutionScenario.complete({
165
+ result: 'cancelled'
166
+ });
167
+ return;
168
+ }
151
169
  if (!token) {
152
- // Cold boot before an auth token is available (e.g. silent-token miss or
153
- // offline launch). The per-user runtimeUrl/connectionName can't be
154
- // resolved yet, but seed the bridge with whatever static apiId entries can
155
- // be derived from connectionReferences so the runtime fields populate on
156
- // the next resolution. NOTE: when connectionReferences is empty (the
157
- // appId-only fast path) this seed is itself empty, so the bridge starts
158
- // with no connections until the bounded retry below resolves a token.
159
- // That gap is transient as long as a retry succeeds within
160
- // MAX_TOKEN_RETRIES; if the budget is exhausted the bridge stays seeded
161
- // with the empty map until the next sign-out/in resets the retry count.
162
- // Still mark ready so the host mounts the UI (the offline SDK serves local
163
- // SQLite without a token) instead of gating on !isReady forever.
164
- setConnectionRefs((0, _NativePowerAppsBridge.buildConnectionReferences)(connectionReferences));
165
- setIsReady(true);
166
- // Schedule a bounded re-resolution so a transient silent-auth miss
167
- // recovers once a token is available, instead of staying on the static
168
- // fallback refs for the rest of the session. Capped by MAX_TOKEN_RETRIES
169
- // so it can never spin.
170
+ resolutionScenario.completeWithError({
171
+ reason: 'tokenUnavailable'
172
+ });
173
+ // Dev Player can render local metadata while token recovery continues;
174
+ // standalone apps stay blocked because production has no static fallback.
175
+ if (_isDevPlayer.isDevPlayer) {
176
+ setConnectionRefs((0, _NativePowerAppsBridge.buildConnectionReferences)(connectionReferences));
177
+ setIsReady(true);
178
+ setResolvedConfigKey(currentConfigKey);
179
+ }
170
180
  if (tokenRetryCountRef.current < MAX_TOKEN_RETRIES) {
171
181
  tokenRetryCountRef.current += 1;
172
182
  retryTimer = setTimeout(() => {
173
183
  if (!cancelled) {
174
- isBackgroundRetryRef.current = true;
184
+ isBackgroundRetryRef.current = _isDevPlayer.isDevPlayer;
175
185
  setRetryTick(t => t + 1);
176
186
  }
177
187
  }, TOKEN_RETRY_DELAY_MS);
188
+ } else if (!_isDevPlayer.isDevPlayer) {
189
+ setErrorKind('generic');
190
+ setError('Unable to authenticate with Power Platform to load this app. Sign in again and retry.');
191
+ setIsReady(true);
178
192
  }
179
193
  return;
180
194
  }
@@ -188,28 +202,47 @@ function useConnectionRefs({
188
202
  let result = null;
189
203
  if (appId && environmentId && !_isDevPlayer.isDevPlayer) {
190
204
  result = await (0, _PowerPlatformConnectionApi.fetchAppLaunchData)(appId, environmentId, region, token);
205
+ if (!result) {
206
+ throw new Error(`App "${appId}" is not available in environment "${environmentId}". Push the mobile app to this environment and try again.`);
207
+ }
208
+ if (result && onDisambiguationNeeded) {
209
+ for (const requirement of [...result.missingConnectionReferences]) {
210
+ const candidates = result.connectionCandidates[requirement.localRefId] ?? [];
211
+ if (candidates.length < 2) continue;
212
+ const selectedId = await onDisambiguationNeeded({
213
+ localRefId: requirement.localRefId,
214
+ apiId: requirement.apiId,
215
+ candidates
216
+ });
217
+ if (selectedId) {
218
+ (0, _connectionState.selectLaunchConnection)(result, requirement.localRefId, selectedId);
219
+ }
220
+ }
221
+ }
191
222
  if (result && isvClientId) {
192
- await (0, _resolveConnections.applyAclGrants)(result.connectionRefs, environmentId, isvClientId, acquireToken);
193
- } else {
194
- (0, _logger.debugWarn)('[useConnectionRefs] could not verify connection ACLs; treating existing connections as setup-required');
223
+ const failedConnectionNames = await (0, _resolveConnections.applyAclGrants)(result.connectionRefs, result.connectionRequirements, environmentId, region, acquireToken);
224
+ (0, _connectionState.invalidateFailedAclGrants)(result, failedConnectionNames);
225
+ } else if (result) {
226
+ (0, _logger.debugWarn)('[useConnectionRefs] could not grant user-selected connections; treating them as setup-required');
195
227
  (0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.PlatformConnectionAclGrant, {
196
228
  phase: 'launchAclVerify',
197
- reason: result ? 'missingIsvClientId' : 'launchDataUnavailable',
198
- connectionRefCount: result ? Object.keys(result.connectionRefs).length : 0
229
+ reason: 'missingIsvClientId',
230
+ connectionRefCount: Object.keys(result.connectionRefs).length
199
231
  });
200
- for (const r of result ? Object.values(result.connectionRefs) : []) {
201
- for (const inst of r.instances || []) {
202
- if (r.status === _types.ConnectionStatus.Connected) {
203
- r.status = _types.ConnectionStatus.NeedsAclGrant;
204
- }
205
- }
206
- }
232
+ const unresolved = new Set((0, _connectionState.getConnectionNamesRequiringAclGrant)(result.connectionRefs, result.connectionRequirements));
233
+ (0, _connectionState.invalidateFailedAclGrants)(result, unresolved);
234
+ }
235
+ if (result) {
236
+ (0, _connectionState.applyRegisteredConnectionUsages)(result, result.registeredConnectionUsages);
207
237
  }
208
238
  }
209
239
 
210
240
  // Slow path: per-connector resolution.
211
- // Used when: running in Dev Player, no appId (local dev), or launch API failed.
241
+ // Used only in Dev Player, where unpublished local app metadata is expected.
212
242
  if (!result) {
243
+ if (!_isDevPlayer.isDevPlayer) {
244
+ throw new Error('Unable to load the production app from the Power Platform launch endpoint.');
245
+ }
213
246
  result = await (0, _resolveConnections.resolveViaStaticRefs)({
214
247
  connectionReferences,
215
248
  schemaMap,
@@ -222,7 +255,12 @@ function useConnectionRefs({
222
255
  onMissingConnection
223
256
  });
224
257
  }
225
- if (cancelled) return;
258
+ if (cancelled) {
259
+ resolutionScenario.complete({
260
+ result: 'cancelled'
261
+ });
262
+ return;
263
+ }
226
264
 
227
265
  // Pre-warm the Dataverse token while the user waits.
228
266
  if (result.dataverseOrgUrl) {
@@ -231,8 +269,20 @@ function useConnectionRefs({
231
269
  if (!cancelled) {
232
270
  setConnectionRefs(result.connectionRefs);
233
271
  setMissingConnectors(result.missingConnectors);
272
+ setMissingConnectionReferences(result.missingConnectionReferences);
273
+ setConnectionRequirements(result.connectionRequirements);
274
+ setResourceConsents(result.resourceConsents);
275
+ setConfigurationErrors(result.configurationErrors);
276
+ setRequiresConnectionConsent(result.requiresConnectionConsent);
234
277
  setDataverseOrgUrl(result.dataverseOrgUrl);
278
+ setDatabaseReferences(result.databaseReferences);
279
+ setResolvedConfigKey(currentConfigKey);
235
280
  setIsReady(true);
281
+ resolutionScenario.complete({
282
+ result: 'resolved',
283
+ missingConnectionReferenceCount: result.missingConnectionReferences.length,
284
+ configurationErrorCount: result.configurationErrors.length
285
+ });
236
286
  }
237
287
  }
238
288
  loadConnections().catch(e => {
@@ -243,6 +293,9 @@ function useConnectionRefs({
243
293
  // production without raising a __DEV__ LogBox redbox over the
244
294
  // otherwise-working UI. We still recover via setIsReady below.
245
295
  (0, _logger.debugLog)('[useConnectionRefs] Failed to load connections:', e);
296
+ resolutionScenario.fail(undefined, {
297
+ errorName: e instanceof Error ? e.name : 'UnknownError'
298
+ });
246
299
  (0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.PlatformLoadConnectionsFailed, {
247
300
  error: String(e),
248
301
  hasAppId: Boolean(appId),
@@ -250,7 +303,9 @@ function useConnectionRefs({
250
303
  connectionReferenceCount: Object.keys(connectionReferences).length,
251
304
  runningInDevPlayer: _isDevPlayer.isDevPlayer
252
305
  });
253
- setError(String(e));
306
+ const accessDenied = e instanceof _PowerPlatformConnectionApi.AppLaunchAccessDeniedError;
307
+ setErrorKind(accessDenied ? 'app-access-denied' : 'generic');
308
+ setError(accessDenied ? 'You don’t have access to this app. Ask the app owner or an administrator to share it with you, then try again.' : 'Unable to load this app from Power Platform. Try again later.');
254
309
  setIsReady(true);
255
310
  }
256
311
  });
@@ -259,12 +314,20 @@ function useConnectionRefs({
259
314
  if (retryTimer) clearTimeout(retryTimer);
260
315
  };
261
316
  // eslint-disable-next-line react-hooks/exhaustive-deps
262
- }, [isSignedIn, retryTick]);
317
+ }, [isSignedIn, retryTick, appId, environmentId, region, connectionReferences, schemaMap, isvClientId]);
263
318
  return {
264
319
  connectionRefs,
265
320
  isReady,
266
321
  error,
322
+ errorKind,
267
323
  missingConnectors,
268
- dataverseOrgUrl
324
+ missingConnectionReferences,
325
+ connectionRequirements,
326
+ resourceConsents,
327
+ configurationErrors,
328
+ requiresConnectionConsent,
329
+ resolvedConfigKey,
330
+ dataverseOrgUrl,
331
+ databaseReferences
269
332
  };
270
333
  }