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

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 (154) hide show
  1. package/README.md +21 -10
  2. package/compatibility/current.json +31 -4
  3. package/lib/NOTICE.txt +1325 -24
  4. package/lib/commonjs/auth/AuthContext.js +110 -33
  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/config/babelConfig.js +13 -0
  9. package/lib/commonjs/config/expoConfig.js +71 -0
  10. package/lib/commonjs/config/metroConfig.js +70 -0
  11. package/lib/commonjs/config/metroLogger.js +182 -0
  12. package/lib/commonjs/config/tamaguiConfig.js +98 -0
  13. package/lib/commonjs/connections/ConnectionSetupScreen.js +459 -80
  14. package/lib/commonjs/connections/PowerPlatformConnectionApi.js +946 -0
  15. package/lib/commonjs/connections/connectionMetadata.js +127 -0
  16. package/lib/commonjs/connections/connectionState.js +96 -0
  17. package/lib/commonjs/connections/disambiguate.js +19 -11
  18. package/lib/commonjs/connections/resolveConnections.js +348 -0
  19. package/lib/commonjs/connections/types.js +106 -0
  20. package/lib/commonjs/{platform → connections}/useConnectionRefs.js +130 -65
  21. package/lib/commonjs/connections/useConnectionSetup.js +111 -52
  22. package/lib/commonjs/controls/FilePicker.js +6 -0
  23. package/lib/commonjs/controls/ImagePicker.js +8 -0
  24. package/lib/commonjs/index.js +167 -4
  25. package/lib/commonjs/logger.js +9 -6
  26. package/lib/commonjs/offline/createOfflineHostActivationCallbacks.js +61 -0
  27. package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.js +48 -0
  28. package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.web.js +13 -0
  29. package/lib/commonjs/platform/DataverseDataSources.js +18 -0
  30. package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +94 -0
  31. package/lib/commonjs/platform/GlobalDiscovery.js +51 -0
  32. package/lib/commonjs/platform/PowerPlatformEnvironmentApi.js +6 -106
  33. package/lib/commonjs/provider/PowerAppsHostProvider.js +157 -25
  34. package/lib/commonjs/provider/PowerAppsProvider.js +178 -134
  35. package/lib/commonjs/provider/SessionDetailsGesture.js +208 -0
  36. package/lib/commonjs/telemetry/OfflineSdkTelemetry.js +95 -0
  37. package/lib/commonjs/telemetry/OfflineSdkTelemetry.web.js +9 -0
  38. package/lib/commonjs/telemetry/index.js +22 -3
  39. package/lib/module/auth/AuthContext.js +110 -33
  40. package/lib/module/bridge/NativePowerAppsBridge.js +12 -19
  41. package/lib/module/bridge/PowerPagesBridge.js +27 -8
  42. package/lib/module/bridge/bridgeState.js +34 -0
  43. package/lib/module/config/babelConfig.js +9 -0
  44. package/lib/module/config/expoConfig.js +67 -0
  45. package/lib/module/config/metroConfig.js +65 -0
  46. package/lib/module/config/metroLogger.js +175 -0
  47. package/lib/module/config/tamaguiConfig.js +93 -0
  48. package/lib/module/connections/ConnectionSetupScreen.js +460 -81
  49. package/lib/module/connections/PowerPlatformConnectionApi.js +932 -0
  50. package/lib/module/connections/connectionMetadata.js +112 -0
  51. package/lib/module/connections/connectionState.js +82 -0
  52. package/lib/module/connections/disambiguate.js +19 -12
  53. package/lib/module/connections/resolveConnections.js +343 -0
  54. package/lib/module/connections/types.js +91 -0
  55. package/lib/module/{platform → connections}/useConnectionRefs.js +131 -66
  56. package/lib/module/connections/useConnectionSetup.js +113 -54
  57. package/lib/module/controls/FilePicker.js +6 -0
  58. package/lib/module/controls/ImagePicker.js +8 -0
  59. package/lib/module/index.js +14 -4
  60. package/lib/module/logger.js +8 -6
  61. package/lib/module/offline/createOfflineHostActivationCallbacks.js +55 -0
  62. package/lib/module/offline/useOfflineIntegrationWithStartupGate.js +44 -0
  63. package/lib/module/offline/useOfflineIntegrationWithStartupGate.web.js +9 -0
  64. package/lib/module/platform/DataverseDataSources.js +13 -0
  65. package/lib/module/platform/DataverseGlobalDiscoveryApi.js +89 -0
  66. package/lib/module/platform/GlobalDiscovery.js +45 -0
  67. package/lib/module/platform/PowerPlatformEnvironmentApi.js +5 -104
  68. package/lib/module/provider/PowerAppsHostProvider.js +160 -28
  69. package/lib/module/provider/PowerAppsProvider.js +182 -138
  70. package/lib/module/provider/SessionDetailsGesture.js +203 -0
  71. package/lib/module/telemetry/OfflineSdkTelemetry.js +91 -0
  72. package/lib/module/telemetry/OfflineSdkTelemetry.web.js +5 -0
  73. package/lib/module/telemetry/index.js +15 -4
  74. package/lib/typescript/commonjs/auth/AuthContext.d.ts +3 -1
  75. package/lib/typescript/commonjs/auth/AuthContext.web.d.ts +1 -0
  76. package/lib/typescript/commonjs/bridge/bridgeState.d.ts +5 -0
  77. package/lib/typescript/commonjs/config/babelConfig.d.ts +12 -0
  78. package/lib/typescript/commonjs/config/expoConfig.d.ts +21 -0
  79. package/lib/typescript/commonjs/config/metroConfig.d.ts +6 -0
  80. package/lib/typescript/commonjs/config/metroLogger.d.ts +21 -0
  81. package/lib/typescript/commonjs/config/tamaguiConfig.d.ts +1792 -0
  82. package/lib/typescript/commonjs/connections/ConnectionSetupScreen.d.ts +28 -7
  83. package/lib/typescript/commonjs/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
  84. package/lib/typescript/commonjs/connections/connectionMetadata.d.ts +66 -0
  85. package/lib/typescript/commonjs/connections/connectionState.d.ts +16 -0
  86. package/lib/typescript/commonjs/connections/disambiguate.d.ts +4 -2
  87. package/lib/typescript/commonjs/{platform → connections}/resolveConnections.d.ts +22 -20
  88. package/lib/typescript/commonjs/connections/types.d.ts +129 -1
  89. package/lib/typescript/commonjs/{platform → connections}/useConnectionRefs.d.ts +36 -25
  90. package/lib/typescript/commonjs/connections/useConnectionSetup.d.ts +12 -8
  91. package/lib/typescript/commonjs/controls/FilePicker.d.ts +7 -1
  92. package/lib/typescript/commonjs/controls/ImagePicker.d.ts +9 -1
  93. package/lib/typescript/commonjs/index.d.ts +18 -10
  94. package/lib/typescript/commonjs/logger.d.ts +4 -4
  95. package/lib/typescript/commonjs/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
  96. package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
  97. package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
  98. package/lib/typescript/commonjs/platform/DataverseDataSources.d.ts +28 -0
  99. package/lib/typescript/commonjs/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
  100. package/lib/typescript/commonjs/platform/GlobalDiscovery.d.ts +4 -0
  101. package/lib/typescript/commonjs/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
  102. package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +29 -18
  103. package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +54 -9
  104. package/lib/typescript/commonjs/provider/SessionDetailsGesture.d.ts +13 -0
  105. package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.d.ts +15 -0
  106. package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
  107. package/lib/typescript/commonjs/telemetry/index.d.ts +13 -1
  108. package/lib/typescript/module/auth/AuthContext.d.ts +3 -1
  109. package/lib/typescript/module/auth/AuthContext.web.d.ts +1 -0
  110. package/lib/typescript/module/bridge/bridgeState.d.ts +5 -0
  111. package/lib/typescript/module/config/babelConfig.d.ts +12 -0
  112. package/lib/typescript/module/config/expoConfig.d.ts +21 -0
  113. package/lib/typescript/module/config/metroConfig.d.ts +6 -0
  114. package/lib/typescript/module/config/metroLogger.d.ts +21 -0
  115. package/lib/typescript/module/config/tamaguiConfig.d.ts +1792 -0
  116. package/lib/typescript/module/connections/ConnectionSetupScreen.d.ts +28 -7
  117. package/lib/typescript/module/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
  118. package/lib/typescript/module/connections/connectionMetadata.d.ts +66 -0
  119. package/lib/typescript/module/connections/connectionState.d.ts +16 -0
  120. package/lib/typescript/module/connections/disambiguate.d.ts +4 -2
  121. package/lib/typescript/module/{platform → connections}/resolveConnections.d.ts +22 -20
  122. package/lib/typescript/module/connections/types.d.ts +129 -1
  123. package/lib/typescript/module/{platform → connections}/useConnectionRefs.d.ts +36 -25
  124. package/lib/typescript/module/connections/useConnectionSetup.d.ts +12 -8
  125. package/lib/typescript/module/controls/FilePicker.d.ts +7 -1
  126. package/lib/typescript/module/controls/ImagePicker.d.ts +9 -1
  127. package/lib/typescript/module/index.d.ts +18 -10
  128. package/lib/typescript/module/logger.d.ts +4 -4
  129. package/lib/typescript/module/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
  130. package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
  131. package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
  132. package/lib/typescript/module/platform/DataverseDataSources.d.ts +28 -0
  133. package/lib/typescript/module/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
  134. package/lib/typescript/module/platform/GlobalDiscovery.d.ts +4 -0
  135. package/lib/typescript/module/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
  136. package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +29 -18
  137. package/lib/typescript/module/provider/PowerAppsProvider.d.ts +54 -9
  138. package/lib/typescript/module/provider/SessionDetailsGesture.d.ts +13 -0
  139. package/lib/typescript/module/telemetry/OfflineSdkTelemetry.d.ts +15 -0
  140. package/lib/typescript/module/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
  141. package/lib/typescript/module/telemetry/index.d.ts +13 -1
  142. package/package.json +52 -13
  143. package/scripts/build-codegen-package.js +283 -0
  144. package/scripts/start-web.mjs +9 -5
  145. package/scripts/upgrade/runtime.js +139 -24
  146. package/scripts/upgrade-authoring.js +274 -180
  147. package/scripts/upgrade.js +10 -16
  148. package/src/config/generated-config.d.ts +4 -0
  149. package/src/config/index.d.ts +3 -0
  150. package/src/config/tsconfig.json +26 -0
  151. package/lib/commonjs/platform/PowerPlatformConnectionApi.js +0 -606
  152. package/lib/commonjs/platform/resolveConnections.js +0 -264
  153. package/lib/module/platform/PowerPlatformConnectionApi.js +0 -595
  154. 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,
