@livx.cc/appwrap 0.42.1 → 0.42.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
CHANGED
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
- FileTimestamp (C617.1) — NativeScript reads bundle/app file metadata at runtime
|
|
9
9
|
- SystemBootTime (35F9.1) — NativeScript timers/uptime
|
|
10
10
|
- DiskSpace (E174.1) — NativeScript file I/O
|
|
11
|
-
NSPrivacyTracking is false (no IDFA, no cross-app/third-party linkage)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
NSPrivacyTracking is false (no IDFA, no cross-app/third-party linkage) and NSPrivacyCollectedDataTypes
|
|
12
|
+
is EMPTY by default: the stock runtime does NOT collect or transmit any data. kit.context() merely
|
|
13
|
+
RETURNS install context (IDFV, platform, version) to the app on request — it never sends it anywhere —
|
|
14
|
+
so the default binary matches a "Data Not Collected" App Store privacy label.
|
|
15
|
+
|
|
16
|
+
If YOUR app wires analytics that actually transmits data (DeviceID/IDFV, ProductInteraction, account,
|
|
17
|
+
location, …) you MUST declare it here — replace this whole file via your app's overrides dir
|
|
18
|
+
(appwrap.overrides/App_Resources/iOS/PrivacyInfo.xcprivacy). `applyOverrides` copies overrides over
|
|
19
|
+
the generated native/ last, so your file wins. Add the matching NSPrivacyCollectedDataTypes entries
|
|
20
|
+
(and set NSPrivacyTracking=true if you track) — see tracking-analytics + store-readiness.
|
|
18
21
|
-->
|
|
19
22
|
<plist version="1.0">
|
|
20
23
|
<dict>
|
|
@@ -23,32 +26,7 @@
|
|
|
23
26
|
<key>NSPrivacyTrackingDomains</key>
|
|
24
27
|
<array/>
|
|
25
28
|
<key>NSPrivacyCollectedDataTypes</key>
|
|
26
|
-
<array
|
|
27
|
-
<dict>
|
|
28
|
-
<key>NSPrivacyCollectedDataType</key>
|
|
29
|
-
<string>NSPrivacyCollectedDataTypeDeviceID</string>
|
|
30
|
-
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
31
|
-
<false/>
|
|
32
|
-
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
33
|
-
<false/>
|
|
34
|
-
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
35
|
-
<array>
|
|
36
|
-
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
|
37
|
-
</array>
|
|
38
|
-
</dict>
|
|
39
|
-
<dict>
|
|
40
|
-
<key>NSPrivacyCollectedDataType</key>
|
|
41
|
-
<string>NSPrivacyCollectedDataTypeProductInteraction</string>
|
|
42
|
-
<key>NSPrivacyCollectedDataTypeLinked</key>
|
|
43
|
-
<false/>
|
|
44
|
-
<key>NSPrivacyCollectedDataTypeTracking</key>
|
|
45
|
-
<false/>
|
|
46
|
-
<key>NSPrivacyCollectedDataTypePurposes</key>
|
|
47
|
-
<array>
|
|
48
|
-
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
|
49
|
-
</array>
|
|
50
|
-
</dict>
|
|
51
|
-
</array>
|
|
29
|
+
<array/>
|
|
52
30
|
<key>NSPrivacyAccessedAPITypes</key>
|
|
53
31
|
<array>
|
|
54
32
|
<dict>
|
package/src/cli.ts
CHANGED
|
@@ -1275,10 +1275,18 @@ function stampVersionManifest(outDir: string, cfg: AppwrapConfig): void {
|
|
|
1275
1275
|
|
|
1276
1276
|
/** Pure-native escape hatch: copy the consumer's overrides dir OVER the generated wrapper, last,
|
|
1277
1277
|
* so it wins. For legacy/custom native code the declarative config can't express. */
|
|
1278
|
-
function applyOverrides(cwd: string, outDir: string, cfg: AppwrapConfig): void {
|
|
1278
|
+
export function applyOverrides(cwd: string, outDir: string, cfg: AppwrapConfig): void {
|
|
1279
1279
|
const dir = resolve(cwd, cfg.overrides ?? 'appwrap.overrides');
|
|
1280
|
-
|
|
1280
|
+
// Files the template still provides must survive an override removal (regenerateCore ran first and
|
|
1281
|
+
// re-copied them) → protect them from the override prune.
|
|
1282
|
+
const templateFiles = existsSync(TEMPLATE_DIR) ? collectRelFiles(TEMPLATE_DIR, templateCopyFilter) : new Set<string>();
|
|
1283
|
+
if (!existsSync(dir)) {
|
|
1284
|
+
// No overrides now: prune anything a PRIOR overrides run left behind (renamed/removed override files).
|
|
1285
|
+
pruneStale(outDir, OVERRIDES_MANIFEST, new Set(), templateFiles);
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1281
1288
|
cpSync(dir, outDir, { recursive: true, force: true });
|
|
1289
|
+
pruneStale(outDir, OVERRIDES_MANIFEST, collectRelFiles(dir), templateFiles);
|
|
1282
1290
|
console.log(` over ← ${dir} (native overrides applied)`);
|
|
1283
1291
|
}
|
|
1284
1292
|
|
|
@@ -1422,6 +1430,54 @@ function copyCiTemplates(cwd: string, outDir: string, cfg: AppwrapConfig, force
|
|
|
1422
1430
|
console.log(ci);
|
|
1423
1431
|
}
|
|
1424
1432
|
|
|
1433
|
+
// Manifests of what the two sync-owned sources (runtime template, consumer overrides) generated LAST run.
|
|
1434
|
+
// Used to prune files that were REMOVED/renamed in the source — cpSync(force) overwrites but never deletes,
|
|
1435
|
+
// so without this a relocated source lingers in native/ and gets re-bundled.
|
|
1436
|
+
const TEMPLATE_MANIFEST = '.appwrap-template-manifest.json';
|
|
1437
|
+
const OVERRIDES_MANIFEST = '.appwrap-overrides-manifest.json';
|
|
1438
|
+
|
|
1439
|
+
/** Files under TEMPLATE_DIR to skip when copying/walking it (deps, build output, PWA staging, per-module
|
|
1440
|
+
* native — those are handled selectively elsewhere). Shared by the cpSync filter and the prune walk so
|
|
1441
|
+
* the copied set and the pruned set are defined identically. */
|
|
1442
|
+
const templateCopyFilter = (src: string): boolean =>
|
|
1443
|
+
!/(?:^|\/)(node_modules|platforms|hooks|app\/www|modules-native)(\/|$)/.test(src.slice(TEMPLATE_DIR.length));
|
|
1444
|
+
|
|
1445
|
+
/** File (not dir) paths under `root`, relative to it, skipping entries `accept` rejects. */
|
|
1446
|
+
function collectRelFiles(root: string, accept: (abs: string) => boolean = () => true): Set<string> {
|
|
1447
|
+
const out = new Set<string>();
|
|
1448
|
+
const rec = (dir: string, rel: string): void => {
|
|
1449
|
+
let entries;
|
|
1450
|
+
try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
1451
|
+
for (const e of entries) {
|
|
1452
|
+
const abs = join(dir, e.name);
|
|
1453
|
+
if (!accept(abs)) continue;
|
|
1454
|
+
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
1455
|
+
if (e.isDirectory()) rec(abs, r);
|
|
1456
|
+
else out.add(r);
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
rec(root, '');
|
|
1460
|
+
return out;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/** Prune generated files sync OWNS that vanished from their source. Deletes only paths recorded in the
|
|
1464
|
+
* PRIOR manifest that are absent from the `current` source set — so it never touches user data or any
|
|
1465
|
+
* region absent from a previous run's manifest. `protect` keeps paths another owned source still ships
|
|
1466
|
+
* (e.g. an override was removed but the template still provides that file). */
|
|
1467
|
+
function pruneStale(outDir: string, manifest: string, current: Set<string>, protect?: Set<string>): void {
|
|
1468
|
+
const mf = join(outDir, manifest);
|
|
1469
|
+
let prev: string[] = [];
|
|
1470
|
+
try { prev = JSON.parse(readFileSync(mf, 'utf8')) as string[]; } catch { /* first run — nothing to prune */ }
|
|
1471
|
+
for (const rel of prev) {
|
|
1472
|
+
if (current.has(rel) || protect?.has(rel)) continue;
|
|
1473
|
+
const abs = join(outDir, rel);
|
|
1474
|
+
try {
|
|
1475
|
+
if (existsSync(abs)) { rmSync(abs, { force: true }); console.log(` prune ✕ ${rel} (removed from source)`); }
|
|
1476
|
+
} catch { /* non-fatal */ }
|
|
1477
|
+
}
|
|
1478
|
+
try { writeFileSync(mf, JSON.stringify([...current])); } catch { /* non-fatal */ }
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1425
1481
|
/**
|
|
1426
1482
|
* Reproduce native/ from source — the shared core of `init` and `sync`. Copies the runtime shell
|
|
1427
1483
|
* template, re-stamps EVERY config artifact, and re-copies the built PWA. `native/` is disposable, so a
|
|
@@ -1441,8 +1497,10 @@ function regenerateCore(cwd: string, outDir: string, cfg: AppwrapConfig, opts: {
|
|
|
1441
1497
|
// modules-native/ is copied selectively per active module (copyModuleNativeSrc), not wholesale.
|
|
1442
1498
|
// Match RELATIVE to TEMPLATE_DIR — when installed from npm, TEMPLATE_DIR itself sits under
|
|
1443
1499
|
// node_modules/, so testing the absolute path would wrongly exclude the entire template.
|
|
1444
|
-
filter:
|
|
1500
|
+
filter: templateCopyFilter,
|
|
1445
1501
|
});
|
|
1502
|
+
// Delete template files removed/renamed since the last regenerate (cpSync only overwrites, never deletes).
|
|
1503
|
+
pruneStale(outDir, TEMPLATE_MANIFEST, collectRelFiles(TEMPLATE_DIR, templateCopyFilter));
|
|
1446
1504
|
stampShellConfig(outDir, cfg);
|
|
1447
1505
|
stampNativeScriptConfig(outDir, cfg);
|
|
1448
1506
|
stampIOSDisplayName(outDir, cfg, req);
|
|
@@ -2176,7 +2234,7 @@ function pinTeamIdToConfig(configPath: string, teamId: string): void {
|
|
|
2176
2234
|
* App_Resources/ is intentionally excluded — sync() rewrites it every run, so its mtime always
|
|
2177
2235
|
* changes and would make the fingerprint permanently stale.
|
|
2178
2236
|
* Collision risk is acceptable — a false "match" just skips a redundant build, not a correctness bug. */
|
|
2179
|
-
function buildFingerprint(cwd: string, cfg: { pwaDist?: string },
|
|
2237
|
+
export function buildFingerprint(cwd: string, cfg: { pwaDist?: string; overrides?: string }, flags: Record<string, string> = {}): string {
|
|
2180
2238
|
const mtime = (p: string): number => {
|
|
2181
2239
|
if (!existsSync(p)) return 0;
|
|
2182
2240
|
try {
|
|
@@ -2190,8 +2248,12 @@ function buildFingerprint(cwd: string, cfg: { pwaDist?: string }, _outDir: strin
|
|
|
2190
2248
|
return s.mtimeMs;
|
|
2191
2249
|
} catch { return 0; }
|
|
2192
2250
|
};
|
|
2251
|
+
// Fingerprint EVERY app source the build actually consumes — not just dist. Missing the overrides dir
|
|
2252
|
+
// (native escape hatch) or a `.js`/`.json`/`--config` config file made edits there produce a stale
|
|
2253
|
+
// "inputs unchanged" skip. Resolve the real config path (ts→js→json / --config) instead of hardcoding.
|
|
2193
2254
|
const distDir = cfg.pwaDist ? resolve(cwd, cfg.pwaDist) : join(cwd, 'dist');
|
|
2194
|
-
const
|
|
2255
|
+
const overridesDir = resolve(cwd, cfg.overrides ?? 'appwrap.overrides');
|
|
2256
|
+
const parts = [mtime(distDir), mtime(overridesDir), mtime(resolveConfigPath(cwd, flags))];
|
|
2195
2257
|
// Simple djb2-style hash — good enough for a build-skip check (not cryptographic).
|
|
2196
2258
|
let h = 5381;
|
|
2197
2259
|
for (const n of parts) h = (((h << 5) + h) ^ (n | 0)) >>> 0;
|
|
@@ -2499,7 +2561,7 @@ async function deployAndroid(cwd: string, flags: Record<string, string>, cfgOver
|
|
|
2499
2561
|
// unchanged since the last build. --force/-f always rebuilds. (Rebuilding the web above usually
|
|
2500
2562
|
// bumps the fingerprint; pair with --no-web-build to actually hit this fast-path.)
|
|
2501
2563
|
const force = 'force' in flags || 'f' in flags;
|
|
2502
|
-
const fp = buildFingerprint(cwd, cfg,
|
|
2564
|
+
const fp = buildFingerprint(cwd, cfg, flags);
|
|
2503
2565
|
const cache = readBuildCache(outDir, 'android');
|
|
2504
2566
|
if (!force && existsSync(apk) && cache?.fingerprint === fp && cache?.artifactPath === apk) {
|
|
2505
2567
|
console.log(`⚡ Skipping build — inputs unchanged since last build (${apk.split('/').pop()})`);
|
|
@@ -2577,7 +2639,7 @@ async function deploy(cwd: string, flags: Record<string, string>, positionals: s
|
|
|
2577
2639
|
// Auto: always checks fingerprint; never skips if sources/deps changed.
|
|
2578
2640
|
const resume = 'resume' in flags || 'r' in flags;
|
|
2579
2641
|
const force = 'force' in flags || 'f' in flags;
|
|
2580
|
-
const fp = buildFingerprint(cwd, cfg,
|
|
2642
|
+
const fp = buildFingerprint(cwd, cfg, flags);
|
|
2581
2643
|
const cache = readBuildCache(outDir, 'ios');
|
|
2582
2644
|
const existingIpa = existsSync(ipaDir)
|
|
2583
2645
|
? readdirSync(ipaDir).find((f) => f.endsWith('.ipa'))
|