@livx.cc/appwrap 0.40.0 → 0.40.1
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_Resources/iOS/Info.plist +4 -6
- package/src/cli.ts +19 -12
- package/src/config.ts +5 -0
- package/src/derive.ts +23 -11
package/package.json
CHANGED
|
@@ -50,11 +50,9 @@
|
|
|
50
50
|
<key>NSAllowsLocalNetworking</key>
|
|
51
51
|
<true/>
|
|
52
52
|
</dict>
|
|
53
|
-
<!--
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<string>audio</string>
|
|
58
|
-
</array>
|
|
53
|
+
<!-- No UIBackgroundModes by default. Declaring a mode with no matching feature is an App Store
|
|
54
|
+
2.5.4 rejection, so modules stamp only what they need: push → `remote-notification`,
|
|
55
|
+
backgroundTask → `fetch`+`processing`, config `backgroundAudio:true` → `audio` (each creates
|
|
56
|
+
the key if absent). Keep the default clean; apps opt into capabilities in appwrap.config. -->
|
|
59
57
|
</dict>
|
|
60
58
|
</plist>
|
package/src/cli.ts
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
stampPlistBackgroundTasks,
|
|
34
34
|
stampPlistOrientations,
|
|
35
35
|
stampPrivacyTracking,
|
|
36
|
+
stripEmptyBackgroundModes,
|
|
36
37
|
} from './derive';
|
|
37
38
|
import type { WebManifest } from './derive';
|
|
38
39
|
|
|
@@ -510,19 +511,25 @@ function stampIOSDisplayName(outDir: string, cfg: AppwrapConfig, req: NativeReqs
|
|
|
510
511
|
);
|
|
511
512
|
}
|
|
512
513
|
|
|
513
|
-
//
|
|
514
|
-
//
|
|
515
|
-
// (
|
|
516
|
-
const iosPush = !!cfg.push?.enabled && cfg.push?.ios !== false;
|
|
514
|
+
// UIBackgroundModes is opt-in per module/config (the template ships none). Toggle each mode in the
|
|
515
|
+
// shared array — a second <key> would be a duplicate (invalid plist). Idempotent both ways: MERGE
|
|
516
|
+
// in-place (creating the key when absent) if wanted, strip when not.
|
|
517
517
|
const bgArray = /(<key>UIBackgroundModes<\/key>\s*<array>)([\s\S]*?)(<\/array>)/;
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
518
|
+
const toggleBgMode = (s: string, mode: string, want: boolean): string => {
|
|
519
|
+
const has = new RegExp(`<string>${mode}</string>`).test(s);
|
|
520
|
+
if (want && !has) {
|
|
521
|
+
return bgArray.test(s)
|
|
522
|
+
? s.replace(bgArray, (_m, open, inner, close) => `${open}${inner}\t<string>${mode}</string>\n\t${close}`)
|
|
523
|
+
: s.replace(/<\/dict>\s*<\/plist>\s*$/, ` <key>UIBackgroundModes</key>\n <array>\n <string>${mode}</string>\n </array>\n</dict>\n</plist>\n`);
|
|
524
|
+
}
|
|
525
|
+
if (!want && has) return s.replace(new RegExp(`\\s*<string>${mode}</string>`), '');
|
|
526
|
+
return s;
|
|
527
|
+
};
|
|
528
|
+
// Remote push needs `remote-notification`; apps that genuinely play audio in the background opt in
|
|
529
|
+
// via `backgroundAudio: true` (Apple 2.5.4 rejects `audio` without a real background-audio feature).
|
|
530
|
+
src = toggleBgMode(src, 'remote-notification', !!cfg.push?.enabled && cfg.push?.ios !== false);
|
|
531
|
+
src = toggleBgMode(src, 'audio', !!cfg.backgroundAudio);
|
|
532
|
+
src = stripEmptyBackgroundModes(src);
|
|
526
533
|
|
|
527
534
|
writeFileSync(plist, src);
|
|
528
535
|
}
|
package/src/config.ts
CHANGED
|
@@ -164,6 +164,11 @@ export interface AppwrapConfig {
|
|
|
164
164
|
* same ids are what `kit.backgroundTask.register(id, …)` / `.schedule({id})` use. No-op when absent
|
|
165
165
|
* or the module is inactive. */
|
|
166
166
|
backgroundTasks?: string[];
|
|
167
|
+
/** Opt in to the `audio` UIBackgroundMode — ONLY for apps that genuinely keep playing audio while
|
|
168
|
+
* backgrounded/screen-locked (music/streaming/podcast players). Off by default: declaring `audio`
|
|
169
|
+
* without a real background-audio feature is an App Store 2.5.4 rejection. Stamps `audio` into
|
|
170
|
+
* Info.plist UIBackgroundModes when true; no-op/stripped when absent. */
|
|
171
|
+
backgroundAudio?: boolean;
|
|
167
172
|
/** Remote push (APNs/FCM). Off unless set — gating matters: an `aps-environment` entitlement on a
|
|
168
173
|
* team that can't hold the Push capability (e.g. a personal team) BREAKS code signing, and the
|
|
169
174
|
* handshake should honestly report `push: 'none'` on an un-provisioned build. The kit returns a raw
|
package/src/derive.ts
CHANGED
|
@@ -195,20 +195,15 @@ export function stampAndroidOrientation(src: string, value: string): string {
|
|
|
195
195
|
* `remote-notification`). `ids` empty/undefined → strips the block + removes the two modes it added.
|
|
196
196
|
*/
|
|
197
197
|
export function stampPlistBackgroundTasks(src: string, ids: string[] | undefined): string {
|
|
198
|
-
// 1)
|
|
198
|
+
// 1) Strip the marker block first (idempotent). It is re-added LAST (step 3) so its position is
|
|
199
|
+
// stable regardless of whether the UIBackgroundModes array below was pre-existing or freshly
|
|
200
|
+
// created — otherwise the marker and a created array flip order between runs (non-idempotent).
|
|
199
201
|
src = src.replace(/\s*<!-- appwrap:bgtask -->[\s\S]*?<!-- \/appwrap:bgtask -->/g, '');
|
|
200
202
|
const list = (ids ?? []).filter(Boolean);
|
|
201
|
-
if (list.length) {
|
|
202
|
-
const items = list.map((s) => ` <string>${s}</string>`).join('\n');
|
|
203
|
-
const block =
|
|
204
|
-
` <!-- appwrap:bgtask -->\n` +
|
|
205
|
-
` <key>BGTaskSchedulerPermittedIdentifiers</key>\n <array>\n${items}\n </array>\n` +
|
|
206
|
-
` <!-- /appwrap:bgtask -->`;
|
|
207
|
-
src = src.replace(/<\/dict>\s*<\/plist>\s*$/, `${block}\n</dict>\n</plist>\n`);
|
|
208
|
-
}
|
|
209
203
|
|
|
210
|
-
// 2) Merge/remove the fetch + processing background modes
|
|
211
|
-
//
|
|
204
|
+
// 2) Merge/remove the fetch + processing background modes — they live in the shared
|
|
205
|
+
// UIBackgroundModes array (which may also hold audio/remote-notification). Create the key when
|
|
206
|
+
// absent; strip the two modes (and, via stripEmptyBackgroundModes, the emptied key) when off.
|
|
212
207
|
const modes = ['fetch', 'processing'];
|
|
213
208
|
const bgArray = /(<key>UIBackgroundModes<\/key>\s*<array>)([\s\S]*?)(<\/array>)/;
|
|
214
209
|
if (list.length) {
|
|
@@ -225,9 +220,26 @@ export function stampPlistBackgroundTasks(src: string, ids: string[] | undefined
|
|
|
225
220
|
} else {
|
|
226
221
|
for (const m of modes) src = src.replace(new RegExp(`\\s*<string>${m}</string>`), '');
|
|
227
222
|
}
|
|
223
|
+
src = stripEmptyBackgroundModes(src);
|
|
224
|
+
|
|
225
|
+
// 3) Re-add the permitted-identifiers marker block LAST, right before </dict> (stable position).
|
|
226
|
+
if (list.length) {
|
|
227
|
+
const items = list.map((s) => ` <string>${s}</string>`).join('\n');
|
|
228
|
+
const block =
|
|
229
|
+
` <!-- appwrap:bgtask -->\n` +
|
|
230
|
+
` <key>BGTaskSchedulerPermittedIdentifiers</key>\n <array>\n${items}\n </array>\n` +
|
|
231
|
+
` <!-- /appwrap:bgtask -->`;
|
|
232
|
+
src = src.replace(/<\/dict>\s*<\/plist>\s*$/, `${block}\n</dict>\n</plist>\n`);
|
|
233
|
+
}
|
|
228
234
|
return src;
|
|
229
235
|
}
|
|
230
236
|
|
|
237
|
+
/** Remove a now-empty `UIBackgroundModes` key+array so a plist that opts into no modes carries no
|
|
238
|
+
* dangling empty key (keeps the default tidy). Pure + idempotent; no-op when the array has members. */
|
|
239
|
+
export function stripEmptyBackgroundModes(src: string): string {
|
|
240
|
+
return src.replace(/\s*<key>UIBackgroundModes<\/key>\s*<array>\s*<\/array>/g, '');
|
|
241
|
+
}
|
|
242
|
+
|
|
231
243
|
/**
|
|
232
244
|
* Stamp (or strip) `WKAppBoundDomains` in an Info.plist string — Apple's gate for running a service
|
|
233
245
|
* worker inside a WKWebView (paired with `limitsNavigationsToAppBoundDomains` on the config). Pure +
|