@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
@@ -28,11 +28,12 @@ import React from 'react';
28
28
  import { createTamagui } from '@tamagui/core';
29
29
  import type { AuthProviderProps } from '../auth/AuthContext';
30
30
  import type { PowerAppsHostProviderProps } from './PowerAppsHostProvider';
31
- import type { ConnectorSchema } from '../platform/useConnectionRefs.js';
32
- import type { DatabaseReference } from '../platform/PowerPlatformEnvironmentApi.js';
33
- import type { OnDisambiguationNeeded } from '../connections/types.js';
31
+ import type { ConnectorSchema } from '../connections/useConnectionRefs.js';
32
+ import type { DatabaseReference } from '../platform/DataverseDataSources.js';
33
+ import type { ConnectionReferenceConfig, OnDisambiguationNeeded } from '../connections/types.js';
34
34
  import type { ThemeTokens } from '../theme/tokens.js';
35
35
  import type { ILogger } from '@microsoft/power-apps-native-common';
36
+ import type { ApplicationInsightsTelemetryProviderConfig } from '@microsoft/power-apps-native-common';
36
37
  /** Shape of power.config.json consumed by the provider. */
37
38
  export interface PowerConfig {
38
39
  /**
@@ -53,10 +54,7 @@ export interface PowerConfig {
53
54
  * Omit for the default global collector.
54
55
  */
55
56
  geoRegion?: string;
56
- connectionReferences?: Record<string, {
57
- id: string;
58
- dataSources: string[];
59
- }>;
57
+ connectionReferences?: Record<string, ConnectionReferenceConfig>;
60
58
  databaseReferences?: Record<string, DatabaseReference>;
61
59
  /**
62
60
  * Allow additional keys — the offline SDK reads arbitrary fields from
@@ -67,7 +65,29 @@ export interface PowerConfig {
67
65
  [key: string]: unknown;
68
66
  }
69
67
  type TamaguiConfig = ReturnType<typeof createTamagui>;
68
+ export interface AppInsightsConfig extends Omit<ApplicationInsightsTelemetryProviderConfig, 'deviceId' | 'sessionId'> {
69
+ /** Explicit opt-in. Application Insights telemetry is disabled unless this is true. */
70
+ enabled: boolean;
71
+ }
72
+ export interface PowerAppsAppConfig {
73
+ expo?: {
74
+ extra?: {
75
+ appInsightsConfig?: unknown;
76
+ [key: string]: unknown;
77
+ };
78
+ [key: string]: unknown;
79
+ };
80
+ [key: string]: unknown;
81
+ }
70
82
  export interface PowerAppsProviderProps {
83
+ /**
84
+ * Contents of the generated app.json.
85
+ *
86
+ * Pass this when the app runs inside a host such as Dev Player, where
87
+ * `Constants.expoConfig` describes the host instead of the loaded app.
88
+ * The provider falls back to `Constants.expoConfig` when omitted.
89
+ */
90
+ appConfig?: PowerAppsAppConfig;
71
91
  /** MSAL configuration from auth.config.json (msal section). */
72
92
  msalConfig: AuthProviderProps['msalConfig'];
73
93
  /** Intune MAM configuration. Defaults to enabled in release builds. */
@@ -106,6 +126,12 @@ export interface PowerAppsProviderProps {
106
126
  renderOAuthWebView?: PowerAppsHostProviderProps['renderOAuthWebView'];
107
127
  /** See PowerAppsHostProviderProps.onDisambiguationNeeded. */
108
128
  onDisambiguationNeeded?: OnDisambiguationNeeded;
129
+ /** See PowerAppsHostProviderProps.onConnectionConsent. */
130
+ onConnectionConsent?: PowerAppsHostProviderProps['onConnectionConsent'];
131
+ /** See PowerAppsHostProviderProps.onResourceConsent. */
132
+ onResourceConsent?: PowerAppsHostProviderProps['onResourceConsent'];
133
+ /** See PowerAppsHostProviderProps.onConnectionConsentDeclined. */
134
+ onConnectionConsentDeclined?: PowerAppsHostProviderProps['onConnectionConsentDeclined'];
109
135
  /**
110
136
  * App name used as `clientType` in the auto-wired 1DS telemetry provider.
111
137
  * Appears in Kusto as the `clientType` column — use a stable
@@ -125,6 +151,25 @@ export interface PowerAppsProviderProps {
125
151
  * logger becomes a no-op regardless of build variant.
126
152
  */
127
153
  logger?: ILogger | null;
154
+ /**
155
+ * Show app and telemetry session details after a three-finger long press.
156
+ * Available in both development and published native apps. @default true
157
+ */
158
+ showSessionDetailsOnLongPress?: boolean;
159
+ /**
160
+ * @deprecated Use `showSessionDetailsOnLongPress` instead.
161
+ */
162
+ showSessionDetailsOnSwipe?: boolean;
163
+ /**
164
+ * Optional customer-owned Application Insights destination.
165
+ *
166
+ * Events are sent directly to the selected C1-owned Application Insights
167
+ * resource and fan out alongside the default Microsoft OneDS provider.
168
+ * Defaults to `appConfig.expo.extra.appInsightsConfig`, then falls back to
169
+ * `Constants.expoConfig.extra.appInsightsConfig`.
170
+ * Ignored when `logger` is provided or `null`.
171
+ */
172
+ appInsightsConfig?: AppInsightsConfig;
128
173
  /**
129
174
  * Offline profile JSON — optional override.
130
175
  * Primary source: SDK config plugin embeds offline-profile.json into the app
@@ -142,7 +187,7 @@ export interface PowerAppsProviderProps {
142
187
  * component, eliminating all provider boilerplate from the consuming app's
143
188
  * root layout.
144
189
  */
145
- export declare function PowerAppsProvider({ msalConfig, intuneConfig, powerConfig, schemaMap, tamaguiConfig, // override with app's tamagui.config.ts
146
- defaultTheme, theme, darkTheme, followSystemTheme, renderOAuthWebView, onDisambiguationNeeded, appName, logger: loggerProp, offlineProfile, children, }: PowerAppsProviderProps): React.JSX.Element;
190
+ export declare function PowerAppsProvider({ appConfig, msalConfig, intuneConfig, powerConfig, schemaMap, tamaguiConfig, // override with app's tamagui.config.ts
191
+ defaultTheme, theme, darkTheme, followSystemTheme, renderOAuthWebView, onDisambiguationNeeded, onConnectionConsent, onResourceConsent, onConnectionConsentDeclined, appName, logger: loggerProp, showSessionDetailsOnLongPress, showSessionDetailsOnSwipe, appInsightsConfig, offlineProfile, children, }: PowerAppsProviderProps): React.JSX.Element;
147
192
  export {};
148
193
  //# sourceMappingURL=PowerAppsProvider.d.ts.map
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ interface SessionDetailsGestureProps {
3
+ enabled: boolean;
4
+ appName: string;
5
+ appId?: string;
6
+ environmentId?: string;
7
+ region: string;
8
+ appVersion?: string;
9
+ children: React.ReactNode;
10
+ }
11
+ export declare function SessionDetailsGesture({ enabled, appName, appId, environmentId, region, appVersion, children, }: SessionDetailsGestureProps): React.JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=SessionDetailsGesture.d.ts.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Forwards the privacy-safe Offline SDK transport through the host's existing
3
+ * app logger so offline events carry the same session, device, and user data.
4
+ *
5
+ * The host owns exactly one thing here: binding the SDK transport to this
6
+ * host's logger. Buffering and teardown semantics live in the SDK.
7
+ *
8
+ * Setup and teardown are fail-open but not silent. This runs from an effect in
9
+ * the provider that roots the app, so an unguarded throw would take down the
10
+ * whole tree — telemetry is never worth an app that will not start. An
11
+ * unexpected failure is therefore downgraded to a logged diagnostic rather than
12
+ * swallowed, so broken wiring stays observable.
13
+ */
14
+ export declare function configureOfflineSdkTelemetry(forwardEvents: boolean): () => void;
15
+ //# sourceMappingURL=OfflineSdkTelemetry.d.ts.map
@@ -0,0 +1,2 @@
1
+ export declare function configureOfflineSdkTelemetry(_forwardEvents: boolean): () => void;
2
+ //# sourceMappingURL=OfflineSdkTelemetry.web.d.ts.map
@@ -7,7 +7,9 @@
7
7
  * (`getAppLogger` / `configureAppLogger`). This module re-exports those
8
8
  * alongside the canonical host operation-name constants.
9
9
  */
10
- export { getAppLogger, configureAppLogger } from '@microsoft/power-apps-native-common';
10
+ import { HttpClient } from '@microsoft/power-apps-native-common/http';
11
+ export { getAppLogger, configureAppLogger, getCustomerTelemetryLogger } from '@microsoft/power-apps-native-common';
12
+ export type { ICustomerTelemetryLogger } from '@microsoft/power-apps-native-common';
11
13
  /**
12
14
  * Canonical operation names for host package telemetry.
13
15
  * Follow the `PowerAppsHost.<Subsystem>.<Action>` convention so events are
@@ -34,15 +36,25 @@ export declare const HostOperationNames: {
34
36
  readonly BridgeTokenAcquireFailed: "PowerAppsHost.Bridge.TokenAcquire.Failed";
35
37
  /** Top-level React error boundary caught an unhandled JS error. */
36
38
  readonly RuntimeUnhandledJsError: "PowerAppsHost.Runtime.UnhandledJsError";
39
+ /** Power Platform RP HTTP request, including backend correlation IDs. */
40
+ readonly PlatformHttpRequest: "PowerAppsHost.Platform.HttpRequest";
37
41
  /** Resolve Dataverse instanceUrl from Power Platform environment API. */
38
42
  readonly PlatformFetchInstanceUrl: "PowerAppsHost.Platform.FetchInstanceUrl";
39
43
  /** useConnectionRefs failed to resolve connections (recoverable). Recorded only on failure. */
40
44
  readonly PlatformLoadConnectionsFailed: "PowerAppsHost.Platform.LoadConnections.Failed";
41
45
  /** Create a new Power Platform connection. */
42
46
  readonly PlatformCreateConnection: "PowerAppsHost.Platform.CreateConnection";
47
+ /** Repair an existing Power Platform connection. */
48
+ readonly PlatformRepairConnection: "PowerAppsHost.Platform.RepairConnection";
49
+ /** Poll a connection until it becomes active. */
50
+ readonly PlatformPollConnectionStatus: "PowerAppsHost.Platform.PollConnectionStatus";
43
51
  /** Resolve app connection references for the current launch. */
44
52
  readonly PlatformResolveConnectionRefs: "PowerAppsHost.Platform.ResolveConnectionRefs";
45
53
  /** Grant this app access to a Power Platform connection ACL. */
46
54
  readonly PlatformConnectionAclGrant: "PowerAppsHost.Platform.ConnectionAclGrant";
55
+ /** Installed Offline SDK predates the SDK-owned routing contract. */
56
+ readonly OfflineRoutingContractUnsupported: "PowerAppsHost.Offline.RoutingContract.Unsupported";
47
57
  };
58
+ export declare const bridgeHttpClient: HttpClient;
59
+ export declare const platformHttpClient: HttpClient;
48
60
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/power-apps-native-host",
3
- "version": "0.2.25",
3
+ "version": "0.3.3",
4
4
  "description": "React Native host integration for @microsoft/power-apps",
5
5
  "author": "Microsoft Corporation",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -12,28 +12,44 @@
12
12
  },