@@ -73,6 +67,7 @@ function useConnectionRefs({
73
67
  environmentId,
74
68
  region = 'prod',
75
69
  connectionReferences,
70
+ hasDataverse,
76
71
  schemaMap,
77
72
  onDisambiguationNeeded,
78
73
  onMissingConnection,
@@ -81,31 +76,37 @@ function useConnectionRefs({
81
76
  const [connectionRefs, setConnectionRefs] = (0, _react.useState)({});
82
77
  const [isReady, setIsReady] = (0, _react.useState)(false);
83
78
  const [error, setError] = (0, _react.useState)(null);
79
+ const [errorKind, setErrorKind] = (0, _react.useState)(null);
84
80
  const [missingConnectors, setMissingConnectors] = (0, _react.useState)([]);
81
+ const [missingConnectionReferences, setMissingConnectionReferences] = (0, _react.useState)([]);
82
+ const [connectionRequirements, setConnectionRequirements] = (0, _react.useState)([]);
83
+ const [resourceConsents, setResourceConsents] = (0, _react.useState)([]);
84
+ const [configurationErrors, setConfigurationErrors] = (0, _react.useState)([]);
85
+ const [requiresConnectionConsent, setRequiresConnectionConsent] = (0, _react.useState)(false);
86
+ const [resolvedConfigKey, setResolvedConfigKey] = (0, _react.useState)(null);
85
87
  const [dataverseOrgUrl, setDataverseOrgUrl] = (0, _react.useState)('');
88
+ const [databaseReferences, setDatabaseReferences] = (0, _react.useState)({});
86
89
 
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.
90
+ // Bounded re-resolution for transient silent-token misses.
93
91
  const [retryTick, setRetryTick] = (0, _react.useState)(0);
94
92
  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.
93
+ // Dev Player retries in the background without dropping its locally ready UI.
100
94
  const isBackgroundRetryRef = (0, _react.useRef)(false);
101
95
  const MAX_TOKEN_RETRIES = 5;
102
96
  const TOKEN_RETRY_DELAY_MS = 2000;
97
+ const currentConfigKey = (0, _types.buildConnectionResolutionKey)(appId, environmentId, _isDevPlayer.isDevPlayer ? connectionReferences : {});
103
98
  (0, _react.useEffect)(() => {
104
99
  if (!isSignedIn) {
105
100
  tokenRetryCountRef.current = 0;
106
101
  isBackgroundRetryRef.current = false;
107
102
  return;
108
103
  }
104
+ if (!_isDevPlayer.isDevPlayer && (!appId || !environmentId)) {
105
+ setErrorKind('generic');
106
+ setError('The production app requires both an app ID and an environment ID before it can load from Power Platform.');
107
+ setIsReady(true);
108
+ return;
109
+ }
109
110
 
110
111
  // Consume the background-retry flag once per run so a later initial run or
111
112
  // sign-in can never inherit a stale value.
@@ -123,6 +124,7 @@ function useConnectionRefs({
123
124
  // an online (offline-disabled) played app never resolves its instanceUrl in
124
125
  // the Dev Player and every Dataverse read fails with "No instanceUrl found".
125
126
  if (_isDevPlayer.isDevPlayer && !hasConnectionRefs && !environmentId) {
127
+ setResolvedConfigKey(currentConfigKey);
126
128
  setIsReady(true);
127
129
  return;
128
130
  }
@@ -131,50 +133,63 @@ function useConnectionRefs({
131
133
  // (and no appId to fetch from the launch API).
132
134
  // Mark ready immediately so the bridge can initialise without any network calls.
133
135
  if (!environmentId && !appId) {
136
+ setResolvedConfigKey(currentConfigKey);
134
137
  setIsReady(true);
135
138
  return;
136
139
  }
137
140
  let cancelled = false;
138
141
  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.
142
+ const resolutionScenario = (0, _index.getAppLogger)().trackScenario(_index.HostOperationNames.PlatformResolveConnectionRefs, {
143
+ mode: _isDevPlayer.isDevPlayer ? 'devPlayer' : 'publishedApp',
144
+ hasAppId: Boolean(appId),
145
+ connectionReferenceCount: Object.keys(connectionReferences).length
146
+ });
147
+ // Preserve the locally ready Dev Player UI during a background retry.
143
148
  if (!isBackgroundRetry) {
144
149
  setIsReady(false);
145
150
  setError(null);
151
+ setErrorKind(null);
146
152
  setMissingConnectors([]);
153
+ setMissingConnectionReferences([]);
154
+ setConnectionRequirements([]);
155
+ setResourceConsents([]);
156
+ setConfigurationErrors([]);
157
+ setRequiresConnectionConsent(false);
158
+ setResolvedConfigKey(null);
159
+ setDataverseOrgUrl('');
160
+ setDatabaseReferences({});
147
161
  }
148
162
  async function loadConnections() {
149
163
  const token = await acquireToken([(0, _PowerPlatformEnvironmentApi.powerPlatformScope)(region)]);
150
- if (cancelled) return;
164
+ if (cancelled) {
165
+ resolutionScenario.complete({
166
+ result: 'cancelled'
167
+ });
168
+ return;
169
+ }
151
170
  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.
171
+ resolutionScenario.completeWithError({
172
+ reason: 'tokenUnavailable'
173
+ });
174
+ // Dev Player can render local metadata while token recovery continues;
175
+ // standalone apps stay blocked because production has no static fallback.
176
+ if (_isDevPlayer.isDevPlayer) {
177
+ setConnectionRefs((0, _NativePowerAppsBridge.buildConnectionReferences)(connectionReferences));
178
+ setIsReady(true);
179
+ setResolvedConfigKey(currentConfigKey);
180
+ }
170
181
  if (tokenRetryCountRef.current < MAX_TOKEN_RETRIES) {
171
182
  tokenRetryCountRef.current += 1;
172
183
  retryTimer = setTimeout(() => {
173
184
  if (!cancelled) {
174
- isBackgroundRetryRef.current = true;
185
+ isBackgroundRetryRef.current = _isDevPlayer.isDevPlayer;
175
186
  setRetryTick(t => t + 1);
176
187
  }
177
188
  }, TOKEN_RETRY_DELAY_MS);
189
+ } else if (!_isDevPlayer.isDevPlayer) {
190
+ setErrorKind('generic');
191
+ setError('Unable to authenticate with Power Platform to load this app. Sign in again and retry.');
192
+ setIsReady(true);
178
193
  }
179
194
  return;
180
195
  }
@@ -188,30 +203,50 @@ function useConnectionRefs({
188
203
  let result = null;
189
204
  if (appId && environmentId && !_isDevPlayer.isDevPlayer) {
190
205
  result = await (0, _PowerPlatformConnectionApi.fetchAppLaunchData)(appId, environmentId, region, token);
206
+ if (!result) {
207
+ throw new Error(`App "${appId}" is not available in environment "${environmentId}". Push the mobile app to this environment and try again.`);
208
+ }
209
+ if (result && onDisambiguationNeeded) {
210
+ for (const requirement of [...result.missingConnectionReferences]) {
211
+ const candidates = result.connectionCandidates[requirement.localRefId] ?? [];
212
+ if (candidates.length < 2) continue;
213
+ const selectedId = await onDisambiguationNeeded({
214
+ localRefId: requirement.localRefId,
215
+ apiId: requirement.apiId,
216
+ candidates
217
+ });
218
+ if (selectedId) {
219
+ (0, _connectionState.selectLaunchConnection)(result, requirement.localRefId, selectedId);
220
+ }
221
+ }
222
+ }
191
223
  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');
224
+ const failedConnectionNames = await (0, _resolveConnections.applyAclGrants)(result.connectionRefs, result.connectionRequirements, environmentId, region, acquireToken);
225
+ (0, _connectionState.invalidateFailedAclGrants)(result, failedConnectionNames);
226
+ } else if (result) {
227
+ (0, _logger.debugWarn)('[useConnectionRefs] could not grant user-selected connections; treating them as setup-required');
195
228
  (0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.PlatformConnectionAclGrant, {
196
229
  phase: 'launchAclVerify',
197
- reason: result ? 'missingIsvClientId' : 'launchDataUnavailable',
198
- connectionRefCount: result ? Object.keys(result.connectionRefs).length : 0
230
+ reason: 'missingIsvClientId',
231
+ connectionRefCount: Object.keys(result.connectionRefs).length
199
232
  });
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
- }
233
+ const unresolved = new Set((0, _connectionState.getConnectionNamesRequiringAclGrant)(result.connectionRefs, result.connectionRequirements));
234
+ (0, _connectionState.invalidateFailedAclGrants)(result, unresolved);
235
+ }
236
+ if (result) {
237
+ (0, _connectionState.applyRegisteredConnectionUsages)(result, result.registeredConnectionUsages);
207
238
  }
208
239
  }
209
240
 
210
241
  // Slow path: per-connector resolution.
211
- // Used when: running in Dev Player, no appId (local dev), or launch API failed.
242
+ // Used only in Dev Player, where unpublished local app metadata is expected.
212
243
  if (!result) {
244
+ if (!_isDevPlayer.isDevPlayer) {
245
+ throw new Error('Unable to load the production app from the Power Platform launch endpoint.');
246
+ }
213
247
  result = await (0, _resolveConnections.resolveViaStaticRefs)({
214
248
  connectionReferences,
249
+ hasDataverse,
215
250
  schemaMap,
216
251
  environmentId,
217
252
  region,
@@ -222,7 +257,12 @@ function useConnectionRefs({
222
257
  onMissingConnection
223
258
  });
224
259
  }
225
- if (cancelled) return;
260
+ if (cancelled) {
261
+ resolutionScenario.complete({
262
+ result: 'cancelled'
263
+ });
264
+ return;
265
+ }
226
266
 
227
267
  // Pre-warm the Dataverse token while the user waits.
228
268
  if (result.dataverseOrgUrl) {
@@ -231,8 +271,20 @@ function useConnectionRefs({
231
271
  if (!cancelled) {
232
272
  setConnectionRefs(result.connectionRefs);
233
273
  setMissingConnectors(result.missingConnectors);
274
+ setMissingConnectionReferences(result.missingConnectionReferences);
275
+ setConnectionRequirements(result.connectionRequirements);
276
+ setResourceConsents(result.resourceConsents);
277
+ setConfigurationErrors(result.configurationErrors);
278
+ setRequiresConnectionConsent(result.requiresConnectionConsent);
234
279
  setDataverseOrgUrl(result.dataverseOrgUrl);
280
+ setDatabaseReferences(result.databaseReferences);
281
+ setResolvedConfigKey(currentConfigKey);
235
282
  setIsReady(true);
283
+ resolutionScenario.complete({
284
+ result: 'resolved',
285
+ missingConnectionReferenceCount: result.missingConnectionReferences.length,
286
+ configurationErrorCount: result.configurationErrors.length
287
+ });
236
288
  }
237
289
  }
238
290
  loadConnections().catch(e => {
@@ -243,6 +295,9 @@ function useConnectionRefs({
243
295
  // production without raising a __DEV__ LogBox redbox over the
244
296
  // otherwise-working UI. We still recover via setIsReady below.
245
297
  (0, _logger.debugLog)('[useConnectionRefs] Failed to load connections:', e);
298
+ resolutionScenario.fail(undefined, {
299
+ errorName: e instanceof Error ? e.name : 'UnknownError'
300
+ });
246
301
  (0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.PlatformLoadConnectionsFailed, {
247
302
  error: String(e),
248
303
  hasAppId: Boolean(appId),
@@ -250,7 +305,9 @@ function useConnectionRefs({
250
305
  connectionReferenceCount: Object.keys(connectionReferences).length,
251
306
  runningInDevPlayer: _isDevPlayer.isDevPlayer
252
307
  });
253
- setError(String(e));
308
+ const accessDenied = e instanceof _PowerPlatformConnectionApi.AppLaunchAccessDeniedError;
309
+ setErrorKind(accessDenied ? 'app-access-denied' : 'generic');
310
+ 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
311
  setIsReady(true);
255
312
  }
256
313
  });
@@ -259,12 +316,20 @@ function useConnectionRefs({
259
316
  if (retryTimer) clearTimeout(retryTimer);
260
317
  };
261
318
  // eslint-disable-next-line react-hooks/exhaustive-deps
262
- }, [isSignedIn, retryTick]);
319
+ }, [isSignedIn, retryTick, appId, environmentId, region, connectionReferences, hasDataverse, schemaMap, isvClientId]);
263
320
  return {
264
321
  connectionRefs,
265
322
  isReady,
266
323
  error,
324
+ errorKind,
267
325
  missingConnectors,
268
- dataverseOrgUrl
326
+ missingConnectionReferences,
327
+ connectionRequirements,
328
+ resourceConsents,
329
+ configurationErrors,
330
+ requiresConnectionConsent,
331
+ resolvedConfigKey,
332
+ dataverseOrgUrl,
333
+ databaseReferences
269
334
  };
270
335
  }