@livx.cc/appwrap 0.54.0 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -4
- package/runtime/app/main-page.ts +0 -2
- package/runtime/app/shell/capabilities.manifest.ts +27 -92
- package/runtime/app/shell/config.ts +4 -0
- package/runtime/app/shell/env.ts +8 -1
- package/runtime/app/shell/handlers.ts +2 -2
- package/runtime/tests/bridge-response-delivery.test.ts +10 -1
- package/runtime/tests/env-scheme.test.ts +50 -0
- package/scripts/stage-assets.mjs +1 -1
- package/src/cli.ts +424 -621
- package/src/config.ts +8 -1
- package/src/handlers.ts +1 -1
- package/src/packs.ts +174 -0
- package/src/testing.ts +173 -0
- package/templates/module-pack/README.md +44 -0
- package/templates/module-pack/handler.ts +9 -0
- package/templates/module-pack/manifest.ts +27 -0
- package/templates/module-pack/native-src/__MODULE_NAME__/.gitkeep +0 -0
- package/runtime/app/shell/billing-offer.ts +0 -54
- package/runtime/app/shell/handlers-billing.ts +0 -662
- package/runtime/app/shell/handlers-health.ts +0 -178
- package/runtime/app/shell/handlers-widget.ts +0 -97
- package/runtime/modules-native/billing/App_Resources/iOS/src/AppwrapManageSubscriptions.swift +0 -44
- package/runtime/modules-native/health/App_Resources/Android/src/main/java/cc/livx/appwrap/HealthConnectBridge.kt +0 -74
- package/runtime/modules-native/widget/App_Resources/Android/src/main/java/cc/livx/appwrap/AppwrapWidgetProvider.kt +0 -118
- package/runtime/modules-native/widget/App_Resources/Android/src/main/res/drawable/appwrap_badge_bg.xml +0 -9
- package/runtime/modules-native/widget/App_Resources/Android/src/main/res/layout/appwrap_widget.xml +0 -123
- package/runtime/modules-native/widget/App_Resources/Android/src/main/res/values/appwrap_widget_colors.xml +0 -10
- package/runtime/modules-native/widget/App_Resources/Android/src/main/res/values/appwrap_widget_styles.xml +0 -67
- package/runtime/modules-native/widget/App_Resources/Android/src/main/res/values-night/appwrap_widget_colors.xml +0 -9
- package/runtime/modules-native/widget/App_Resources/Android/src/main/res/xml/appwrap_widget_info.xml +0 -17
- package/runtime/modules-native/widget/App_Resources/iOS/extensions/AppwrapWidget/AppwrapWidget.entitlements +0 -10
- package/runtime/modules-native/widget/App_Resources/iOS/extensions/AppwrapWidget/AppwrapWidget.swift +0 -275
- package/runtime/modules-native/widget/App_Resources/iOS/extensions/AppwrapWidget/Info.plist +0 -25
- package/runtime/modules-native/widget/App_Resources/iOS/extensions/AppwrapWidget/extension.json +0 -9
- package/runtime/modules-native/widget/App_Resources/iOS/src/AppwrapWidgetReload.swift +0 -18
- package/runtime/tests/billing-offer.test.ts +0 -61
- package/runtime-desktop/autotest/index.html +0 -77
- package/runtime-desktop/bridge-shim/build.sh +0 -9
- package/runtime-desktop/bridge-shim/content-entry.ts +0 -87
- package/runtime-desktop/bridge-shim/shim.ts +0 -132
- package/runtime-desktop/chrome/appwrap-browser-chrome.html +0 -185
- package/runtime-desktop/crates/webview-control/Cargo.lock +0 -330
- package/runtime-desktop/crates/webview-control/Cargo.toml +0 -28
- package/runtime-desktop/crates/webview-control/examples/host_harness.rs +0 -362
- package/runtime-desktop/crates/webview-control/src/embedded.rs +0 -138
- package/runtime-desktop/crates/webview-control/src/lib.rs +0 -100
- package/runtime-desktop/crates/webview-control/src/popup.rs +0 -522
- package/runtime-desktop/crates/webview-control/src/profiles.rs +0 -57
- package/runtime-desktop/crates/webview-control/src/wkwebview_backend.rs +0 -558
- package/runtime-desktop/src-tauri/Cargo.lock +0 -5474
- package/runtime-desktop/src-tauri/Cargo.toml +0 -45
- package/runtime-desktop/src-tauri/build.rs +0 -3
- package/runtime-desktop/src-tauri/capabilities/default.json +0 -6
- package/runtime-desktop/src-tauri/icons/icon.png +0 -0
- package/runtime-desktop/src-tauri/shell_config.json +0 -10
- package/runtime-desktop/src-tauri/src/biometrics_mac.rs +0 -138
- package/runtime-desktop/src-tauri/src/bridge_mac.rs +0 -424
- package/runtime-desktop/src-tauri/src/browser_tab.rs +0 -804
- package/runtime-desktop/src-tauri/src/main.rs +0 -1228
- package/runtime-desktop/src-tauri/src/notifications_mac.rs +0 -202
- package/runtime-desktop/src-tauri/src/oauth_mac.rs +0 -139
- package/runtime-desktop/src-tauri/src/plugin_host.rs +0 -522
- package/runtime-desktop/src-tauri/src/popup_mac.rs +0 -5
- package/runtime-desktop/src-tauri/src/push_mac.rs +0 -193
- package/runtime-desktop/src-tauri/src/server.rs +0 -156
- package/runtime-desktop/src-tauri/src/sidecar.rs +0 -398
- package/runtime-desktop/src-tauri/tauri.conf.json +0 -14
- package/src/desktop.ts +0 -300
- package/src/plugin/host.ts +0 -242
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livx.cc/appwrap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.56.0",
|
|
4
4
|
"description": "Wrap any PWA into a native app with native capabilities (appwrap runtime + @livx.cc/native-kit).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Elya Livshitz",
|
|
@@ -32,18 +32,19 @@
|
|
|
32
32
|
"./config": "./src/config.ts",
|
|
33
33
|
"./handlers": "./src/handlers.ts",
|
|
34
34
|
"./plugin": "./src/plugin/index.ts",
|
|
35
|
-
"./cli": "./src/cli.ts"
|
|
35
|
+
"./cli": "./src/cli.ts",
|
|
36
|
+
"./testing": "./src/testing.ts",
|
|
37
|
+
"./runtime/*": "./runtime/*.ts"
|
|
36
38
|
},
|
|
37
39
|
"files": [
|
|
38
40
|
"src",
|
|
39
41
|
"runtime",
|
|
40
|
-
"runtime-desktop",
|
|
41
42
|
"templates",
|
|
42
43
|
"scripts/stage-assets.mjs"
|
|
43
44
|
],
|
|
44
45
|
"scripts": {
|
|
45
46
|
"prepack": "node scripts/stage-assets.mjs",
|
|
46
|
-
"postpack": "rm -rf runtime
|
|
47
|
+
"postpack": "rm -rf runtime templates"
|
|
47
48
|
},
|
|
48
49
|
"publishConfig": {
|
|
49
50
|
"access": "public"
|
package/runtime/app/main-page.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { registerHandlers } from './shell/handlers';
|
|
|
5
5
|
import { registerExtendedHandlers } from './shell/handlers-extended';
|
|
6
6
|
import { registerParityHandlers } from './shell/handlers-parity';
|
|
7
7
|
import { registerSystemHandlers } from './shell/handlers-system';
|
|
8
|
-
import { registerBillingHandlers } from './shell/handlers-billing';
|
|
9
8
|
import { registerMediaHandlers } from './shell/handlers-media';
|
|
10
9
|
import { registerKeyboardHandlers } from './shell/keyboard';
|
|
11
10
|
import { registerFsHandlers } from './shell/handlers-fs';
|
|
@@ -100,7 +99,6 @@ export function onPageLoaded(args: EventData): void {
|
|
|
100
99
|
registerExtendedHandlers();
|
|
101
100
|
registerParityHandlers();
|
|
102
101
|
registerSystemHandlers();
|
|
103
|
-
registerBillingHandlers();
|
|
104
102
|
registerMediaHandlers();
|
|
105
103
|
registerKeyboardHandlers();
|
|
106
104
|
registerFsHandlers();
|
|
@@ -61,8 +61,19 @@ export interface ModuleManifest {
|
|
|
61
61
|
* App_Resources layout, copied into `native/` ONLY when the module is active (stays strippable).
|
|
62
62
|
* Defaults to the module `name` when the convention dir exists. */
|
|
63
63
|
nativeSrc?: string;
|
|
64
|
+
/** Build-time registration wiring for a module contributed by an OUT-OF-REPO pack (see packs.ts).
|
|
65
|
+
* Built-in modules leave this undefined — their handler barrel wiring comes from cli.ts's
|
|
66
|
+
* OPTIONAL_GROUP_HANDLERS (keyed by `group`). A pack module is self-describing: `file` is the
|
|
67
|
+
* handler module relative to the pack dir, `fn` the exported register function the shell barrel
|
|
68
|
+
* calls at page load. Runtime ignores this field (pure build-time metadata). */
|
|
69
|
+
handler?: { file: string; fn: string };
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
/** Manifest schema version the built-in modules speak. A module pack (packs.ts) declares the version
|
|
73
|
+
* it targets; a mismatch is rejected at resolve time (loud) rather than silently mis-derived. Bump
|
|
74
|
+
* this only on a breaking change to the `ModuleManifest` shape the CLI's derivation depends on. */
|
|
75
|
+
export const MANIFEST_SCHEMA_VERSION = 1;
|
|
76
|
+
|
|
66
77
|
export const MODULES: ModuleManifest[] = [
|
|
67
78
|
// ── core (always on, no permissions) ───────────────────────────────────
|
|
68
79
|
{ name: 'haptics', core: true, group: 'core', capabilities: { haptics: 'native' } },
|
|
@@ -148,19 +159,6 @@ export const MODULES: ModuleManifest[] = [
|
|
|
148
159
|
] },
|
|
149
160
|
android: { permissions: ['android.permission.READ_CALENDAR', 'android.permission.WRITE_CALENDAR'] },
|
|
150
161
|
},
|
|
151
|
-
{
|
|
152
|
-
name: 'billing', group: 'billing',
|
|
153
|
-
capabilities: { billing: { ios: true, android: true } },
|
|
154
|
-
// iOS: Swift shim (AppwrapManageSubscriptions.swift) bridging StoreKit 2's Swift-async
|
|
155
|
-
// showManageSubscriptions(in:) to an @objc completion the ObjC bridge can call. Compiled into
|
|
156
|
-
// the app target only when `billing` is active (NS auto-compiles App_Resources/iOS/src/*.swift).
|
|
157
|
-
// Android: Play Billing Library v7 (handlers-billing.ts registerAndroidBilling). The BILLING
|
|
158
|
-
// permission is auto-merged by the library's own manifest, so no android.permissions entry —
|
|
159
|
-
// only the gradle dep is needed, and it lands ONLY when `billing` is active (strippable weight).
|
|
160
|
-
android: { gradleDeps: ['com.android.billingclient:billing:7.1.1'] },
|
|
161
|
-
nativeSrc: 'billing',
|
|
162
|
-
},
|
|
163
|
-
|
|
164
162
|
// ── reviews — in-app store review prompt — opt-in, STRIPPABLE (own handler + group) ──
|
|
165
163
|
// Moved out of the always-bundled parity/system handlers: the Android Play In-App Review path
|
|
166
164
|
// references `com.google.android.play:review` classes — a gradle dep that must NOT land in builds
|
|
@@ -221,53 +219,6 @@ export const MODULES: ModuleManifest[] = [
|
|
|
221
219
|
android: { permissions: ['android.permission.RECORD_AUDIO'] },
|
|
222
220
|
},
|
|
223
221
|
|
|
224
|
-
// ── health (steps) — opt-in heavy module; FG live + BG via OS step store ──
|
|
225
|
-
{
|
|
226
|
-
name: 'health', group: 'health',
|
|
227
|
-
capabilities: { health: 'native' },
|
|
228
|
-
// iOS reads HealthKit (the Health app's aggregated total incl. Apple Watch): health-share usage
|
|
229
|
-
// string + the healthkit entitlement, both module-owned. No background execution — HealthKit
|
|
230
|
-
// already has the steps the OS recorded while the app was killed.
|
|
231
|
-
ios: {
|
|
232
|
-
// App Store validation REQUIRES both Share AND Update purpose strings whenever the HealthKit
|
|
233
|
-
// entitlement is present — even for a read-only app (else upload fails 409 "Missing purpose
|
|
234
|
-
// string … NSHealthUpdateUsageDescription"). Device debug builds don't validate this; only the
|
|
235
|
-
// App Store upload does.
|
|
236
|
-
permissions: [
|
|
237
|
-
{ key: 'NSHealthShareUsageDescription', domain: 'health', defaultUsage: 'Read your step count from the Health app.' },
|
|
238
|
-
{ key: 'NSHealthUpdateUsageDescription', domain: 'health', defaultUsage: 'Read your step count from the Health app.' },
|
|
239
|
-
// Live step stream (health.liveSteps) uses CMPedometer (CoreMotion) → iOS terminates the app
|
|
240
|
-
// on access without this Motion & Fitness usage string. Required for the real-time count.
|
|
241
|
-
{ key: 'NSMotionUsageDescription', domain: 'motion', defaultUsage: 'Count your steps live as you walk.' },
|
|
242
|
-
],
|
|
243
|
-
entitlements: { 'com.apple.developer.healthkit': true },
|
|
244
|
-
},
|
|
245
|
-
// Android primary = Health Connect (system store, Wear-inclusive, survives kill — matches iOS).
|
|
246
|
-
// Its client is Kotlin-coroutine-only, so the module ships HealthConnectBridge.kt (nativeSrc) and
|
|
247
|
-
// flags kotlin:true. Falls back to SensorManager TYPE_STEP_COUNTER (ACTIVITY_RECOGNITION) when HC absent.
|
|
248
|
-
android: {
|
|
249
|
-
permissions: ['android.permission.health.READ_STEPS', 'android.permission.ACTIVITY_RECOGNITION'],
|
|
250
|
-
gradleDeps: [
|
|
251
|
-
'androidx.health.connect:connect-client:1.1.0-rc01',
|
|
252
|
-
'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0',
|
|
253
|
-
],
|
|
254
|
-
kotlin: true,
|
|
255
|
-
// Health Connect refuses to honor permissions unless the app declares a rationale activity
|
|
256
|
-
// handling VIEW_PERMISSION_USAGE / HEALTH_PERMISSIONS (alias onto the NS main activity).
|
|
257
|
-
manifestApplication: `<activity-alias
|
|
258
|
-
android:name="ViewPermissionUsageActivity"
|
|
259
|
-
android:exported="true"
|
|
260
|
-
android:targetActivity="com.tns.NativeScriptActivity"
|
|
261
|
-
android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
|
|
262
|
-
<intent-filter>
|
|
263
|
-
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
|
|
264
|
-
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
|
|
265
|
-
</intent-filter>
|
|
266
|
-
</activity-alias>`,
|
|
267
|
-
},
|
|
268
|
-
nativeSrc: 'health',
|
|
269
|
-
},
|
|
270
|
-
|
|
271
222
|
// ── tracking — App Tracking Transparency (iOS) — opt-in, STRIPPABLE (own handler + group) ──
|
|
272
223
|
// The native-only store-compliance seam for cross-company tracking (IDFA / cross-app identity):
|
|
273
224
|
// Apple REQUIRES the ATT prompt + NSUserTrackingUsageDescription and forbids tracking before
|
|
@@ -321,46 +272,30 @@ export const MODULES: ModuleManifest[] = [
|
|
|
321
272
|
android: { gradleDeps: ['androidx.work:work-runtime:2.9.1', 'com.google.guava:guava:33.3.1-android'] },
|
|
322
273
|
},
|
|
323
274
|
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
// into a shared container (App Group on iOS, SharedPreferences on Android) via the `widget.publish`
|
|
327
|
-
// handler; the extension/provider reads it at render and each tile deep-links back on tap. Two kinds:
|
|
328
|
-
// a `launcher` icon-grid (the host's pinned apps) and a `data` widget (a mini-app's live stat/list).
|
|
329
|
-
// The App Group id is derived (`group.<appId>`) and templated into both targets by the CLI.
|
|
330
|
-
{
|
|
331
|
-
name: 'widget', group: 'widget',
|
|
332
|
-
capabilities: { widget: { ios: true, android: true } },
|
|
333
|
-
// Android launcher widget = an AppWidgetProvider receiver (icon-grid) fed by the widget.publish
|
|
334
|
-
// handler via SharedPreferences; Kotlin source + res ship as nativeSrc. iOS side = the WidgetKit
|
|
335
|
-
// extension under nativeSrc's App_Resources/iOS/extensions/.
|
|
336
|
-
android: {
|
|
337
|
-
kotlin: true,
|
|
338
|
-
manifestApplication: `<receiver
|
|
339
|
-
android:name="cc.livx.appwrap.AppwrapWidgetProvider"
|
|
340
|
-
android:exported="true">
|
|
341
|
-
<intent-filter>
|
|
342
|
-
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
|
343
|
-
</intent-filter>
|
|
344
|
-
<meta-data
|
|
345
|
-
android:name="android.appwidget.provider"
|
|
346
|
-
android:resource="@xml/appwrap_widget_info" />
|
|
347
|
-
</receiver>`,
|
|
348
|
-
},
|
|
349
|
-
nativeSrc: 'widget',
|
|
350
|
-
},
|
|
275
|
+
// NOTE: billing, health, and widget are NOT part of CE — they live in host-provided module packs
|
|
276
|
+
// (a consumer opts in via `modulePacks`).
|
|
351
277
|
];
|
|
352
278
|
|
|
353
279
|
/** Opt-in registration groups that own their own NS handler file (strippable when inactive). Core
|
|
354
|
-
* groups (core/extended/parity/system/media
|
|
355
|
-
export const OPTIONAL_GROUPS = ['
|
|
280
|
+
* groups (core/extended/parity/system/media) are always bundled; only these are CLI-gated. */
|
|
281
|
+
export const OPTIONAL_GROUPS = ['oauth', 'reviews', 'scanner', 'speech', 'tracking', 'appleSignIn', 'backgroundTask'] as const;
|
|
282
|
+
|
|
283
|
+
/** CLI-gated groups (strippable in explicit mode) that legacy mode (no `modules` key) STILL auto-bundles
|
|
284
|
+
* for back-compat. Empty now that billing (its only member) moved to a host-provided pack — kept as the
|
|
285
|
+
* seam so the carve-out can be reinstated without touching cli.ts if a future built-in needs it. */
|
|
286
|
+
export const LEGACY_BUNDLED_GROUPS = [] as const;
|
|
356
287
|
|
|
357
|
-
/** Resolve the active capability map for the handshake from a set of active capability names.
|
|
288
|
+
/** Resolve the active capability map for the handshake from a set of active capability names. Pack
|
|
289
|
+
* modules (from the config's `modulePacks`) aren't in the static MODULES, so their handshake-relevant
|
|
290
|
+
* subset is passed in via `extraModules` (generated into active-modules.generated.ts) and merged — a
|
|
291
|
+
* pack capability is advertised exactly like a built-in one. Empty for a pack-less build. */
|
|
358
292
|
export function buildCapabilityMap(
|
|
359
293
|
activeNames: Set<string>,
|
|
360
|
-
platform: 'ios' | 'android'
|
|
294
|
+
platform: 'ios' | 'android',
|
|
295
|
+
extraModules: ReadonlyArray<Pick<ModuleManifest, 'name' | 'core' | 'capabilities' | 'group'>> = []
|
|
361
296
|
): Record<string, 'native' | 'none'> {
|
|
362
297
|
const map: Record<string, 'native' | 'none'> = {};
|
|
363
|
-
for (const m of MODULES) {
|
|
298
|
+
for (const m of [...MODULES, ...extraModules]) {
|
|
364
299
|
if (!m.core && !activeNames.has(m.name)) continue;
|
|
365
300
|
for (const [cap, val] of Object.entries(m.capabilities)) {
|
|
366
301
|
if (val === 'native') map[cap] = 'native';
|
|
@@ -29,6 +29,10 @@ export const SHELL_CONFIG = {
|
|
|
29
29
|
/** Absolute backend origin for an offline (app://) PWA whose API/WS calls were originally same-origin.
|
|
30
30
|
* Injected to the page as `window.__APPWRAP_BACKEND_ORIGIN__`; empty = same-origin (browser default). */
|
|
31
31
|
backendOrigin: '',
|
|
32
|
+
/** The app's own custom URL scheme (`appwrap.json.urlScheme`), the one registered for deep links.
|
|
33
|
+
* Surfaced to the page as `window.__APPWRAP__.scheme` so a webapp can build its own deep-link URLs;
|
|
34
|
+
* empty = no scheme configured, and the `scheme` key is OMITTED from `__APPWRAP__` (see `detectEnv`). */
|
|
35
|
+
urlScheme: '' as string,
|
|
32
36
|
/** Debug/dev mode: keep the screen awake (no auto-lock while foreground) + WebView inspectable
|
|
33
37
|
* (Safari Web Inspector / chrome://inspect). Set true by `appwrap deploy`; never in store builds. */
|
|
34
38
|
debug: false,
|
package/runtime/app/shell/env.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Application, isAndroid } from '@nativescript/core';
|
|
2
|
+
import { SHELL_CONFIG } from './config';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Runtime environment hints the page can't reliably detect itself, surfaced natively so a PWA can
|
|
@@ -10,18 +11,24 @@ import { Application, isAndroid } from '@nativescript/core';
|
|
|
10
11
|
* cap DPR / disable heavy backgrounds when set.
|
|
11
12
|
* • reduceMotion— the OS "reduce motion" accessibility setting is on. Apps should disable non-essential
|
|
12
13
|
* animation (mirrors the `prefers-reduced-motion` media query, but works pre-paint).
|
|
14
|
+
* • scheme — the app's own custom URL scheme (from `SHELL_CONFIG.urlScheme`), so a webapp inside the
|
|
15
|
+
* shell can build its own deep-link URLs. OMITTED when no urlScheme is configured.
|
|
13
16
|
*/
|
|
14
17
|
export interface AppwrapEnv {
|
|
15
18
|
native: boolean;
|
|
16
19
|
platform: 'android' | 'ios';
|
|
17
20
|
isEmulator: boolean;
|
|
18
21
|
reduceMotion: boolean;
|
|
22
|
+
scheme?: string;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
export function detectEnv(): AppwrapEnv {
|
|
22
|
-
|
|
26
|
+
const base: AppwrapEnv = isAndroid
|
|
23
27
|
? { native: true, platform: 'android', isEmulator: androidIsEmulator(), reduceMotion: androidReduceMotion() }
|
|
24
28
|
: { native: true, platform: 'ios', isEmulator: iosIsSimulator(), reduceMotion: iosReduceMotion() };
|
|
29
|
+
// Only surface `scheme` when configured — contract requires the key ABSENT for an empty urlScheme.
|
|
30
|
+
if (SHELL_CONFIG.urlScheme) base.scheme = SHELL_CONFIG.urlScheme;
|
|
31
|
+
return base;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
/** Document-start snippet: merge the detected hints onto `window.__APPWRAP__` (idempotent). */
|
|
@@ -6,7 +6,7 @@ import { showToast } from './toast';
|
|
|
6
6
|
import { showBanner, dismissBanner } from './banner';
|
|
7
7
|
import { setStatusBarStyle } from './status-bar';
|
|
8
8
|
import { buildCapabilityMap } from './capabilities.manifest';
|
|
9
|
-
import { ACTIVE_MODULE_NAMES } from './active-modules.generated';
|
|
9
|
+
import { ACTIVE_MODULE_NAMES, PACK_MODULES } from './active-modules.generated';
|
|
10
10
|
import { consumePendingBackgroundTaskId } from './background-context';
|
|
11
11
|
|
|
12
12
|
/** Build identifier for the native shell bundle — bump per deploy to spot stale bundles. */
|
|
@@ -26,7 +26,7 @@ export function registerHandlers(): void {
|
|
|
26
26
|
// Capability map is composed from the active module set (appwrap.json `modules` → the generated
|
|
27
27
|
// ACTIVE_MODULE_NAMES) + the always-on core, deduped. Opt-out capabilities report 'none' so the
|
|
28
28
|
// kit degrades gracefully. Push is special — gated by per-platform build config, not the manifest.
|
|
29
|
-
const capabilities = buildCapabilityMap(new Set(ACTIVE_MODULE_NAMES), isIOS ? 'ios' : 'android') as Record<string, 'native' | 'none'>;
|
|
29
|
+
const capabilities = buildCapabilityMap(new Set(ACTIVE_MODULE_NAMES), isIOS ? 'ios' : 'android', PACK_MODULES) as Record<string, 'native' | 'none'>;
|
|
30
30
|
capabilities.push = (isIOS ? SHELL_CONFIG.pushIos : SHELL_CONFIG.pushAndroid) ? 'native' : 'none';
|
|
31
31
|
// Background launch: the headless runner (handlers-background) set the wake id before loading this
|
|
32
32
|
// (offscreen) WebView. Report it so `kit.backgroundTask` dispatches the registered handler. Consumed
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
2
2
|
|
|
3
3
|
// The shell imports NativeScript globals at module load; the bridge only needs isIOS/isAndroid.
|
|
4
|
-
|
|
4
|
+
// Superset shape — bun shares the runtime-module cache across files in a dir run, so every
|
|
5
|
+
// @nativescript/core mock must expose the same named exports the runtime modules import.
|
|
6
|
+
mock.module('@nativescript/core', () => ({
|
|
7
|
+
isIOS: false, isAndroid: false, WebView: class {},
|
|
8
|
+
ApplicationSettings: { getString: (_k: string, d = '') => d, setString: () => {}, remove: () => {} },
|
|
9
|
+
Dialogs: { confirm: async () => true, action: async () => '', alert: async () => undefined, prompt: async () => ({ result: false, text: '' }) },
|
|
10
|
+
Utils: { dispatchToMainThread: (fn: () => void) => fn() },
|
|
11
|
+
Application: { on: () => {}, suspendEvent: 's', resumeEvent: 'r', orientationChangedEvent: 'o', android: {} },
|
|
12
|
+
Connectivity: { startMonitoring: () => {} },
|
|
13
|
+
}));
|
|
5
14
|
|
|
6
15
|
const { Bridge } = await import('../app/shell/bridge');
|
|
7
16
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `detectEnv()` / `envGlobalsJs()` surface the app's own custom URL scheme to the page as
|
|
3
|
+
* `window.__APPWRAP__.scheme`. Contract: the key is present ONLY when `SHELL_CONFIG.urlScheme` is set,
|
|
4
|
+
* and OMITTED (not `scheme:""`/undefined) when empty — for BOTH android and ios.
|
|
5
|
+
*/
|
|
6
|
+
import { afterEach, describe, expect, mock, test } from 'bun:test';
|
|
7
|
+
|
|
8
|
+
// isAndroid is a getter over a mutable flag so both platform branches are exercised. Superset the
|
|
9
|
+
// named exports env.ts reaches (Application is only touched on the android branch's reduce-motion path,
|
|
10
|
+
// which throws-and-falls-back off-device — safe to stub minimally).
|
|
11
|
+
let androidFlag = false;
|
|
12
|
+
mock.module('@nativescript/core', () => ({
|
|
13
|
+
Application: { android: {} },
|
|
14
|
+
get isAndroid() { return androidFlag; },
|
|
15
|
+
isIOS: false,
|
|
16
|
+
WebView: class {},
|
|
17
|
+
// Superset shape — bun shares the runtime-module cache across files in a dir run, so every
|
|
18
|
+
// @nativescript/core mock must expose the same named exports the runtime modules import.
|
|
19
|
+
ApplicationSettings: { getString: (_k: string, d = '') => d, setString: () => {}, remove: () => {} },
|
|
20
|
+
Dialogs: { confirm: async () => true, action: async () => '', alert: async () => undefined, prompt: async () => ({ result: false, text: '' }) },
|
|
21
|
+
Utils: { dispatchToMainThread: (fn: () => void) => fn() },
|
|
22
|
+
Connectivity: { startMonitoring: () => {} },
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
// Dynamic import AFTER mock.module so the stub is registered before env.ts pulls @nativescript/core
|
|
26
|
+
// (static imports hoist above mock.module and would load the real native module).
|
|
27
|
+
const { SHELL_CONFIG } = await import('../app/shell/config');
|
|
28
|
+
const { detectEnv, envGlobalsJs } = await import('../app/shell/env');
|
|
29
|
+
|
|
30
|
+
afterEach(() => { SHELL_CONFIG.urlScheme = ''; });
|
|
31
|
+
|
|
32
|
+
for (const platform of ['android', 'ios'] as const) {
|
|
33
|
+
describe(`scheme surfacing — ${platform}`, () => {
|
|
34
|
+
test('INCLUDES scheme when urlScheme is set', () => {
|
|
35
|
+
androidFlag = platform === 'android';
|
|
36
|
+
SHELL_CONFIG.urlScheme = 'demoapp';
|
|
37
|
+
expect(detectEnv().scheme).toBe('demoapp');
|
|
38
|
+
expect(envGlobalsJs()).toContain('"scheme":"demoapp"');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('OMITS the scheme key when urlScheme is empty', () => {
|
|
42
|
+
androidFlag = platform === 'android';
|
|
43
|
+
SHELL_CONFIG.urlScheme = '';
|
|
44
|
+
const env = detectEnv();
|
|
45
|
+
expect('scheme' in env).toBe(false);
|
|
46
|
+
// The injected snippet must carry no `scheme` key at all (not scheme:"" / scheme:undefined).
|
|
47
|
+
expect(envGlobalsJs()).not.toContain('scheme');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
package/scripts/stage-assets.mjs
CHANGED
|
@@ -12,7 +12,7 @@ const repoRoot = resolve(pkgDir, '../..');
|
|
|
12
12
|
// src-tauri/target — gigabytes — and src-tauri/gen — regenerated on build) and OS cruft.
|
|
13
13
|
const EXCLUDE = /(?:^|\/)(?:node_modules|platforms|hooks|app\/www|target|gen|\.DS_Store)(?:\/|$)/;
|
|
14
14
|
|
|
15
|
-
for (const name of ['runtime', '
|
|
15
|
+
for (const name of ['runtime', 'templates']) {
|
|
16
16
|
const srcRoot = resolve(repoRoot, name);
|
|
17
17
|
const dest = resolve(pkgDir, name);
|
|
18
18
|
rmSync(dest, { recursive: true, force: true });
|