@livx.cc/appwrap 0.38.0 → 0.38.2
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 +1 -1
- package/runtime/app/shell/config.ts +5 -0
- package/runtime/app/shell/custom-webview.ios.ts +4 -0
- package/src/cli.ts +7 -0
- package/src/config.ts +9 -0
- package/src/derive.ts +21 -0
- package/templates/ci/fastlane/Fastfile +18 -0
package/package.json
CHANGED
|
@@ -40,6 +40,11 @@ export const SHELL_CONFIG = {
|
|
|
40
40
|
* fights the app:// handler / remote-update detection). On by default; set false to opt out and keep
|
|
41
41
|
* the SW (e.g. for in-WebView web-push). See `serviceWorkerGuardJs`. */
|
|
42
42
|
neutralizeServiceWorker: true,
|
|
43
|
+
/** iOS App-Bound Domains (from `appwrap.json.appBoundDomains`). When non-empty the shell sets
|
|
44
|
+
* `WKWebViewConfiguration.limitsNavigationsToAppBoundDomains = true` — Apple's gate for running a
|
|
45
|
+
* service worker in the WKWebView (the same hosts are stamped to Info.plist `WKAppBoundDomains`).
|
|
46
|
+
* RESTRICTS the WebView to these domains, so only for single-origin apps. Empty = no restriction. */
|
|
47
|
+
appBoundDomains: [] as string[],
|
|
43
48
|
/** Open external-origin navigations (`<a>` to another origin, incl. `target="_blank"`, and
|
|
44
49
|
* `window.open(...)`) in the OS default browser (Safari / Chrome) instead of inside the shell
|
|
45
50
|
* WebView — regular-native-app behavior. Same-origin SPA navigation is untouched. Off by default.
|
|
@@ -180,6 +180,10 @@ export class CustomWebView extends WebView {
|
|
|
180
180
|
config.allowsPictureInPictureMediaPlayback = true;
|
|
181
181
|
config.mediaTypesRequiringUserActionForPlayback = 0; // WKAudiovisualMediaTypeNone
|
|
182
182
|
config.websiteDataStore = WKWebsiteDataStore.defaultDataStore();
|
|
183
|
+
// Apple's hard requirement for a service worker in a WKWebView: restrict navigation to the
|
|
184
|
+
// declared app-bound domains (also stamped to Info.plist WKAppBoundDomains). Only set when
|
|
185
|
+
// configured — otherwise the WebView stays unrestricted (default behavior).
|
|
186
|
+
if (SHELL_CONFIG.appBoundDomains?.length) config.limitsNavigationsToAppBoundDomains = true;
|
|
183
187
|
|
|
184
188
|
// Env hints + framework globals (backend origin) + a getUserMedia guard for UNDECLARED capabilities
|
|
185
189
|
// (must reject in JS before WebKit's native capture path — see mediaCaptureGuardJs) + native-feel
|
package/src/cli.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
resolveBuildNumber,
|
|
29
29
|
stampAndroidOrientation,
|
|
30
30
|
stampAndroidQueries,
|
|
31
|
+
stampAppBoundDomains,
|
|
31
32
|
stampPlistBackgroundTasks,
|
|
32
33
|
stampPlistOrientations,
|
|
33
34
|
stampPrivacyTracking,
|
|
@@ -410,6 +411,7 @@ export const SHELL_CONFIG = {
|
|
|
410
411
|
debugLog: ${JSON.stringify(cfg.debugLog ?? '*')},
|
|
411
412
|
devMenu: ${JSON.stringify(cfg.devMenu ?? true)},
|
|
412
413
|
neutralizeServiceWorker: ${JSON.stringify(cfg.neutralizeServiceWorker ?? true)},
|
|
414
|
+
appBoundDomains: ${JSON.stringify(cfg.appBoundDomains ?? [])} as string[],
|
|
413
415
|
openNewWindowsInBrowser: ${JSON.stringify(cfg.openNewWindowsInBrowser ?? false)},
|
|
414
416
|
pushIos: ${JSON.stringify(!!cfg.push?.enabled && cfg.push?.ios !== false)},
|
|
415
417
|
pushAndroid: ${JSON.stringify(!!cfg.push?.enabled && cfg.push?.android !== false)},
|
|
@@ -448,6 +450,11 @@ function stampIOSDisplayName(outDir: string, cfg: AppwrapConfig, req: NativeReqs
|
|
|
448
450
|
const bgActive = req.activeOptionalGroups.includes('backgroundTask');
|
|
449
451
|
src = stampPlistBackgroundTasks(src, bgActive ? cfg.backgroundTasks : undefined);
|
|
450
452
|
|
|
453
|
+
// iOS App-Bound Domains — gate for a WKWebView service worker (paired with
|
|
454
|
+
// limitsNavigationsToAppBoundDomains in the shell config). Idempotent both ways: empty/undefined
|
|
455
|
+
// strips the WKAppBoundDomains key, so it no-ops when the field is absent.
|
|
456
|
+
src = stampAppBoundDomains(src, cfg.appBoundDomains);
|
|
457
|
+
|
|
451
458
|
// Permission usage strings + URL scheme + export-compliance — idempotent: strip stamped block, re-add
|
|
452
459
|
src = src.replace(/\s*<!-- appwrap:begin -->[\s\S]*?<!-- appwrap:end -->/g, '');
|
|
453
460
|
const extras: string[] = [];
|
package/src/config.ts
CHANGED
|
@@ -97,6 +97,15 @@ export interface AppwrapConfig {
|
|
|
97
97
|
* for in-WebView web-push as a fallback. NOTE: keeping the SW is the only way to get web push; native
|
|
98
98
|
* push is the separate `push` lane (APNs/FCM) and does NOT need a SW. */
|
|
99
99
|
neutralizeServiceWorker?: boolean;
|
|
100
|
+
/** iOS App-Bound Domains — the hosts (no scheme, max 10 per Apple) the WebView may navigate to.
|
|
101
|
+
* Setting this stamps `WKAppBoundDomains` into Info.plist AND sets
|
|
102
|
+
* `WKWebViewConfiguration.limitsNavigationsToAppBoundDomains = true`, which is Apple's HARD
|
|
103
|
+
* requirement for a service worker to run inside a WKWebView. Pair with `neutralizeServiceWorker:false`
|
|
104
|
+
* to actually enable a SW (this flag is the iOS gate; Android's SW is a separate lane).
|
|
105
|
+
* TRADEOFF: `limitsNavigationsToAppBoundDomains` RESTRICTS the WebView to exactly these domains —
|
|
106
|
+
* any navigation/OAuth/redirect to another domain breaks. Only for single-origin apps. Empty/unset =
|
|
107
|
+
* no change to current behavior (no Info.plist key, no nav restriction). */
|
|
108
|
+
appBoundDomains?: string[];
|
|
100
109
|
/** Hand navigations that LEAVE the app's own origin to the OS default browser (Safari / Chrome)
|
|
101
110
|
* instead of replacing the shell WebView — so external links and `window.open(...)`/`target="_blank"`
|
|
102
111
|
* behave like a regular native app (open in the system browser) rather than navigating away inside
|
package/src/derive.ts
CHANGED
|
@@ -228,6 +228,27 @@ export function stampPlistBackgroundTasks(src: string, ids: string[] | undefined
|
|
|
228
228
|
return src;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Stamp (or strip) `WKAppBoundDomains` in an Info.plist string — Apple's gate for running a service
|
|
233
|
+
* worker inside a WKWebView (paired with `limitsNavigationsToAppBoundDomains` on the config). Pure +
|
|
234
|
+
* idempotent: rewrites an `appwrap:appbound` marker block (added before `</dict>`), so a re-sync never
|
|
235
|
+
* duplicates. `domains` empty/undefined → strips the block entirely (no key = current behavior). Apple
|
|
236
|
+
* caps the array at 10; we stamp what's given. Mirrors `stampPlistOrientations` (pure string transform).
|
|
237
|
+
*/
|
|
238
|
+
export function stampAppBoundDomains(src: string, domains: string[] | undefined): string {
|
|
239
|
+
src = src.replace(/\s*<!-- appwrap:appbound -->[\s\S]*?<!-- \/appwrap:appbound -->/g, '');
|
|
240
|
+
const list = (domains ?? []).filter(Boolean);
|
|
241
|
+
if (list.length) {
|
|
242
|
+
const items = list.map((d) => ` <string>${d}</string>`).join('\n');
|
|
243
|
+
const block =
|
|
244
|
+
` <!-- appwrap:appbound -->\n` +
|
|
245
|
+
` <key>WKAppBoundDomains</key>\n <array>\n${items}\n </array>\n` +
|
|
246
|
+
` <!-- /appwrap:appbound -->`;
|
|
247
|
+
src = src.replace(/<\/dict>\s*<\/plist>\s*$/, `${block}\n</dict>\n</plist>\n`);
|
|
248
|
+
}
|
|
249
|
+
return src;
|
|
250
|
+
}
|
|
251
|
+
|
|
231
252
|
/**
|
|
232
253
|
* Stamp the App Tracking Transparency declarations into PrivacyInfo.xcprivacy. Rewrites the two
|
|
233
254
|
* tracking keys IN PLACE (the template ships them, so we never restructure the doc — we only flip
|
|
@@ -80,7 +80,25 @@ def appwrap_archive_and_sign_ios
|
|
|
80
80
|
api_key
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
# Restore the login keychain as the system default and drop fastlane's temp keychain. `setup_ci`/`match`
|
|
84
|
+
# create `fastlane_tmp_keychain` and point the default keychain at it; if a lane dies mid-run before
|
|
85
|
+
# fastlane's own teardown, that stray keychain is left as the DEFAULT — so macOS (com.apple.iCloudHelper
|
|
86
|
+
# etc.) keeps prompting for its password forever. CI runners are ephemeral so this is a no-op there;
|
|
87
|
+
# locally it prevents the recurring keychain prompt. Best-effort: never fail the lane on cleanup.
|
|
88
|
+
def appwrap_keychain_cleanup
|
|
89
|
+
login = File.expand_path('~/Library/Keychains/login.keychain-db')
|
|
90
|
+
return unless File.exist?(login)
|
|
91
|
+
sh("security default-keychain -s '#{login}'", error_callback: ->(_) {})
|
|
92
|
+
sh('security delete-keychain ~/Library/Keychains/fastlane_tmp_keychain-db 2>/dev/null || true',
|
|
93
|
+
error_callback: ->(_) {})
|
|
94
|
+
rescue StandardError => e
|
|
95
|
+
UI.message("keychain cleanup skipped: #{e}")
|
|
96
|
+
end
|
|
97
|
+
|
|
83
98
|
platform :ios do
|
|
99
|
+
after_all { appwrap_keychain_cleanup }
|
|
100
|
+
error { |_lane, _exception| appwrap_keychain_cleanup }
|
|
101
|
+
|
|
84
102
|
desc 'Build the NativeScript wrapper and upload to TestFlight'
|
|
85
103
|
lane :beta do
|
|
86
104
|
api_key = appwrap_archive_and_sign_ios
|