13
13
  "main": "./lib/commonjs/index.js",
14
14
  "module": "./lib/module/index.js",
15
- "types": "./lib/typescript/commonjs/index.d.ts",
15
+ "types": "./src/config/index.d.ts",
16
16
  "exports": {
17
17
  ".": {
18
18
  "browser": {
19
- "types": "./lib/typescript/commonjs/index.d.ts",
20
- "default": "./lib/commonjs/index.js"
19
+ "types": "./src/config/index.d.ts",
20
+ "default": "./lib/module/index.js"
21
21
  },
22
22
  "import": {
23
- "types": "./lib/typescript/module/index.d.ts",
23
+ "types": "./src/config/index.d.ts",
24
24
  "default": "./lib/module/index.js"
25
25
  },
26
26
  "require": {
27
- "types": "./lib/typescript/commonjs/index.d.ts",
27
+ "types": "./src/config/index.d.ts",
28
28
  "default": "./lib/commonjs/index.js"
29
29
  }
30
30
  },
31
31
  "./package.json": "./package.json",
32
+ "./config/tsconfig": "./src/config/tsconfig.json",
33
+ "./config/*": {
34
+ "browser": {
35
+ "types": "./lib/typescript/module/config/*.d.ts",
36
+ "default": "./lib/module/config/*.js"
37
+ },
38
+ "import": {
39
+ "types": "./lib/typescript/module/config/*.d.ts",
40
+ "default": "./lib/module/config/*.js"
41
+ },
42
+ "require": {
43
+ "types": "./lib/typescript/commonjs/config/*.d.ts",
44
+ "default": "./lib/commonjs/config/*.js"
45
+ }
46
+ },
32
47
  "./app.plugin.js": "./app.plugin.js"
33
48
  },
