@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
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.buildConnectionParameterSets = buildConnectionParameterSets;
7
+ exports.buildConnectionUsageRequest = buildConnectionUsageRequest;
8
+ exports.buildConnectionUsageUrl = buildConnectionUsageUrl;
9
+ exports.buildConnectionUserConsentUrl = buildConnectionUserConsentUrl;
10
+ exports.buildConsentLinkDefinition = buildConsentLinkDefinition;
11
+ exports.buildScopeConsentRequest = buildScopeConsentRequest;
12
+ exports.buildScopeConsentUrl = buildScopeConsentUrl;
13
+ exports.getAppLaunchFailureMessage = getAppLaunchFailureMessage;
14
+ exports.hasOAuthParameter = hasOAuthParameter;
15
+ exports.isAppLaunchAccessDenied = isAppLaunchAccessDenied;
16
+ exports.parseConnectionParameters = parseConnectionParameters;
17
+ exports.serializeConnectionParameters = serializeConnectionParameters;
18
+ var _types = require("./types.js");
19
+ function parseConnectionParameters(parameters) {
20
+ return Object.entries(parameters ?? {}).flatMap(([name, parameter]) => {
21
+ if (!parameter.uiDefinition) return [];
22
+ const constraints = parameter.uiDefinition.constraints;
23
+ const type = (parameter.type ?? 'string').toLowerCase();
24
+ const supportedType = type === 'string' || type === 'securestring' || type === 'boolean' || type === 'integer' || type === 'clientcertificate' ? type : null;
25
+ if (!supportedType) return [];
26
+ return [{
27
+ name,
28
+ displayName: parameter.uiDefinition.displayName ?? name,
29
+ type: supportedType,
30
+ required: constraints?.required === true || constraints?.required === 'true',
31
+ hidden: constraints?.hidden === true || constraints?.hidden === 'true',
32
+ capabilities: constraints?.capability,
33
+ allowedValues: constraints?.allowedValues?.filter(item => Boolean(item.value)).map(item => ({
34
+ value: item.value,
35
+ displayName: item.displayName ?? item.value
36
+ })),
37
+ defaultValue: parameter.defaultValue
38
+ }];
39
+ });
40
+ }
41
+ function buildConnectionParameterSets(connectionParameters, parameterSets) {
42
+ const declaredSets = (parameterSets ?? []).filter(item => Boolean(item.name)).map(item => ({
43
+ name: item.name,
44
+ displayName: item.uiDefinition?.displayName ?? item.name,
45
+ parameters: parseConnectionParameters(item.connectionParameters ?? item.parameters),
46
+ requiresApiConsent: hasOAuthParameter(item.connectionParameters ?? item.parameters),
47
+ gatewayParameterName: Object.entries(item.connectionParameters ?? item.parameters ?? {}).find(([, parameter]) => parameter.type?.toLowerCase() === 'gatewaysetting')?.[0]
48
+ }));
49
+ return declaredSets.length > 0 ? declaredSets : [{
50
+ name: undefined,
51
+ displayName: 'Default',
52
+ parameters: parseConnectionParameters(connectionParameters),
53
+ requiresApiConsent: hasOAuthParameter(connectionParameters),
54
+ gatewayParameterName: Object.entries(connectionParameters ?? {}).find(([, parameter]) => parameter.type?.toLowerCase() === 'gatewaysetting')?.[0]
55
+ }];
56
+ }
57
+ function hasOAuthParameter(parameters) {
58
+ return Object.values(parameters ?? {}).some(parameter => parameter.type?.toLowerCase() === 'oauthsetting');
59
+ }
60
+ function buildConnectionUserConsentUrl(environmentEndpoint, connectorId, connectionId) {
61
+ return `${environmentEndpoint}/connectivity/connectors/${encodeURIComponent((0, _types.getConnectorId)(connectorId))}` + `/connections/${encodeURIComponent(connectionId)}/userConsent?api-version=1`;
62
+ }
63
+ function getAppLaunchFailureMessage(status, statusText, responseText) {
64
+ if (status === 404) return null;
65
+ return `Unable to load the published app (HTTP ${status}): ${responseText || statusText}`;
66
+ }
67
+ function isAppLaunchAccessDenied(status, responseText) {
68
+ if (status !== 403) return false;
69
+ try {
70
+ const body = JSON.parse(responseText);
71
+ return body.error?.code === 'PowerAppsNoAccess' || body.error?.details?.some(detail => detail.code === 'PowerAppsNoAccess') === true;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+ function buildConsentLinkDefinition(redirectUrl) {
77
+ return {
78
+ redirectUrl
79
+ };
80
+ }
81
+ function serializeConnectionParameters(input) {
82
+ const parameters = {
83
+ ...(input?.parameters ?? {}),
84
+ ...(input?.gatewayObjectId && input.gatewayParameterName ? {
85
+ [input.gatewayParameterName]: {
86
+ gatewayObjectId: input.gatewayObjectId
87
+ }
88
+ } : {})
89
+ };
90
+ if (!input?.parameterSetName) return {
91
+ connectionParameters: parameters
92
+ };
93
+ return {
94
+ connectionParametersSet: {
95
+ name: input.parameterSetName,
96
+ values: Object.fromEntries(Object.entries(parameters).map(([name, value]) => [name, {
97
+ value
98
+ }]))
99
+ }
100
+ };
101
+ }
102
+ function buildConnectionUsageUrl(environmentBaseUrl, environmentId, usage) {
103
+ return `${environmentBaseUrl}/powerapps/apis/${encodeURIComponent((0, _types.getConnectorId)(usage.apiId))}` + `/connections/${encodeURIComponent(usage.connectionId)}/usages` + `?api-version=1&$filter=environment eq '${environmentId}'`;
104
+ }
105
+ function buildConnectionUsageRequest(appId, usage) {
106
+ const resourceId = appId.startsWith('/providers/') ? appId : `/providers/Microsoft.PowerApps/apps/${appId}`;
107
+ return {
108
+ properties: {
109
+ reference: usage.localRefId,
110
+ resourceId,
111
+ appActions: usage.appActions
112
+ }
113
+ };
114
+ }
115
+ function buildScopeConsentUrl(environmentBaseUrl, appId) {
116
+ const appName = appId.split('/').filter(Boolean).pop() ?? appId;
117
+ return `${environmentBaseUrl}/powerapps/apps/${encodeURIComponent(appName)}` + '/reportScopesConsent?api-version=1';
118
+ }
119
+ function buildScopeConsentRequest(usages) {
120
+ return {
121
+ consents: usages.map(usage => ({
122
+ connectionId: usage.connectionId,
123
+ consentType: 'userConsent',
124
+ scopes: usage.apiScopes
125
+ }))
126
+ };
127
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.applyRegisteredConnectionSelections = applyRegisteredConnectionSelections;
7
+ exports.applyRegisteredConnectionUsages = applyRegisteredConnectionUsages;
8
+ exports.getConnectionNamesRequiringAclGrant = getConnectionNamesRequiringAclGrant;
9
+ exports.getUnregisteredConnectionRequirements = getUnregisteredConnectionRequirements;
10
+ exports.hasRegisteredConnectionConsent = hasRegisteredConnectionConsent;
11
+ exports.invalidateFailedAclGrants = invalidateFailedAclGrants;
12
+ exports.isTransientConnectionConsentStatus = isTransientConnectionConsentStatus;
13
+ exports.selectLaunchConnection = selectLaunchConnection;
14
+ exports.shouldGrantConnectionUserConsent = shouldGrantConnectionUserConsent;
15
+ exports.shouldPersistConnectionUsages = shouldPersistConnectionUsages;
16
+ exports.shouldShowConnectionReview = shouldShowConnectionReview;
17
+ var _types = require("./types.js");
18
+ var _disambiguate = require("./disambiguate.js");
19
+ function isTransientConnectionConsentStatus(status) {
20
+ return status === 408 || status === 429 || status >= 500;
21
+ }
22
+ function shouldGrantConnectionUserConsent(requirement, runningInDevPlayer) {
23
+ return runningInDevPlayer || !requirement.sharedConnectionId;
24
+ }
25
+ function getConnectionNamesRequiringAclGrant(connectionRefs, connectionRequirements) {
26
+ const connectionNames = [];
27
+ const seen = new Set();
28
+ for (const requirement of connectionRequirements) {
29
+ if (!shouldGrantConnectionUserConsent(requirement, false)) continue;
30
+ const connectionName = requirement.dataSources.map(dataSource => connectionRefs[dataSource]?.connectionName).find(selectedName => Boolean(selectedName));
31
+ if (!connectionName || seen.has(connectionName)) continue;
32
+ seen.add(connectionName);
33
+ connectionNames.push(connectionName);
34
+ }
35
+ return connectionNames;
36
+ }
37
+ function invalidateFailedAclGrants(result, failedConnectionNames) {
38
+ if (failedConnectionNames.size === 0) return;
39
+ for (const requirement of result.connectionRequirements) {
40
+ const selected = requirement.dataSources.map(dataSource => result.connectionRefs[dataSource]).find(Boolean);
41
+ if (!selected || !failedConnectionNames.has(selected.connectionName)) continue;
42
+ for (const dataSource of requirement.dataSources) {
43
+ const reference = result.connectionRefs[dataSource];
44
+ if (reference) reference.connectionName = '';
45
+ }
46
+ if (!result.missingConnectionReferences.some(item => item.localRefId === requirement.localRefId)) {
47
+ result.missingConnectionReferences.push(requirement);
48
+ }
49
+ if (!result.missingConnectors.includes(requirement.apiId)) {
50
+ result.missingConnectors.push(requirement.apiId);
51
+ }
52
+ }
53
+ }
54
+ function selectLaunchConnection(result, localRefId, connectionId) {
55
+ const requirement = result.connectionRequirements.find(item => item.localRefId === localRefId);
56
+ const candidate = result.connectionCandidates[localRefId]?.find(item => (0, _disambiguate.connectionIdsEqual)(item.id, connectionId));
57
+ if (!requirement || !candidate) return false;
58
+ for (const dataSource of requirement.dataSources) {
59
+ const reference = result.connectionRefs[dataSource];
60
+ if (reference) reference.connectionName = candidate.id;
61
+ }
62
+ result.missingConnectionReferences = result.missingConnectionReferences.filter(item => item.localRefId !== localRefId);
63
+ if (!result.missingConnectionReferences.some(item => item.apiId === requirement.apiId)) {
64
+ result.missingConnectors = result.missingConnectors.filter(apiId => apiId !== requirement.apiId);
65
+ }
66
+ return true;
67
+ }
68
+ function getUnregisteredConnectionRequirements(result, usages = result.registeredConnectionUsages) {
69
+ return result.connectionRequirements.filter(requirement => {
70
+ const selectedConnectionId = requirement.dataSources.map(dataSource => result.connectionRefs[dataSource]?.connectionName).find(Boolean);
71
+ if (!selectedConnectionId) return true;
72
+ return !usages.some(usage => usage.reference.toLowerCase() === requirement.localRefId.toLowerCase() && (0, _types.normalizeApiId)(usage.apiId) === requirement.apiId && (0, _disambiguate.connectionIdsEqual)(usage.connectionId, selectedConnectionId) && (usage.appActions.length === 0 || (requirement.appActions ?? []).every(action => usage.appActions.includes(action))));
73
+ });
74
+ }
75
+ function applyRegisteredConnectionSelections(result, usages) {
76
+ for (const requirement of result.connectionRequirements) {
77
+ const usage = usages.find(item => item.reference.toLowerCase() === requirement.localRefId.toLowerCase() && (0, _types.normalizeApiId)(item.apiId) === requirement.apiId);
78
+ if (!usage) continue;
79
+ const candidate = result.connectionCandidates[requirement.localRefId]?.find(item => (0, _disambiguate.connectionIdsEqual)(item.id, usage.connectionId));
80
+ if (candidate) selectLaunchConnection(result, requirement.localRefId, candidate.id);
81
+ }
82
+ }
83
+ function hasRegisteredConnectionConsent(result, usages = result.registeredConnectionUsages) {
84
+ return getUnregisteredConnectionRequirements(result, usages).length === 0;
85
+ }
86
+ function applyRegisteredConnectionUsages(result, usages) {
87
+ applyRegisteredConnectionSelections(result, usages);
88
+ result.registeredConnectionUsages = usages;
89
+ result.requiresConnectionConsent = getUnregisteredConnectionRequirements(result, usages).length > 0;
90
+ }
91
+ function shouldPersistConnectionUsages(runningInDevPlayer, appId) {
92
+ return !runningInDevPlayer && Boolean(appId);
93
+ }
94
+ function shouldShowConnectionReview(runningInDevPlayer, requiresConnectionConsent, hasResourceConsents) {
95
+ return !runningInDevPlayer && (requiresConnectionConsent || hasResourceConsents);
96
+ }
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.connectionIdsEqual = connectionIdsEqual;
6
7
  exports.disambiguateConnections = disambiguateConnections;
7
8
  exports.extractConnectionId = extractConnectionId;
8
9
  var _types = require("./types.js");
@@ -35,11 +36,20 @@ var _types = require("./types.js");
35
36
  function disambiguateConnections(connectionReferences, allConnectionsByApiId) {
36
37
  const resolutions = [];
37
38
  for (const [localRefId, ref] of Object.entries(connectionReferences)) {
38
- const apiId = ref.id;
39
- const all = allConnectionsByApiId[apiId] ?? [];
39
+ const apiId = (0, _types.normalizeApiId)(ref.id);
40
+ const all = allConnectionsByApiId[apiId] ?? allConnectionsByApiId[ref.id] ?? [];
40
41
  const connected = all.filter(c => c.status === _types.ConnectionStatus.Connected);
41
42
  let result;
42
- if (connected.length === 1) {
43
+ if (ref.sharedConnectionId) {
44
+ const sharedId = extractConnectionId(ref.sharedConnectionId);
45
+ const shared = connected.find(connection => connectionIdsEqual(connection.id, sharedId));
46
+ result = shared ? {
47
+ kind: 'resolved',
48
+ connectionId: shared.id
49
+ } : {
50
+ kind: 'missing'
51
+ };
52
+ } else if (connected.length === 1) {
43
53
  result = {
44
54
  kind: 'resolved',
45
55
  connectionId: connected[0].id
@@ -73,16 +83,14 @@ function disambiguateConnections(connectionReferences, allConnectionsByApiId) {
73
83
  * "abc12345def6789012345678901234ab" (bare UUID, no dashes)
74
84
  * "abc12345-def6-7890-1234-5678901234ab" (UUID with dashes)
75
85
  *
76
- * Returns the 32-char hex ID (dashes stripped) or the raw string if no UUID
77
- * pattern is found.
86
+ * Returns the terminal resource name exactly as supplied by Power Platform.
87
+ * Some connectors require dashed GUIDs while others use compact IDs.
78
88
  */
79
89
  function extractConnectionId(rawName) {
80
- // Match a standard UUID (with or without dashes) at the end of the string.
81
- const match = rawName.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i);
82
- if (match) {
83
- return match[1].replace(/-/g, '');
84
- }
85
- // Some connections use a 32-char hex ID without dashes, after a slash.
86
90
  const slashIdx = rawName.lastIndexOf('/');
87
91
  return slashIdx >= 0 ? rawName.slice(slashIdx + 1) : rawName;
92
+ }
93
+ function connectionIdsEqual(left, right) {
94
+ const normalize = value => extractConnectionId(value).replace(/-/g, '').toLowerCase();
95
+ return normalize(left) === normalize(right);
88
96
  }
@@ -0,0 +1,348 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.applyAclGrants = applyAclGrants;
7
+ exports.resolveViaStaticRefs = resolveViaStaticRefs;
8
+ var _PowerPlatformConnectionApi = require("./PowerPlatformConnectionApi.js");
9
+ var _connectionState = require("./connectionState.js");
10
+ var _PowerPlatformEnvironmentApi = require("../platform/PowerPlatformEnvironmentApi.js");
11
+ var _DataverseGlobalDiscoveryApi = require("../platform/DataverseGlobalDiscoveryApi.js");
12
+ var _disambiguate = require("../connections/disambiguate.js");
13
+ var _types = require("./types.js");
14
+ var _index = require("../telemetry/index.js");
15
+ /*!
16
+ * power-apps-native-host — resolveConnections
17
+ *
18
+ * Pure async helpers for the two connection-resolution strategies used by
19
+ * useConnectionRefs:
20
+ *
21
+ * • resolveViaStaticRefs — per-connector resolution using the static
22
+ * connectionReferences map from power.config.json. Used only by Dev Player
23
+ * for unpublished local apps. Handles pre-disambiguation ACL grants so
24
+ * inaccessible connections are excluded before the user is asked to pick.
25
+ *
26
+ * • applyAclGrants — post-resolution user consent used by the fast path
27
+ * (production APKs via fetchAppLaunchData).
28
+ *
29
+ * Both helpers return / consume the shared AppLaunchResult shape so the
30
+ * hook orchestrator can handle them uniformly.
31
+ */
32
+
33
+ const API_VERSION = '1';
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // ConnectorSchema
37
+ // ---------------------------------------------------------------------------
38
+
39
+ /**
40
+ * Connector schema entry generated by `generate-connector-schemas`.
41
+ * Keyed by data source name in the `schemaMap` passed to `useConnectionRefs`.
42
+ */
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // resolveViaStaticRefs
46
+ // ---------------------------------------------------------------------------
47
+
48
+ /**
49
+ * Resolve connections from the static `connectionReferences` map.
50
+ *
51
+ * Steps:
52
+ * 1. Build an apiId → schema lookup.
53
+ * 2. In parallel: fetch the Dataverse instanceUrl and all connection instances
54
+ * for every connector.
55
+ * 3. If `isvClientId` is set, run pre-disambiguation user consent — connections
56
+ * where the grant fails are marked `NeedsAclGrant` and excluded from
57
+ * resolution, prompting the user to create a new connection.
58
+ * 4. Disambiguate: resolve single matches, invoke callbacks for ambiguous /
59
+ * missing cases, perform one-shot retries when `onMissingConnection` returns
60
+ * `true`.
61
+ * 5. Build and return an `AppLaunchResult`-shaped object.
62
+ */
63
+ async function resolveViaStaticRefs({
64
+ connectionReferences,
65
+ hasDataverse,
66
+ schemaMap,
67
+ environmentId,
68
+ region,
69
+ acquireToken,
70
+ token,
71
+ isvClientId,
72
+ onDisambiguationNeeded,
73
+ onMissingConnection
74
+ }) {
75
+ const directConnectionReferences = Object.fromEntries(Object.entries(connectionReferences).filter(([, reference]) => !(0, _types.isCompositeConnectionApiId)(reference.id)));
76
+ const flowConnectionReferences = Object.fromEntries(Object.entries(connectionReferences).filter(([, reference]) => (0, _types.getConnectorId)(reference.id) === 'shared_logicflows'));
77
+ // Build apiId → schema lookup from the static refs.
78
+ const schemaByApiId = Object.fromEntries(Object.values(directConnectionReferences).map(ref => {
79
+ const schema = ref.dataSources.map(ds => schemaMap[ds]).find(Boolean);
80
+ return schema ? [(0, _types.normalizeApiId)(ref.id), schema] : null;
81
+ }).filter(entry => entry !== null));
82
+ await Promise.all(Object.values({
83
+ ...directConnectionReferences,
84
+ ...flowConnectionReferences
85
+ }).map(async reference => {
86
+ const apiId = (0, _types.normalizeApiId)(reference.id);
87
+ if (schemaByApiId[apiId]) return;
88
+ const metadata = await (0, _PowerPlatformConnectionApi.fetchConnectorMetadata)((0, _types.getConnectorId)(reference.id), environmentId, region, token);
89
+ if (!metadata?.primaryRuntimeUrl) return;
90
+ schemaByApiId[apiId] = {
91
+ name: metadata.name,
92
+ properties: {
93
+ primaryRuntimeUrl: metadata.primaryRuntimeUrl,
94
+ scopes: metadata.scopes
95
+ }
96
+ };
97
+ }));
98
+ const base = (0, _PowerPlatformEnvironmentApi.buildEnvironmentEndpoint)(environmentId, region);
99
+ const headers = {
100
+ Authorization: `Bearer ${token}`
101
+ };
102
+
103
+ // Fetch the Dataverse instanceUrl only when the app uses Dataverse.
104
+ const [instanceUrl, ...connResults] = await Promise.all([hasDataverse ? (0, _DataverseGlobalDiscoveryApi.fetchInstanceUrl)(environmentId, region, acquireToken, token) : Promise.resolve(undefined), ...Object.entries(directConnectionReferences).map(async ([localRefId, ref]) => {
105
+ const apiId = (0, _types.normalizeApiId)(ref.id);
106
+ const schema = schemaByApiId[apiId];
107
+ if (!schema) {
108
+ return {
109
+ localRefId,
110
+ apiId,
111
+ dataSources: ref.dataSources,
112
+ instances: []
113
+ };
114
+ }
115
+ const url = `${base}/connectivity/connectors/${schema.name}/connections` + `?api-version=${API_VERSION}&$filter=environment eq '${environmentId}'`;
116
+ try {
117
+ const res = await _index.platformHttpClient.request(url, {
118
+ headers
119
+ });
120
+ if (!res.ok) {
121
+ return {
122
+ localRefId,
123
+ apiId,
124
+ dataSources: ref.dataSources,
125
+ instances: []
126
+ };
127
+ }
128
+ const body = await res.json();
129
+ const instances = (body.value ?? []).map(c => {
130
+ const connected = c.properties?.statuses?.[0]?.status === 'Connected';
131
+ return {
132
+ id: (0, _disambiguate.extractConnectionId)(c.name),
133
+ name: c.name,
134
+ apiId,
135
+ ownerId: c.properties?.createdBy?.id,
136
+ status: connected ? _types.ConnectionStatus.Connected : _types.ConnectionStatus.NotConnected,
137
+ displayName: c.properties?.displayName,
138
+ isAuthenticated: connected
139
+ };
140
+ });
141
+ return {
142
+ localRefId,
143
+ apiId,
144
+ dataSources: ref.dataSources,
145
+ instances
146
+ };
147
+ } catch {
148
+ return {
149
+ localRefId,
150
+ apiId,
151
+ dataSources: ref.dataSources,
152
+ instances: []
153
+ };
154
+ }
155
+ })]);
156
+
157
+ // Pre-disambiguation delegated consent grants (ISV apps only).
158
+ //
159
+ // Connections where the grant fails are marked NeedsAclGrant so
160
+ // disambiguation treats them as unavailable and surfaces the
161
+ // "missing connection" flow, steering the user to create a new one. Fixed
162
+ if (isvClientId) {
163
+ const consentToken = await acquireToken([(0, _PowerPlatformEnvironmentApi.connectionUserConsentScope)(region)]);
164
+ if (consentToken) {
165
+ const attemptedGrants = new Set();
166
+ await Promise.all(connResults.map(async r => {
167
+ const reference = directConnectionReferences[r.localRefId];
168
+ if (!reference || !(0, _connectionState.shouldGrantConnectionUserConsent)(reference, true)) return;
169
+ const connId = schemaByApiId[r.apiId]?.name;
170
+ if (!connId) return;
171
+ await Promise.all(r.instances.map(async inst => {
172
+ if (inst.status !== _types.ConnectionStatus.Connected) return;
173
+ if (reference?.sharedConnectionId && !(0, _disambiguate.connectionIdsEqual)(inst.id, reference.sharedConnectionId)) return;
174
+ const grantKey = `${connId.toLowerCase()}/${inst.id.toLowerCase()}`;
175
+ if (attemptedGrants.has(grantKey)) return;
176
+ attemptedGrants.add(grantKey);
177
+ const ok = await (0, _PowerPlatformConnectionApi.grantConnectionUserConsent)(connId, inst.id, environmentId, region, consentToken);
178
+ if (!ok) inst.status = _types.ConnectionStatus.NeedsAclGrant;
179
+ }));
180
+ }));
181
+ } else {
182
+ for (const result of connResults) {
183
+ for (const instance of result.instances) {
184
+ if (instance.status === _types.ConnectionStatus.Connected) {
185
+ instance.status = _types.ConnectionStatus.NeedsAclGrant;
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ // Disambiguation.
193
+ const allConnectionsByApiId = {};
194
+ for (const r of connResults) {
195
+ allConnectionsByApiId[r.apiId] = r.instances;
196
+ }
197
+ const disambiguationResults = (0, _disambiguate.disambiguateConnections)(directConnectionReferences, allConnectionsByApiId);
198
+ const resolvedIds = {};
199
+ const unresolvedApiIds = [];
200
+ for (const dr of disambiguationResults) {
201
+ if (dr.result.kind === 'resolved') {
202
+ resolvedIds[dr.localRefId] = dr.result.connectionId;
203
+ } else if (dr.result.kind === 'ambiguous') {
204
+ const picked = onDisambiguationNeeded ? await onDisambiguationNeeded({
205
+ localRefId: dr.localRefId,
206
+ apiId: dr.apiId,
207
+ candidates: dr.result.candidates
208
+ }) : null;
209
+ if (picked) {
210
+ resolvedIds[dr.localRefId] = picked;
211
+ } else {
212
+ resolvedIds[dr.localRefId] = '';
213
+ unresolvedApiIds.push((0, _types.normalizeApiId)(dr.apiId));
214
+ }
215
+ } else {
216
+ // kind === 'missing'
217
+ let retried = false;
218
+ if (onMissingConnection) {
219
+ retried = await onMissingConnection({
220
+ localRefId: dr.localRefId,
221
+ apiId: dr.apiId
222
+ });
223
+ }
224
+ if (retried) {
225
+ // One-shot retry for this specific connector.
226
+ const schema = schemaByApiId[dr.apiId];
227
+ if (schema) {
228
+ try {
229
+ const url = `${base}/connectivity/connectors/${schema.name}/connections` + `?api-version=${API_VERSION}&$filter=environment eq '${environmentId}'`;
230
+ const res = await _index.platformHttpClient.request(url, {
231
+ headers
232
+ });
233
+ if (res.ok) {
234
+ const body = await res.json();
235
+ const retryInstances = (body.value ?? []).map(c => {
236
+ const connected = c.properties?.statuses?.[0]?.status === 'Connected';
237
+ return {
238
+ id: (0, _disambiguate.extractConnectionId)(c.name),
239
+ name: c.name,
240
+ apiId: dr.apiId,
241
+ status: connected ? _types.ConnectionStatus.Connected : _types.ConnectionStatus.NotConnected,
242
+ isAuthenticated: connected
243
+ };
244
+ });
245
+ const connected = retryInstances.find(i => i.status === _types.ConnectionStatus.Connected);
246
+ if (connected) {
247
+ resolvedIds[dr.localRefId] = connected.id;
248
+ continue;
249
+ }
250
+ }
251
+ } catch {
252
+ // Fall through to missing.
253
+ }
254
+ }
255
+ }
256
+ resolvedIds[dr.localRefId] = '';
257
+ unresolvedApiIds.push((0, _types.normalizeApiId)(dr.apiId));
258
+ }
259
+ }
260
+ const dataverseOrgUrl = (instanceUrl ?? '').replace(/\/$/, '');
261
+
262
+ // Build the final AppConnectionReference map.
263
+ const connectionRefs = {};
264
+ for (const [localRefId, ref] of Object.entries(directConnectionReferences)) {
265
+ const normalizedApiId = (0, _types.normalizeApiId)(ref.id);
266
+ const schema = schemaByApiId[normalizedApiId];
267
+ const connectionName = resolvedIds[localRefId] ?? '';
268
+ const isDataverse = ref.id.includes('commondataserviceforapps');
269
+ const entry = {
270
+ apiId: normalizedApiId,
271
+ runtimeUrl: schema?.properties.primaryRuntimeUrl ?? '',
272
+ connectionName,
273
+ datasetName: isDataverse ? dataverseOrgUrl : (0, _types.getConnectionDatasetName)(ref)
274
+ };
275
+ for (const ds of ref.dataSources) {
276
+ connectionRefs[ds] = entry;
277
+ }
278
+ }
279
+ for (const ref of Object.values(flowConnectionReferences)) {
280
+ const normalizedApiId = (0, _types.normalizeApiId)(ref.id);
281
+ const schema = schemaByApiId[normalizedApiId];
282
+ const entry = {
283
+ apiId: normalizedApiId,
284
+ runtimeUrl: schema?.properties.primaryRuntimeUrl ?? '',
285
+ connectionName: (0, _types.getFlowConnectionName)(ref),
286
+ datasetName: ''
287
+ };
288
+ for (const dataSource of (0, _types.getFlowDataSourceNames)(ref)) {
289
+ connectionRefs[dataSource] = entry;
290
+ }
291
+ }
292
+ const scopesByApiId = Object.fromEntries(Object.entries(schemaByApiId).map(([apiId, schema]) => [apiId, {
293
+ will: schema.properties.scopes?.will ?? [],
294
+ wont: schema.properties.scopes?.wont ?? []
295
+ }]));
296
+ const requirements = (0, _types.buildConnectionRequirements)(connectionReferences, scopesByApiId);
297
+ const requirementsByLocalRefId = Object.fromEntries(requirements.map(requirement => [requirement.localRefId, requirement]));
298
+ const missingConnectionReferences = Object.entries(resolvedIds).filter(([, connectionId]) => !connectionId).map(([localRefId]) => requirementsByLocalRefId[localRefId]).filter(requirement => Boolean(requirement));
299
+ const connectionCandidates = Object.fromEntries(connResults.map(result => [result.localRefId, result.instances]));
300
+ return {
301
+ connectionRefs,
302
+ dataverseOrgUrl,
303
+ databaseReferences: {},
304
+ missingConnectors: [...new Set(unresolvedApiIds)],
305
+ missingConnectionReferences,
306
+ connectionRequirements: Object.values(requirementsByLocalRefId),
307
+ connectionCandidates,
308
+ resourceConsents: [],
309
+ configurationErrors: (0, _types.findConnectionDataSourceConflicts)(requirements).map(dataSource => `Data source "${dataSource}" is assigned to multiple connection references.`),
310
+ registeredConnectionUsages: [],
311
+ requiresConnectionConsent: requirements.length > 0
312
+ };
313
+ }
314
+
315
+ // ---------------------------------------------------------------------------
316
+ // applyAclGrants
317
+ // ---------------------------------------------------------------------------
318
+
319
+ /**
320
+ * Run idempotent post-resolution delegated consent grants for the calling app.
321
+ *
322
+ * Used by the fast path (production APKs via `fetchAppLaunchData`) where the
323
+ * launch API has already resolved the user's connections. Grants are applied
324
+ * only to user-selected connections; fixed shared connections are already
325
+ * authorized by their owner and must not receive delegated user consent.
326
+ *
327
+ * For the slow path the grants run before disambiguation inside
328
+ * `resolveViaStaticRefs`, where they can still influence which connections
329
+ * are considered available.
330
+ *
331
+ * Returns the connection names whose ACL grants failed so callers can fail closed.
332
+ */
333
+ async function applyAclGrants(connectionRefs, connectionRequirements, environmentId, region, acquireToken) {
334
+ const connectionNames = (0, _connectionState.getConnectionNamesRequiringAclGrant)(connectionRefs, connectionRequirements);
335
+ if (connectionNames.length === 0) return new Set();
336
+ const connectionNamesSet = new Set(connectionNames);
337
+ const consentToken = await acquireToken([(0, _PowerPlatformEnvironmentApi.connectionUserConsentScope)(region)]);
338
+ if (!consentToken) return new Set(connectionNames);
339
+ const seen = new Set();
340
+ const failed = new Set();
341
+ await Promise.all(Object.values(connectionRefs).map(async ref => {
342
+ if (!ref.connectionName || !connectionNamesSet.has(ref.connectionName) || seen.has(ref.connectionName)) return;
343
+ seen.add(ref.connectionName);
344
+ const granted = await (0, _PowerPlatformConnectionApi.grantConnectionUserConsent)(ref.apiId, ref.connectionName, environmentId, region, consentToken);
345
+ if (!granted) failed.add(ref.connectionName);
346
+ }));
347
+ return failed;
348
+ }