@microsoft/power-apps-native-host 0.2.22
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.
- package/LICENSE +112 -0
- package/README.md +59 -0
- package/app.plugin.js +4 -0
- package/lib/NOTICE.txt +1659 -0
- package/lib/commonjs/ErrorBoundary.js +128 -0
- package/lib/commonjs/auth/AuthContext.js +281 -0
- package/lib/commonjs/bridge/NativePowerAppsBridge.js +279 -0
- package/lib/commonjs/bridge/setupNativeHost.js +65 -0
- package/lib/commonjs/connections/ConnectionSetupScreen.js +531 -0
- package/lib/commonjs/connections/disambiguate.js +88 -0
- package/lib/commonjs/connections/types.js +58 -0
- package/lib/commonjs/connections/useConnectionSetup.js +236 -0
- package/lib/commonjs/controls/FilePicker.js +183 -0
- package/lib/commonjs/controls/ImagePicker.js +236 -0
- package/lib/commonjs/index.js +246 -0
- package/lib/commonjs/logger.js +27 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/platform/PowerPlatformConnectionApi.js +606 -0
- package/lib/commonjs/platform/PowerPlatformEnvironmentApi.js +194 -0
- package/lib/commonjs/platform/resolveConnections.js +264 -0
- package/lib/commonjs/platform/useConnectionRefs.js +270 -0
- package/lib/commonjs/provider/PowerAppsHostProvider.js +336 -0
- package/lib/commonjs/provider/PowerAppsProvider.js +351 -0
- package/lib/commonjs/telemetry/index.js +70 -0
- package/lib/commonjs/theme/ThemeProvider.js +80 -0
- package/lib/commonjs/theme/tokens.js +50 -0
- package/lib/commonjs/utils/dataverseUtils.js +98 -0
- package/lib/commonjs/utils/isDevPlayer.js +27 -0
- package/lib/module/ErrorBoundary.js +122 -0
- package/lib/module/auth/AuthContext.js +276 -0
- package/lib/module/bridge/NativePowerAppsBridge.js +273 -0
- package/lib/module/bridge/setupNativeHost.js +62 -0
- package/lib/module/connections/ConnectionSetupScreen.js +526 -0
- package/lib/module/connections/disambiguate.js +83 -0
- package/lib/module/connections/types.js +60 -0
- package/lib/module/connections/useConnectionSetup.js +233 -0
- package/lib/module/controls/FilePicker.js +178 -0
- package/lib/module/controls/ImagePicker.js +231 -0
- package/lib/module/index.js +41 -0
- package/lib/module/logger.js +22 -0
- package/lib/module/package.json +1 -0
- package/lib/module/platform/PowerPlatformConnectionApi.js +595 -0
- package/lib/module/platform/PowerPlatformEnvironmentApi.js +186 -0
- package/lib/module/platform/resolveConnections.js +259 -0
- package/lib/module/platform/useConnectionRefs.js +267 -0
- package/lib/module/provider/PowerAppsHostProvider.js +332 -0
- package/lib/module/provider/PowerAppsProvider.js +345 -0
- package/lib/module/telemetry/index.js +55 -0
- package/lib/module/theme/ThemeProvider.js +63 -0
- package/lib/module/theme/tokens.js +46 -0
- package/lib/module/utils/dataverseUtils.js +90 -0
- package/lib/module/utils/isDevPlayer.js +23 -0
- package/lib/typescript/commonjs/ErrorBoundary.d.ts +30 -0
- package/lib/typescript/commonjs/auth/AuthContext.d.ts +57 -0
- package/lib/typescript/commonjs/bridge/NativePowerAppsBridge.d.ts +112 -0
- package/lib/typescript/commonjs/bridge/setupNativeHost.d.ts +39 -0
- package/lib/typescript/commonjs/connections/ConnectionSetupScreen.d.ts +107 -0
- package/lib/typescript/commonjs/connections/disambiguate.d.ts +43 -0
- package/lib/typescript/commonjs/connections/types.d.ts +91 -0
- package/lib/typescript/commonjs/connections/useConnectionSetup.d.ts +96 -0
- package/lib/typescript/commonjs/controls/FilePicker.d.ts +65 -0
- package/lib/typescript/commonjs/controls/ImagePicker.d.ts +76 -0
- package/lib/typescript/commonjs/index.d.ts +42 -0
- package/lib/typescript/commonjs/logger.d.ts +11 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/platform/PowerPlatformConnectionApi.d.ts +205 -0
- package/lib/typescript/commonjs/platform/PowerPlatformEnvironmentApi.d.ts +108 -0
- package/lib/typescript/commonjs/platform/resolveConnections.d.ts +86 -0
- package/lib/typescript/commonjs/platform/useConnectionRefs.d.ts +126 -0
- package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +123 -0
- package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +148 -0
- package/lib/typescript/commonjs/telemetry/index.d.ts +48 -0
- package/lib/typescript/commonjs/theme/ThemeProvider.d.ts +30 -0
- package/lib/typescript/commonjs/theme/tokens.d.ts +27 -0
- package/lib/typescript/commonjs/utils/dataverseUtils.d.ts +39 -0
- package/lib/typescript/commonjs/utils/isDevPlayer.d.ts +19 -0
- package/lib/typescript/module/ErrorBoundary.d.ts +30 -0
- package/lib/typescript/module/auth/AuthContext.d.ts +57 -0
- package/lib/typescript/module/bridge/NativePowerAppsBridge.d.ts +112 -0
- package/lib/typescript/module/bridge/setupNativeHost.d.ts +39 -0
- package/lib/typescript/module/connections/ConnectionSetupScreen.d.ts +107 -0
- package/lib/typescript/module/connections/disambiguate.d.ts +43 -0
- package/lib/typescript/module/connections/types.d.ts +91 -0
- package/lib/typescript/module/connections/useConnectionSetup.d.ts +96 -0
- package/lib/typescript/module/controls/FilePicker.d.ts +65 -0
- package/lib/typescript/module/controls/ImagePicker.d.ts +76 -0
- package/lib/typescript/module/index.d.ts +42 -0
- package/lib/typescript/module/logger.d.ts +11 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/platform/PowerPlatformConnectionApi.d.ts +205 -0
- package/lib/typescript/module/platform/PowerPlatformEnvironmentApi.d.ts +108 -0
- package/lib/typescript/module/platform/resolveConnections.d.ts +86 -0
- package/lib/typescript/module/platform/useConnectionRefs.d.ts +126 -0
- package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +123 -0
- package/lib/typescript/module/provider/PowerAppsProvider.d.ts +148 -0
- package/lib/typescript/module/telemetry/index.d.ts +48 -0
- package/lib/typescript/module/theme/ThemeProvider.d.ts +30 -0
- package/lib/typescript/module/theme/tokens.d.ts +27 -0
- package/lib/typescript/module/utils/dataverseUtils.d.ts +39 -0
- package/lib/typescript/module/utils/isDevPlayer.d.ts +19 -0
- package/package.json +119 -0
- package/scripts/build-ios-base.js +96 -0
- package/scripts/bundle.js +95 -0
- package/scripts/generate-connector-schemas.js +75 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.PowerAppsProvider = PowerAppsProvider;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _reactNativeGestureHandler = require("react-native-gesture-handler");
|
|
10
|
+
var _core = require("@tamagui/core");
|
|
11
|
+
var _portal = require("@tamagui/portal");
|
|
12
|
+
var _v = require("@tamagui/config/v3");
|
|
13
|
+
var _toast = require("@tamagui/toast");
|
|
14
|
+
var _reactQuery = require("@tanstack/react-query");
|
|
15
|
+
var _netinfo = _interopRequireDefault(require("@react-native-community/netinfo"));
|
|
16
|
+
var _AuthContext = require("../auth/AuthContext.js");
|
|
17
|
+
var _PowerAppsHostProvider = require("./PowerAppsHostProvider.js");
|
|
18
|
+
var _PowerPlatformEnvironmentApi = require("../platform/PowerPlatformEnvironmentApi.js");
|
|
19
|
+
var _ThemeProvider = require("../theme/ThemeProvider.js");
|
|
20
|
+
var _ErrorBoundary = require("../ErrorBoundary.js");
|
|
21
|
+
var _expoConstants = _interopRequireDefault(require("expo-constants"));
|
|
22
|
+
var _powerAppsNativeCommon = require("@microsoft/power-apps-native-common");
|
|
23
|
+
var _powerAppsNativeOffline = require("@microsoft/power-apps-native-offline");
|
|
24
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
25
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
26
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
27
|
+
/*!
|
|
28
|
+
* power-apps-native-host — PowerAppsProvider
|
|
29
|
+
*
|
|
30
|
+
* A single provider that composes all infrastructure required to run a
|
|
31
|
+
* Power Apps canvas app inside an Expo shell:
|
|
32
|
+
*
|
|
33
|
+
* GestureHandlerRootView
|
|
34
|
+
* └── TamaguiProvider (defaults to @tamagui/config/v3)
|
|
35
|
+
* └── PortalProvider (required by ToastViewport)
|
|
36
|
+
* └── ToastProvider + ToastViewport
|
|
37
|
+
* └── QueryClientProvider (own QueryClient, wired to NetInfo + AppState)
|
|
38
|
+
* └── AuthProvider
|
|
39
|
+
* └── PowerAppsHostProvider (connected via useAuth)
|
|
40
|
+
* └── children
|
|
41
|
+
*
|
|
42
|
+
* Usage:
|
|
43
|
+
*
|
|
44
|
+
* <PowerAppsProvider
|
|
45
|
+
* msalConfig={authConfig.msal}
|
|
46
|
+
* powerConfig={powerConfig}
|
|
47
|
+
* schemaMap={schemaMap}
|
|
48
|
+
* >
|
|
49
|
+
* <StatusBar style="auto" />
|
|
50
|
+
* <Slot />
|
|
51
|
+
* </PowerAppsProvider>
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
(0, _powerAppsNativeCommon.suppressKnownWarnings)();
|
|
55
|
+
|
|
56
|
+
// ---- React-Query wiring (runs once at module load) ----
|
|
57
|
+
|
|
58
|
+
// Wire onlineManager to NetInfo so refetchOnReconnect fires on mobile
|
|
59
|
+
// (the default browser-event-based detection is a no-op in React Native).
|
|
60
|
+
_reactQuery.onlineManager.setEventListener(setOnline => {
|
|
61
|
+
return _netinfo.default.addEventListener(state => setOnline(!!state.isConnected));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Wire focusManager to AppState so queries refetch when the app foregrounds.
|
|
65
|
+
_reactQuery.focusManager.setEventListener(handleFocus => {
|
|
66
|
+
const sub = _reactNative.AppState.addEventListener('change', status => {
|
|
67
|
+
if (_reactNative.Platform.OS !== 'web') handleFocus(status === 'active');
|
|
68
|
+
});
|
|
69
|
+
return () => sub.remove();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ---- Singletons ----
|
|
73
|
+
|
|
74
|
+
// The app's tamagui.config.ts (generated by /tamagui-design-system) is always
|
|
75
|
+
// passed as the `tamaguiConfig` prop and owns all $surface* / $accent* tokens.
|
|
76
|
+
// The bare @tamagui/config/v3 preset is only a last-resort fallback for tests
|
|
77
|
+
// and storybook — it will not resolve custom $surface* tokens.
|
|
78
|
+
const defaultTamaguiConfig = (0, _core.createTamagui)(_v.config);
|
|
79
|
+
|
|
80
|
+
// Whether the offline SDK will actually orchestrate for this app. Mirrors the
|
|
81
|
+
// resolution that @microsoft/power-apps-native-offline's useOfflineIntegration
|
|
82
|
+
// performs (offlineProfile prop → expo manifest `extra.__offlineProfile`;
|
|
83
|
+
// enabled defaults true unless the profile's `appConfig.enabled` is explicitly
|
|
84
|
+
// false), but computed synchronously here because the QueryClient's networkMode
|
|
85
|
+
// must be fixed BEFORE the first query runs. The SDK's own `isEnabled` is only
|
|
86
|
+
// known deep inside the tree (below QueryClientProvider), which is too late to
|
|
87
|
+
// configure the client without a race. Kept deliberately narrow: it only
|
|
88
|
+
// decides networkMode below.
|
|
89
|
+
function isOfflineEnabled(offlineProfile) {
|
|
90
|
+
// Only honour the prop when it carries a profileId — this mirrors the SDK's
|
|
91
|
+
// useOfflineIntegration, which ignores a profileId-less prop and falls through
|
|
92
|
+
// to the manifest. Without this guard a truthy-but-invalid prop (no profileId)
|
|
93
|
+
// short-circuits to `false` here while the SDK still resolves enabled from the
|
|
94
|
+
// manifest, leaving networkMode on 'online' (blank offline lists).
|
|
95
|
+
const propProfile = offlineProfile;
|
|
96
|
+
let profile = propProfile && propProfile.profileId ? propProfile : undefined;
|
|
97
|
+
if (!profile) {
|
|
98
|
+
const fromManifest = _expoConstants.default.expoConfig?.extra?.__offlineProfile;
|
|
99
|
+
if (fromManifest && typeof fromManifest === 'object' && fromManifest.profileId) {
|
|
100
|
+
profile = fromManifest;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (profile) {
|
|
104
|
+
// Honour an explicit `appConfig.enabled: false`. This mirrors the SDK's
|
|
105
|
+
// useOfflineIntegration, whose validateConfig classifies `enabled === false`
|
|
106
|
+
// as "explicitly-disabled" and stays inert. A host shell's placeholder
|
|
107
|
+
// profile (e.g. Power Apps Dev Player's offline-profile.json, enabled:false)
|
|
108
|
+
// must NOT flip networkMode to 'always' for the loaded app — otherwise every
|
|
109
|
+
// scanned app gets paused queries / blank lists. Any other resolved profile
|
|
110
|
+
// (enabled true, or omitted → SDK defaults to true) enables offline.
|
|
111
|
+
const appConfig = profile.appConfig;
|
|
112
|
+
if (appConfig && appConfig.enabled === false) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Builds the app's QueryClient. networkMode is gated on whether offline is
|
|
121
|
+
// actually enabled:
|
|
122
|
+
// • Offline enabled → 'always': the host is offline-first, so reads/writes are
|
|
123
|
+
// served by the offline SDK from local SQLite regardless of connectivity.
|
|
124
|
+
// onlineManager is wired to NetInfo (above) for refetchOnReconnect, but that
|
|
125
|
+
// same wiring makes the default 'online' mode PAUSE every query/mutation while
|
|
126
|
+
// offline (queryFn never runs → blank lists with WiFi off). 'always' makes
|
|
127
|
+
// React Query ignore connectivity for both the initial attempt AND retries, so
|
|
128
|
+
// local-SQLite reads/writes never get paused. ('offlineFirst' would still pause
|
|
129
|
+
// *retries* offline — wrong for a purely-local SDK where a transient first
|
|
130
|
+
// attempt error, e.g. SDK-not-ready at cold boot, would leave the query stuck
|
|
131
|
+
// 'paused' until reconnect.)
|
|
132
|
+
// • Offline disabled → undefined: React Query keeps its default 'online' mode,
|
|
133
|
+
// i.e. the exact pre-offline behaviour for online-only apps.
|
|
134
|
+
function createQueryClient(offlineEnabled) {
|
|
135
|
+
const networkMode = offlineEnabled ? 'always' : undefined;
|
|
136
|
+
return new _reactQuery.QueryClient({
|
|
137
|
+
defaultOptions: {
|
|
138
|
+
queries: {
|
|
139
|
+
staleTime: 60_000,
|
|
140
|
+
retry: 2,
|
|
141
|
+
networkMode
|
|
142
|
+
},
|
|
143
|
+
mutations: {
|
|
144
|
+
retry: 0,
|
|
145
|
+
networkMode
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---- Types ----
|
|
152
|
+
|
|
153
|
+
/** Shape of power.config.json consumed by the provider. */
|
|
154
|
+
|
|
155
|
+
// ---- Inner component ----
|
|
156
|
+
// Separate from PowerAppsProvider only because useAuth() must be called inside
|
|
157
|
+
// AuthProvider. All auth-dependent wiring lives here.
|
|
158
|
+
|
|
159
|
+
function InnerProviders({
|
|
160
|
+
msalConfig,
|
|
161
|
+
powerConfig,
|
|
162
|
+
schemaMap,
|
|
163
|
+
renderOAuthWebView,
|
|
164
|
+
onDisambiguationNeeded,
|
|
165
|
+
offlineProfile,
|
|
166
|
+
children
|
|
167
|
+
}) {
|
|
168
|
+
const {
|
|
169
|
+
acquireToken,
|
|
170
|
+
isSignedIn,
|
|
171
|
+
hasRealAccount
|
|
172
|
+
} = (0, _AuthContext.useAuth)();
|
|
173
|
+
const connectionReferences = (0, _react.useMemo)(() => powerConfig.connectionReferences ?? {}, [powerConfig.connectionReferences]);
|
|
174
|
+
|
|
175
|
+
// ---- Offline (all logic lives in the SDK) ----
|
|
176
|
+
// Adapter over the host's fetchInstanceUrl: the offline SDK's contract passes a
|
|
177
|
+
// 4th `signal?: AbortSignal` arg (which fetchInstanceUrl treats as bearerToken)
|
|
178
|
+
// and expects a `string | null` return, so drop the extra arg and normalise
|
|
179
|
+
// `undefined` -> `null`.
|
|
180
|
+
const resolveInstanceUrlForOffline = (0, _react.useCallback)(async (environmentId, region, acqToken, _signal) => {
|
|
181
|
+
const url = await (0, _PowerPlatformEnvironmentApi.fetchInstanceUrl)(environmentId, region, acqToken);
|
|
182
|
+
return url ?? null;
|
|
183
|
+
}, []);
|
|
184
|
+
|
|
185
|
+
// Gate offline engagement on a REAL account (`hasRealAccount`), not the
|
|
186
|
+
// `isSignedIn` flag — `isSignedIn` is inflated by noAuthMode (login-bypassed /
|
|
187
|
+
// demo sessions report signed-in but have no account, so `acquireToken` always
|
|
188
|
+
// returns null). Passing the inflated flag makes the SDK orchestrator engage
|
|
189
|
+
// and then spin in a perpetual "waiting-for-auth" 3s retry loop (each retry
|
|
190
|
+
// re-renders the provider subtree). With no obtainable token offline cannot
|
|
191
|
+
// sync anyway, so we only engage once a usable token exists.
|
|
192
|
+
const offline = (0, _powerAppsNativeOffline.useOfflineIntegration)({
|
|
193
|
+
powerConfig,
|
|
194
|
+
offlineProfile,
|
|
195
|
+
acquireToken,
|
|
196
|
+
isSignedIn: hasRealAccount,
|
|
197
|
+
fetchInstanceUrl: resolveInstanceUrlForOffline
|
|
198
|
+
});
|
|
199
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_PowerAppsHostProvider.PowerAppsHostProvider, {
|
|
200
|
+
acquireToken: acquireToken,
|
|
201
|
+
isSignedIn: isSignedIn,
|
|
202
|
+
appId: powerConfig.appId ?? undefined,
|
|
203
|
+
environmentId: powerConfig.environmentId ?? '',
|
|
204
|
+
region: powerConfig.region ?? 'prod',
|
|
205
|
+
connectionReferences: connectionReferences,
|
|
206
|
+
schemaMap: schemaMap,
|
|
207
|
+
databaseReferences: powerConfig.databaseReferences,
|
|
208
|
+
isvClientId: msalConfig.clientId,
|
|
209
|
+
renderOAuthWebView: renderOAuthWebView,
|
|
210
|
+
onDisambiguationNeeded: onDisambiguationNeeded,
|
|
211
|
+
children: [children, offline.StatusOverlay && /*#__PURE__*/(0, _jsxRuntime.jsx)(offline.StatusOverlay, {})]
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ---- Public component ----
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Top-level provider for Power Apps canvas apps running inside an Expo shell.
|
|
219
|
+
*
|
|
220
|
+
* Composes GestureHandlerRootView, TamaguiProvider, ToastProvider,
|
|
221
|
+
* QueryClientProvider, AuthProvider, and PowerAppsHostProvider into a single
|
|
222
|
+
* component, eliminating all provider boilerplate from the consuming app's
|
|
223
|
+
* root layout.
|
|
224
|
+
*/
|
|
225
|
+
function PowerAppsProvider({
|
|
226
|
+
msalConfig,
|
|
227
|
+
intuneConfig,
|
|
228
|
+
powerConfig,
|
|
229
|
+
schemaMap,
|
|
230
|
+
tamaguiConfig = defaultTamaguiConfig,
|
|
231
|
+
// override with app's tamagui.config.ts
|
|
232
|
+
defaultTheme = 'light',
|
|
233
|
+
theme,
|
|
234
|
+
darkTheme,
|
|
235
|
+
followSystemTheme = true,
|
|
236
|
+
renderOAuthWebView,
|
|
237
|
+
onDisambiguationNeeded,
|
|
238
|
+
appName,
|
|
239
|
+
logger: loggerProp,
|
|
240
|
+
offlineProfile,
|
|
241
|
+
children
|
|
242
|
+
}) {
|
|
243
|
+
// Capture the logger-prop identity at mount time so the effect dep array
|
|
244
|
+
// is stable (loggerProp is an object reference; re-running on every render
|
|
245
|
+
// would reconfigure and leak the previous provider).
|
|
246
|
+
const loggerPropRef = (0, _react.useRef)(loggerProp);
|
|
247
|
+
|
|
248
|
+
// Gate the offline-first networkMode on whether offline is actually enabled
|
|
249
|
+
// for this app. Keyed on the resolved boolean (not the prop object refs) so the
|
|
250
|
+
// QueryClient identity stays stable across renders even if the caller passes
|
|
251
|
+
// fresh offlineProfile literals.
|
|
252
|
+
const offlineEnabled = (0, _react.useMemo)(() => isOfflineEnabled(offlineProfile), [offlineProfile]);
|
|
253
|
+
const queryClient = (0, _react.useMemo)(() => createQueryClient(offlineEnabled), [offlineEnabled]);
|
|
254
|
+
|
|
255
|
+
// Configure the app logger exactly once per mount inside useEffect so that
|
|
256
|
+
// side effects (provider construction, async resolveDeviceId) never run
|
|
257
|
+
// during the render phase. This is safe because AppLogger.ts starts with a
|
|
258
|
+
// silent no-op Logger, so any telemetry emitted before the first commit is
|
|
259
|
+
// silently dropped rather than crashing.
|
|
260
|
+
(0, _react.useEffect)(() => {
|
|
261
|
+
const lp = loggerPropRef.current;
|
|
262
|
+
if (lp === null) {
|
|
263
|
+
// Explicit opt-out — replace with a silent no-op Logger.
|
|
264
|
+
(0, _powerAppsNativeCommon.configureAppLogger)(new _powerAppsNativeCommon.Logger());
|
|
265
|
+
} else if (lp !== undefined) {
|
|
266
|
+
// Caller supplied their own fully-configured logger.
|
|
267
|
+
(0, _powerAppsNativeCommon.configureAppLogger)(lp);
|
|
268
|
+
} else {
|
|
269
|
+
// Default: auto-wire the 1DS JS adapter so telemetry flows out of the
|
|
270
|
+
// box without any boilerplate in the consuming app.
|
|
271
|
+
const providerConfig = {
|
|
272
|
+
clusterCategory: (0, _powerAppsNativeCommon.regionToClusterCategory)(powerConfig.region ?? 'dev'),
|
|
273
|
+
clientType: appName ?? 'PowerAppsNative',
|
|
274
|
+
appVersion: _expoConstants.default.expoConfig?.version,
|
|
275
|
+
minSeverity: __DEV__ ? 'verbose' : 'info',
|
|
276
|
+
telemetryLocation: (0, _powerAppsNativeCommon.resolveTelemetryLocation)(powerConfig.geoRegion)
|
|
277
|
+
};
|
|
278
|
+
// Resolve the stable device ID first so every event — including the
|
|
279
|
+
// very first — carries the real GUID rather than the 'react-native'
|
|
280
|
+
// fallback. Events emitted while this resolves are silently dropped
|
|
281
|
+
// by the no-op Logger that is active until configureAppLogger is called.
|
|
282
|
+
(0, _powerAppsNativeCommon.resolveDeviceId)().then(deviceId => {
|
|
283
|
+
const provider = new _powerAppsNativeCommon.OneDsTelemetryProvider({
|
|
284
|
+
...providerConfig,
|
|
285
|
+
deviceId
|
|
286
|
+
});
|
|
287
|
+
(0, _powerAppsNativeCommon.configureAppLogger)(new _powerAppsNativeCommon.Logger({
|
|
288
|
+
providers: [provider]
|
|
289
|
+
}));
|
|
290
|
+
}).catch(_err => {
|
|
291
|
+
// SecureStore unavailable — fall back to a provider without a stable ID.
|
|
292
|
+
const provider = new _powerAppsNativeCommon.OneDsTelemetryProvider(providerConfig);
|
|
293
|
+
(0, _powerAppsNativeCommon.configureAppLogger)(new _powerAppsNativeCommon.Logger({
|
|
294
|
+
providers: [provider]
|
|
295
|
+
}));
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Flush any queued telemetry when this component unmounts (dev reload /
|
|
300
|
+
// process exit). Placed inside this effect so the cleanup is paired with
|
|
301
|
+
// the setup — if the effect re-runs (which it won't here, empty deps),
|
|
302
|
+
// the previous provider is flushed before the new one is configured.
|
|
303
|
+
return () => {
|
|
304
|
+
void (0, _powerAppsNativeCommon.getAppLogger)().flush();
|
|
305
|
+
};
|
|
306
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
307
|
+
}, []); // intentionally empty — logger is configured once per mount
|
|
308
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorBoundary.ErrorBoundary, {
|
|
309
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureHandlerRootView, {
|
|
310
|
+
style: {
|
|
311
|
+
flex: 1
|
|
312
|
+
},
|
|
313
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ThemeProvider.ThemeProvider, {
|
|
314
|
+
theme: theme,
|
|
315
|
+
darkTheme: darkTheme,
|
|
316
|
+
followSystem: followSystemTheme,
|
|
317
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_core.TamaguiProvider, {
|
|
318
|
+
config: tamaguiConfig,
|
|
319
|
+
defaultTheme: defaultTheme,
|
|
320
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_portal.PortalProvider, {
|
|
321
|
+
shouldAddRootHost: true,
|
|
322
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_toast.ToastProvider, {
|
|
323
|
+
swipeDirection: "horizontal",
|
|
324
|
+
duration: 3000,
|
|
325
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactQuery.QueryClientProvider, {
|
|
326
|
+
client: queryClient,
|
|
327
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_AuthContext.AuthProvider, {
|
|
328
|
+
msalConfig: msalConfig,
|
|
329
|
+
intuneConfig: intuneConfig,
|
|
330
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(InnerProviders, {
|
|
331
|
+
msalConfig: msalConfig,
|
|
332
|
+
powerConfig: powerConfig,
|
|
333
|
+
schemaMap: schemaMap,
|
|
334
|
+
renderOAuthWebView: renderOAuthWebView,
|
|
335
|
+
onDisambiguationNeeded: onDisambiguationNeeded,
|
|
336
|
+
offlineProfile: offlineProfile,
|
|
337
|
+
children: children
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_toast.ToastViewport, {
|
|
341
|
+
top: 50,
|
|
342
|
+
left: 0,
|
|
343
|
+
right: 0
|
|
344
|
+
})]
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
});
|
|
351
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.HostOperationNames = void 0;
|
|
7
|
+
Object.defineProperty(exports, "configureAppLogger", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _powerAppsNativeCommon.configureAppLogger;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
Object.defineProperty(exports, "getAppLogger", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () {
|
|
16
|
+
return _powerAppsNativeCommon.getAppLogger;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
var _powerAppsNativeCommon = require("@microsoft/power-apps-native-common");
|
|
20
|
+
/*!
|
|
21
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
22
|
+
*
|
|
23
|
+
* power-apps-native-host — package telemetry
|
|
24
|
+
*
|
|
25
|
+
* The shared logger singleton lives in `@microsoft/power-apps-native-common`
|
|
26
|
+
* (`getAppLogger` / `configureAppLogger`). This module re-exports those
|
|
27
|
+
* alongside the canonical host operation-name constants.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Canonical operation names for host package telemetry.
|
|
32
|
+
* Follow the `PowerAppsHost.<Subsystem>.<Action>` convention so events are
|
|
33
|
+
* filterable in Kusto by prefix.
|
|
34
|
+
*/
|
|
35
|
+
const HostOperationNames = exports.HostOperationNames = {
|
|
36
|
+
// ── Auth ─────────────────────────────────────────────────────────────────
|
|
37
|
+
/** MSAL PublicClientApplication.init() + initial account resolution. */
|
|
38
|
+
AuthInit: 'PowerAppsHost.Auth.Init',
|
|
39
|
+
/** Interactive sign-in (acquireToken interactive). */
|
|
40
|
+
AuthSignIn: 'PowerAppsHost.Auth.SignIn',
|
|
41
|
+
/** Sign-out (token cache clear + broker logout). */
|
|
42
|
+
AuthSignOut: 'PowerAppsHost.Auth.SignOut',
|
|
43
|
+
/** Silent token acquisition. Recorded only on failure. */
|
|
44
|
+
AuthAcquireTokenSilentFailed: 'PowerAppsHost.Auth.AcquireTokenSilent.Failed',
|
|
45
|
+
/** Intune MAM enrollment after sign-in. */
|
|
46
|
+
AuthIntuneEnroll: 'PowerAppsHost.Auth.Intune.Enroll',
|
|
47
|
+
/** Intune MAM unenrollment during sign-out. */
|
|
48
|
+
AuthIntuneUnenroll: 'PowerAppsHost.Auth.Intune.Unenroll',
|
|
49
|
+
// ── Bridge / HTTP ─────────────────────────────────────────────────────────
|
|
50
|
+
/** SDK-originated HTTP request relayed through NativePowerAppsBridge. */
|
|
51
|
+
BridgeHttpRequest: 'PowerAppsHost.Bridge.HttpRequest',
|
|
52
|
+
/** Plugin call forwarded through NativePowerAppsBridge (verbose; also used for unhandled). */
|
|
53
|
+
BridgeUnhandledPlugin: 'PowerAppsHost.Bridge.PluginCall',
|
|
54
|
+
/** Connector/Dataverse token acquisition for the bridge. Recorded only on failure. */
|
|
55
|
+
BridgeTokenAcquireFailed: 'PowerAppsHost.Bridge.TokenAcquire.Failed',
|
|
56
|
+
// ── Runtime ───────────────────────────────────────────────────────────────
|
|
57
|
+
/** Top-level React error boundary caught an unhandled JS error. */
|
|
58
|
+
RuntimeUnhandledJsError: 'PowerAppsHost.Runtime.UnhandledJsError',
|
|
59
|
+
// ── Platform APIs ─────────────────────────────────────────────────────────
|
|
60
|
+
/** Resolve Dataverse instanceUrl from Power Platform environment API. */
|
|
61
|
+
PlatformFetchInstanceUrl: 'PowerAppsHost.Platform.FetchInstanceUrl',
|
|
62
|
+
/** useConnectionRefs failed to resolve connections (recoverable). Recorded only on failure. */
|
|
63
|
+
PlatformLoadConnectionsFailed: 'PowerAppsHost.Platform.LoadConnections.Failed',
|
|
64
|
+
/** Create a new Power Platform connection. */
|
|
65
|
+
PlatformCreateConnection: 'PowerAppsHost.Platform.CreateConnection',
|
|
66
|
+
/** Resolve app connection references for the current launch. */
|
|
67
|
+
PlatformResolveConnectionRefs: 'PowerAppsHost.Platform.ResolveConnectionRefs',
|
|
68
|
+
/** Grant this app access to a Power Platform connection ACL. */
|
|
69
|
+
PlatformConnectionAclGrant: 'PowerAppsHost.Platform.ConnectionAclGrant'
|
|
70
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ThemeProvider = ThemeProvider;
|
|
7
|
+
Object.defineProperty(exports, "darkTheme", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _tokens.darkTheme;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
Object.defineProperty(exports, "lightTheme", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () {
|
|
16
|
+
return _tokens.lightTheme;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
exports.useThemeControl = useThemeControl;
|
|
20
|
+
exports.useThemeTokens = useThemeTokens;
|
|
21
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
22
|
+
var _reactNative = require("react-native");
|
|
23
|
+
var _tokens = require("./tokens.js");
|
|
24
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
25
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
26
|
+
/**
|
|
27
|
+
* ThemeProvider — distributes {@link ThemeTokens} through the component tree.
|
|
28
|
+
*
|
|
29
|
+
* `PowerAppsProvider` mounts this automatically. Use directly only when
|
|
30
|
+
* composing providers manually.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
// ── Contexts ─────────────────────────────────────────────────────────────────
|
|
34
|
+
const ThemeContext = /*#__PURE__*/(0, _react.createContext)(_tokens.lightTheme);
|
|
35
|
+
const ThemeControlContext = /*#__PURE__*/(0, _react.createContext)({
|
|
36
|
+
setTheme: () => {},
|
|
37
|
+
resetTheme: () => {}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ── Provider ─────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
function ThemeProvider({
|
|
43
|
+
theme = _tokens.lightTheme,
|
|
44
|
+
darkTheme = _tokens.darkTheme,
|
|
45
|
+
followSystem = true,
|
|
46
|
+
children
|
|
47
|
+
}) {
|
|
48
|
+
const colorScheme = (0, _reactNative.useColorScheme)();
|
|
49
|
+
const [override, setOverride] = (0, _react.useState)(null);
|
|
50
|
+
const resolved = (0, _react.useMemo)(() => {
|
|
51
|
+
if (override !== null) return override;
|
|
52
|
+
return followSystem && colorScheme === 'dark' ? darkTheme : theme;
|
|
53
|
+
}, [theme, darkTheme, followSystem, colorScheme, override]);
|
|
54
|
+
const control = (0, _react.useMemo)(() => ({
|
|
55
|
+
setTheme: tokens => setOverride(tokens),
|
|
56
|
+
resetTheme: () => setOverride(null)
|
|
57
|
+
}), []);
|
|
58
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ThemeControlContext.Provider, {
|
|
59
|
+
value: control,
|
|
60
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ThemeContext.Provider, {
|
|
61
|
+
value: resolved,
|
|
62
|
+
children: children
|
|
63
|
+
})
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Hooks ─────────────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
/** Returns current {@link ThemeTokens} from the nearest ThemeProvider. */
|
|
70
|
+
function useThemeTokens() {
|
|
71
|
+
return (0, _react.useContext)(ThemeContext);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Returns `{ setTheme, resetTheme }` for imperative theme overrides.
|
|
76
|
+
* Stable reference — does not re-render consumers on theme change.
|
|
77
|
+
*/
|
|
78
|
+
function useThemeControl() {
|
|
79
|
+
return (0, _react.useContext)(ThemeControlContext);
|
|
80
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.lightTheme = exports.darkTheme = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Theme token schema and built-in palettes for power-apps-native-host.
|
|
9
|
+
*
|
|
10
|
+
* Pass a custom {@link ThemeTokens} object to `PowerAppsProvider` to override
|
|
11
|
+
* the default Microsoft blue palette.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ── Built-in themes ──────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
/** Microsoft blue light palette (default). */
|
|
17
|
+
const lightTheme = exports.lightTheme = {
|
|
18
|
+
surface0: '#ffffff',
|
|
19
|
+
surface1: '#f7f7f8',
|
|
20
|
+
surface2: '#efeff1',
|
|
21
|
+
surface3: '#e3e3e7',
|
|
22
|
+
surface4: '#d0d0d6',
|
|
23
|
+
text0: '#111113',
|
|
24
|
+
text1: '#2e2e33',
|
|
25
|
+
text2: '#696969',
|
|
26
|
+
text3: '#9e9e9e',
|
|
27
|
+
accentDeep: '#004578',
|
|
28
|
+
accentBase: '#0078d4',
|
|
29
|
+
accentSoft: '#deecf9',
|
|
30
|
+
accentOnAccent: '#ffffff',
|
|
31
|
+
isDark: false
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** Microsoft blue dark palette (default). */
|
|
35
|
+
const darkTheme = exports.darkTheme = {
|
|
36
|
+
surface0: '#111113',
|
|
37
|
+
surface1: '#1c1c1f',
|
|
38
|
+
surface2: '#28282c',
|
|
39
|
+
surface3: '#333338',
|
|
40
|
+
surface4: '#404047',
|
|
41
|
+
text0: '#ededef',
|
|
42
|
+
text1: '#c8c8cc',
|
|
43
|
+
text2: '#9898a0',
|
|
44
|
+
text3: '#6b6b75',
|
|
45
|
+
accentDeep: '#82c7ff',
|
|
46
|
+
accentBase: '#2899f5',
|
|
47
|
+
accentSoft: '#0a2238',
|
|
48
|
+
accentOnAccent: '#ffffff',
|
|
49
|
+
isDark: true
|
|
50
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.base64ToDataUri = base64ToDataUri;
|
|
7
|
+
exports.base64ToDataverseUploadFile = base64ToDataverseUploadFile;
|
|
8
|
+
exports.base64ToUint8Array = base64ToUint8Array;
|
|
9
|
+
exports.createDataverseUploadFile = createDataverseUploadFile;
|
|
10
|
+
exports.detectImageMime = detectImageMime;
|
|
11
|
+
exports.readFileAsUint8Array = readFileAsUint8Array;
|
|
12
|
+
var _legacy = require("expo-file-system/legacy");
|
|
13
|
+
/**
|
|
14
|
+
* Utilities for reading local file:// URIs and converting them into the binary
|
|
15
|
+
* format expected by the Dataverse upload endpoint via NativePowerAppsBridge.
|
|
16
|
+
*
|
|
17
|
+
* Background:
|
|
18
|
+
* - `fetch('file://')` fails silently on Android.
|
|
19
|
+
* - `fetch('data:...')` throws "Network request failed" on Android.
|
|
20
|
+
* - `new Blob([Uint8Array])` crashes Hermes ("ArrayBufferView not supported").
|
|
21
|
+
* - NativePowerAppsBridge accepts `Uint8Array` and extracts `.buffer` as the
|
|
22
|
+
* fetch body, which React Native sends as raw bytes correctly.
|
|
23
|
+
*
|
|
24
|
+
* Solution: read via `readAsStringAsync` (base64) → `atob` → `Uint8Array`.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read a local `file://` URI and return its contents as a `Uint8Array`.
|
|
29
|
+
* Safe on both Android and iOS.
|
|
30
|
+
*/
|
|
31
|
+
async function readFileAsUint8Array(uri) {
|
|
32
|
+
const base64 = await (0, _legacy.readAsStringAsync)(uri, {
|
|
33
|
+
encoding: _legacy.EncodingType.Base64
|
|
34
|
+
});
|
|
35
|
+
return base64ToUint8Array(base64);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Detect the image MIME type from the first bytes of a base64-encoded string.
|
|
40
|
+
* Returns 'image/jpeg' as a safe default.
|
|
41
|
+
*/
|
|
42
|
+
function detectImageMime(base64) {
|
|
43
|
+
const normalized = normalizeBase64(base64);
|
|
44
|
+
if (normalized.startsWith('iVBOR')) return 'image/png';
|
|
45
|
+
if (normalized.startsWith('R0lG')) return 'image/gif';
|
|
46
|
+
if (normalized.startsWith('Qk')) return 'image/bmp';
|
|
47
|
+
if (normalized.startsWith('UklGR')) return 'image/webp';
|
|
48
|
+
return 'image/jpeg';
|
|
49
|
+
}
|
|
50
|
+
function normalizeBase64(base64) {
|
|
51
|
+
return base64.includes('base64,') ? base64.slice(base64.indexOf('base64,') + 'base64,'.length) : base64;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Convert a base64 string into bytes suitable for Dataverse binary upload APIs. */
|
|
55
|
+
function base64ToUint8Array(base64) {
|
|
56
|
+
const normalized = normalizeBase64(base64);
|
|
57
|
+
const binaryString = atob(normalized);
|
|
58
|
+
const uint8 = new Uint8Array(binaryString.length);
|
|
59
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
60
|
+
uint8[i] = binaryString.charCodeAt(i);
|
|
61
|
+
}
|
|
62
|
+
return uint8;
|
|
63
|
+
}
|
|
64
|
+
function uint8ArrayToArrayBuffer(bytes) {
|
|
65
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
66
|
+
copy.set(bytes);
|
|
67
|
+
return copy.buffer;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a lightweight File-compatible object for generated Dataverse upload methods.
|
|
72
|
+
* React Native does not consistently provide a browser File implementation, but the
|
|
73
|
+
* generated services only require `name` and `arrayBuffer()`.
|
|
74
|
+
*/
|
|
75
|
+
function createDataverseUploadFile(name, mimeType, bytes) {
|
|
76
|
+
return {
|
|
77
|
+
name,
|
|
78
|
+
type: mimeType,
|
|
79
|
+
size: bytes.byteLength,
|
|
80
|
+
arrayBuffer: async () => uint8ArrayToArrayBuffer(bytes)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Create a File-compatible image object from Dataverse-style base64 image content. */
|
|
85
|
+
function base64ToDataverseUploadFile(base64, name = 'image') {
|
|
86
|
+
const bytes = base64ToUint8Array(base64);
|
|
87
|
+
const mimeType = detectImageMime(base64);
|
|
88
|
+
const extension = mimeType.slice('image/'.length).replace('jpeg', 'jpg');
|
|
89
|
+
return createDataverseUploadFile(name.includes('.') ? name : `${name}.${extension}`, mimeType, bytes);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Build a `data:<mime>;base64,<data>` URI from a raw base64 string returned
|
|
94
|
+
* by Dataverse for an image column.
|
|
95
|
+
*/
|
|
96
|
+
function base64ToDataUri(base64) {
|
|
97
|
+
return `data:${detectImageMime(base64)};base64,${base64}`;
|
|
98
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isDevPlayer = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
var _expoConstants = _interopRequireDefault(require("expo-constants"));
|
|
9
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
/**
|
|
11
|
+
* True when the JS bundle is running inside the Power Apps Dev Player shell.
|
|
12
|
+
*
|
|
13
|
+
* Two signals are combined to cover both execution paths:
|
|
14
|
+
*
|
|
15
|
+
* 1. `Constants.expoConfig.extra.isDevPlayer` — set in apps/dev-player/app.config.js.
|
|
16
|
+
* Fires when the dev-player's **own** JS bundle is active (e.g. the launcher screen).
|
|
17
|
+
*
|
|
18
|
+
* 2. `NativeModules.PowerAppsDevLauncher` — the dev-player's Expo native module,
|
|
19
|
+
* registered in the dev-player APK/IPA regardless of which JS bundle is loaded.
|
|
20
|
+
* Fires when a template bundle is loaded via QR-code/URL into the dev-player shell,
|
|
21
|
+
* because expo-constants then reflects the template's manifest (no isDevPlayer flag)
|
|
22
|
+
* but the native module is still present in the host binary.
|
|
23
|
+
*
|
|
24
|
+
* Neither signal fires in a standalone release APK generated from the template, so
|
|
25
|
+
* `isDevPlayer` is false there and all debug logging is silenced.
|
|
26
|
+
*/
|
|
27
|
+
const isDevPlayer = exports.isDevPlayer = _expoConstants.default.expoConfig?.extra?.isDevPlayer === true || !!_reactNative.NativeModules.PowerAppsDevLauncher;
|