34
49
  "bin": {
35
50
  "generate-connector-schemas": "scripts/generate-connector-schemas.js",
36
51
  "js-bundle": "scripts/bundle.js",
52
+ "build-codegen-package": "scripts/build-codegen-package.js",
37
53
  "build-ios-base": "scripts/build-ios-base.js",
38
54
  "power-apps-web": "scripts/start-web.mjs",
39
55
  "wrap": "scripts/wrap.js",
@@ -42,19 +58,26 @@
42
58
  "files": [
43
59
  "lib/",
44
60
  "!lib/**/*.map",
61
+ "src/config/generated-config.d.ts",
62
+ "src/config/index.d.ts",
63
+ "src/config/tsconfig.json",
45
64
  "app.plugin.js",
46
65
  "scripts/",
47
66
  "compatibility/"
48
67
  ],
49
68
  "scripts": {
50
- "prepack": "npm run test:upgrade && bob build && node ../../scripts/notices.js",
51
- "build": "bob build && node ../../scripts/notices.js",
69
+ "prepack": "npm run build && npm test",
70
+ "build": "bob build && node ../../scripts/package-build/notices.js",
52
71
  "type-check": "tsc --noEmit",
53
- "test:upgrade": "node --test test/upgrade.test.js"
72
+ "test": "node --test --test-force-exit test/*.test.js",
73
+ "test:upgrade": "node --test --test-force-exit test/upgrade.test.js",
74
+ "test:offline-compatibility": "bob build && node --test test/OfflineSdkCompatibility.test.js"
54
75
  },
55
76
  "dependencies": {
56
- "@microsoft/power-apps-native-auth": "^0.3.5",
57
- "@microsoft/power-apps-native-common": "^0.1.8",
77
+ "@expo/config-plugins": "55.0.11",
78
+ "@microsoft/power-apps-native-assets": "^0.3.0",
79
+ "@microsoft/power-apps-native-auth": "^0.3.8",
80
+ "@microsoft/power-apps-native-common": "^0.2.2",
58
81
  "@ster5/global-mutex": "3.3.0",
59
82
  "@tamagui/config": "2.4.5",
60
83
  "@tamagui/core": "2.4.5",
@@ -66,12 +89,20 @@
66
89
  "write-file-atomic": "7.0.1"
67
90
  },
68
91
  "peerDependencies": {
92
+ "@expo/vector-icons": ">=15.1.1 <16",
69
93
  "@microsoft/power-apps": "*",
70
- "@microsoft/power-apps-native-offline": ">=0.1.0",
94
+ "@microsoft/power-apps-native-offline": ">=0.1.32",
71
95
  "@react-native-community/netinfo": ">=11.0.0",
72
96
  "burnt": ">=0.12.2",
97
+ "expo-application": ">=55.0.0 <56",
73
98
  "expo-constants": ">=55.0.0 <56",
99
+ "expo-clipboard": ">=55.0.0 <56",
100
+ "expo-document-picker": ">=55.0.13 <56",
101
+ "expo-file-system": ">=55.0.19 <56",
102
+ "expo-image-picker": ">=55.0.20 <56",
74
103
  "expo-linking": ">=55.0.14 <56",
104
+ "expo-sharing": ">=55.0.18 <56",
105
+ "expo-secure-store": ">=55.0.0 <56",
75
106
  "expo-web-browser": ">=55.0.14 <56",
76
107
  "react": ">=19.2.0 <20",
77
108
  "react-dom": ">=19.2.0 <19.3.0",
@@ -91,9 +122,13 @@
91
122
  }
92
123
  },
93
124
  "devDependencies": {
125
+ "@microsoft/power-apps-cli": "0.15.3",
94
126
  "@types/react": "19.2.14",
95
127
  "cross-env": "7.0.3",
128
+ "expo-application": "55.0.15",
129
+ "expo-clipboard": "55.0.13",
96
130
  "expo-constants": "55.0.16",
131
+ "expo-secure-store": "55.0.13",
97
132
  "expo-web-browser": "55.0.15",
98
133
  "react": "19.2.0",
99
134
  "react-dom": "19.2.0",
@@ -109,10 +144,14 @@
109
144
  "publishConfig": {
110
145
  "access": "public"
111
146
  },
112
- "sideEffects": false,
147
+ "sideEffects": [
148
+ "**/index.js",
149
+ "**/index.ts"
150
+ ],
113
151
  "react-native-builder-bob": {
114
152
  "source": "src",
115
153
  "output": "lib",
154
+ "exclude": "config/{generated-config.d.ts,index.d.ts,tsconfig.json}",
116
155
  "targets": [
117
156
  [
118
157
  "module",
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Codegen native package producer.
4
+ *
5
+ * Produces a *self-consistent* native asset package for the Power Apps wrap
6
+ * pipeline from a single `expo export`, so the embedded Hermes bundle and the
7
+ * hash-addressed asset files share one Metro AssetRegistry (their hashes line
8
+ * up with what the runtime asset resolver sees). No JavaScript is compiled or
9
+ * downloaded inside the wrap pipeline — this step runs at customer deploy time.
10
+ *
11
+ * Usage (via the app's npm scripts):
12
+ * build-codegen-package android
13
+ * build-codegen-package ios
14
+ *
15
+ * Emits into ./dist (deploy uploads the whole dist/ to blob):
16
+ * dist/index.android.bundle.hbc | dist/main.jsbundle.hbc ← Hermes bundle
17
+ * dist/powerapps-customer-assets-<platform>/manifest.json
18
+ * dist/powerapps-customer-assets-<platform>/assets/<fileHash>.<type>
19
+ *
20
+ * Android and iOS asset packages are kept in separate, platform-suffixed dirs
21
+ * because their scales/densities (and therefore fileHashes) differ. The wrap
22
+ * pipeline downloads the platform-matching dir and injects it into the base app
23
+ * under the neutral name `powerapps-customer-assets/`, which the native module
24
+ * reads at runtime.
25
+ *
26
+ * manifest.json shape (schemaVersion 1):
27
+ * {
28
+ * "schemaVersion": 1,
29
+ * "platform": "android" | "ios",
30
+ * "bundle": { "path": "index.android.bundle.hbc" },
31
+ * "assets": [
32
+ * { "fileHash": "<md5>", "type": "png", "path": "assets/<md5>.png",
33
+ * "hash": "<metroRegistryHash>", "scale": 2 }
34
+ * ]
35
+ * }
36
+ *
37
+ * The `hash` (Metro AssetRegistry hash) + `scale` let the native module build a
38
+ * runtime lookup the JS resolver keys by `"<hash>@<scale>"`. Integrity fields
39
+ * (sha256, signing) are intentionally out of scope here (Scope 3).
40
+ */
41
+
42
+ 'use strict';
43
+
44
+ const { execFileSync, spawn } = require('child_process');
45
+ const path = require('path');
46
+ const fs = require('fs');
47
+ const os = require('os');
48
+ const crypto = require('crypto');
49
+
50
+ const platform = process.argv[2];
51
+ if (platform !== 'android' && platform !== 'ios') {
52
+ console.error('Usage: build-codegen-package <android|ios>');
53
+ process.exit(1);
54
+ }
55
+
56
+ const isWin = process.platform === 'win32';
57
+ const isLinux = process.platform === 'linux';
58
+
59
+ const projectRoot = process.cwd();
60
+ const workspaceRoot = path.resolve(projectRoot, '../..');
61
+
62
+ const HERMES_MAGIC = Buffer.from([0xc6, 0x1f, 0xbc, 0x03]);
63
+ // Platform-suffixed in blob so android/ios packages don't clobber each other;
64
+ // the wrap injects into the neutral `powerapps-customer-assets/` on-device.
65
+ const ASSET_DIR_NAME = `powerapps-customer-assets-${platform}`;
66
+ const bundleName =
67
+ platform === 'android' ? 'index.android.bundle.hbc' : 'main.jsbundle.hbc';
68
+
69
+ function resolveExpoBin() {
70
+ const name = isWin ? 'expo.cmd' : 'expo';
71
+ const local = path.join(projectRoot, 'node_modules', '.bin', name);
72
+ const hoisted = path.join(workspaceRoot, 'node_modules', '.bin', name);
73
+ return fs.existsSync(local) ? local : hoisted;
74
+ }
75
+
76
+ function resolveHermesc() {
77
+ const hcPkg = require.resolve('hermes-compiler/package.json', {
78
+ paths: [projectRoot, workspaceRoot],
79
+ });
80
+ const hcDir = path.dirname(hcPkg);
81
+ const binDir = isWin ? 'win64-bin' : isLinux ? 'linux64-bin' : 'osx-bin';
82
+ const binName = isWin ? 'hermesc.exe' : 'hermesc';
83
+ const flat = path.join(hcDir, binDir, binName);
84
+ const nested = path.join(hcDir, binName, binDir, binName);
85
+ return fs.existsSync(flat) ? flat : nested;
86
+ }
87
+
88
+ function findFiles(dir, predicate) {
89
+ if (!fs.existsSync(dir)) return [];
90
+ return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
91
+ const p = path.join(dir, entry.name);
92
+ return entry.isDirectory()
93
+ ? findFiles(p, predicate)
94
+ : predicate(p)
95
+ ? [p]
96
+ : [];
97
+ });
98
+ }
99
+
100
+ function sha256File(filePath) {
101
+ return crypto
102
+ .createHash('sha256')
103
+ .update(fs.readFileSync(filePath))
104
+ .digest('hex');
105
+ }
106
+
107
+ function isHermesBytecode(filePath) {
108
+ const fd = fs.openSync(filePath, 'r');
109
+ try {
110
+ const head = Buffer.alloc(4);
111
+ fs.readSync(fd, head, 0, 4, 0);
112
+ return head.equals(HERMES_MAGIC);
113
+ } finally {
114
+ fs.closeSync(fd);
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Run `expo export` for the native platform and wait for completion. Unlike
120
+ * `expo export:embed`, this yields hash-addressed assets and (with
121
+ * `--dump-assetmap`) an assetmap.json we can turn into a manifest.
122
+ */
123
+ function runExpoExport(expoBin, outputDir) {
124
+ const marker = `Exported: ${outputDir}`;
125
+ return new Promise((resolve, reject) => {
126
+ const child = spawn(
127
+ expoBin,
128
+ [
129
+ 'export',
130
+ '--platform',
131
+ platform,
132
+ '--output-dir',
133
+ outputDir,
134
+ '--dump-assetmap',
135
+ '--clear',
136
+ ],
137
+ {
138
+ cwd: projectRoot,
139
+ env: { ...process.env, CI: process.env.CI ?? '1', EXPO_NO_TELEMETRY: '1' },
140
+ shell: isWin,
141
+ stdio: ['ignore', 'pipe', 'pipe'],
142
+ },
143
+ );
144
+ let tail = '';
145
+ let done = false;
146
+ const forward = (chunk, dest) => {
147
+ dest.write(chunk);
148
+ tail = (tail + chunk).slice(-marker.length * 2);
149
+ if (!done && tail.includes(marker)) {
150
+ done = true;
151
+ if (isWin && child.pid) {
152
+ try {
153
+ execFileSync('taskkill', ['/pid', String(child.pid), '/t', '/f'], {
154
+ stdio: 'ignore',
155
+ });
156
+ } catch {
157
+ child.kill();
158
+ }
159
+ } else {
160
+ child.kill('SIGKILL');
161
+ }
162
+ }
163
+ };
164
+ child.stdout.on('data', (c) => forward(c, process.stdout));
165
+ child.stderr.on('data', (c) => forward(c, process.stderr));
166
+ child.once('error', reject);
167
+ child.once('exit', (code, signal) => {
168
+ if (done) resolve();
169
+ else
170
+ reject(
171
+ new Error(
172
+ code === 0
173
+ ? `Expo export exited before completion marker: ${marker}`
174
+ : `Expo export failed (${signal ? `signal ${signal}` : `code ${code}`})`,
175
+ ),
176
+ );
177
+ });
178
+ });
179
+ }
180
+
181
+ function locateBundle(exportDir) {
182
+ const jsDir = path.join(exportDir, '_expo', 'static', 'js', platform);
183
+ const candidates = findFiles(jsDir, (p) => /\.(hbc|js)$/.test(p));
184
+ const hbc = candidates.find((p) => p.endsWith('.hbc'));
185
+ if (hbc) return { file: hbc, isHermes: true };
186
+ if (candidates.length === 1) return { file: candidates[0], isHermes: false };
187
+ throw new Error(
188
+ `Expected exactly one native bundle under ${jsDir}, found ${candidates.length}`,
189
+ );
190
+ }
191
+
192
+ function buildAssetPackage(exportDir, distDir, bundlePath) {
193
+ const assetMapPath = path.join(exportDir, 'assetmap.json');
194
+ const assetsOutDir = path.join(distDir, ASSET_DIR_NAME, 'assets');
195
+ const manifestPath = path.join(distDir, ASSET_DIR_NAME, 'manifest.json');
196
+
197
+ const manifest = {
198
+ schemaVersion: 1,
199
+ platform,
200
+ // BIN-3: sha256 pins every artifact the wrap pipeline injects. The wrap
201
+ // recomputes these before use, so a truncated or swapped blob fails the
202
+ // build instead of shipping inside a signed app.
203
+ bundle: { path: bundleName, sha256: sha256File(bundlePath) },
204
+ assets: [],
205
+ };
206
+
207
+ // No assets is valid (an app may reference none). Still emit an empty
208
+ // manifest so the wrap injection + native module behave uniformly.
209
+ if (fs.existsSync(assetMapPath)) {
210
+ fs.mkdirSync(assetsOutDir, { recursive: true });
211
+ const assetMap = JSON.parse(fs.readFileSync(assetMapPath, 'utf8'));
212
+ for (const [registryHash, entry] of Object.entries(assetMap)) {
213
+ const type = String(entry.type ?? '').replace(/^\./, '') || 'dat';
214
+ const fileHashes = entry.fileHashes ?? [];
215
+ const scales = entry.scales ?? [];
216
+ for (let i = 0; i < fileHashes.length; i++) {
217
+ const fileHash = fileHashes[i];
218
+ const scale = scales[i] ?? 1;
219
+ const src = path.join(exportDir, 'assets', fileHash);
220
+ if (!fs.existsSync(src)) {
221
+ throw new Error(`Exported asset missing on disk: assets/${fileHash}`);
222
+ }
223
+ const relPath = `assets/${fileHash}.${type}`;
224
+ const destPath = path.join(distDir, ASSET_DIR_NAME, relPath);
225
+ fs.copyFileSync(src, destPath);
226
+ manifest.assets.push({
227
+ fileHash,
228
+ type,
229
+ path: relPath,
230
+ hash: registryHash,
231
+ scale,
232
+ sha256: sha256File(destPath),
233
+ });
234
+ }
235
+ }
236
+ } else {
237
+ fs.mkdirSync(path.join(distDir, ASSET_DIR_NAME), { recursive: true });
238
+ }
239
+
240
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
241
+ return manifest.assets.length;
242
+ }
243
+
244
+ async function main() {
245
+ const distDir = path.join(projectRoot, 'dist');
246
+ fs.mkdirSync(distDir, { recursive: true });
247
+
248
+ const exportDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pa-codegen-export-'));
249
+ try {
250
+ console.log(`\n[package] Step 1: expo export (${platform})`);
251
+ await runExpoExport(resolveExpoBin(), exportDir);
252
+
253
+ console.log('[package] Step 2: locate + stage Hermes bundle');
254
+ const { file, isHermes } = locateBundle(exportDir);
255
+ const outBundle = path.join(distDir, bundleName);
256
+ if (isHermes) {
257
+ fs.copyFileSync(file, outBundle);
258
+ } else {
259
+ console.log('[package] plain JS bundle exported; compiling with hermesc');
260
+ execFileSync(resolveHermesc(), ['-emit-binary', '-out', outBundle, file], {
261
+ stdio: 'inherit',
262
+ });
263
+ }
264
+ if (!isHermesBytecode(outBundle)) {
265
+ throw new Error(`Staged bundle is not Hermes bytecode: ${outBundle}`);
266
+ }
267
+
268
+ console.log('[package] Step 3: build asset package + manifest');
269
+ const count = buildAssetPackage(exportDir, distDir, outBundle);
270
+
271
+ console.log(
272
+ `\n[package] Done. Bundle: "${outBundle}"; assets: ${count}; ` +
273
+ `manifest: "${path.join(distDir, ASSET_DIR_NAME, 'manifest.json')}"`,
274
+ );
275
+ } finally {
276
+ fs.rmSync(exportDir, { recursive: true, force: true });
277
+ }
278
+ }
279
+
280
+ main().catch((error) => {
281
+ console.error(error);
282
+ process.exit(1);
283
+ });
@@ -7,7 +7,9 @@ import process from 'node:process';
7
7
 
8
8
  const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
9
9
  const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';
10
- const localAppUrl = 'http://localhost:8082';
10
+ const localAppPort = process.env.POWER_APPS_WEB_APP_PORT?.trim() || '8082';
11
+ const playerPort = process.env.POWER_APPS_WEB_PLAYER_PORT?.trim() || '8080';
12
+ const localAppUrl = `http://localhost:${localAppPort}`;
11
13
  const children = new Set();
12
14
  let shuttingDown = false;
13
15
 
@@ -106,7 +108,9 @@ function captureLocalPlayUrl(child, environmentId) {
106
108
  child.stdout.removeAllListeners('data');
107
109
  child.stdout.pipe(process.stdout);
108
110
  void waitForApp()
109
- .then(() => openBrowser(playUrl.href))
111
+ .then(() => {
112
+ if (process.env.POWER_APPS_WEB_NO_OPEN !== 'true') openBrowser(playUrl.href);
113
+ })
110
114
  .catch((error) => {
111
115
  console.error(error instanceof Error ? error.message : String(error));
112
116
  shutdown(1);
@@ -128,10 +132,10 @@ const schemaProcess = spawn(npmCommand, ['run', 'generate-schemas'], {
128
132
  const schemaExitCode = await new Promise((resolve) => schemaProcess.once('exit', resolve));
129
133
  if (schemaExitCode !== 0) process.exit(schemaExitCode ?? 1);
130
134
 
131
- run(npxCommand, ['expo', 'start', '--port', '8082', '--localhost']);
135
+ run(npxCommand, ['expo', 'start', '--port', localAppPort, '--localhost']);
132
136
  const powerApps = run(
133
137
  npxCommand,
134
- ['power-apps', 'run', '--port', '8080', '--local-app-url', localAppUrl],
138
+ ['pa', 'app', 'run', '--config-only', '--port', playerPort, '--local-app-url', localAppUrl],
135
139
  { stdio: ['inherit', 'pipe', 'inherit'] }
136
140
  );
137
- captureLocalPlayUrl(powerApps, powerConfig.environmentId);
141
+ captureLocalPlayUrl(powerApps, powerConfig.environmentId);